/* =============================================================================
   collection-panel.css
   Reusable right-side Collection panel
   Included on every page via master.blade.php
   ============================================================================= */

/* Panel must appear above filter header (z-index: 20) on PLP */
.page-venues .ai-panel {
  z-index: 9300;
}

/* The sticky nav header (.app-navigation, z-index: 9100; .ai-header on the
   AI-Wise variant) creates its own stacking context, so its absolutely-positioned
   burger dropdown (z-index: 9200) is trapped below the open panel above — clicking
   the menu opened it invisibly behind the panel. Wherever the panel docks (≥768px),
   lift the header above the panel (9300) so the dropdown paints on top. Scoped to
   the panel-open state so normal overlay stacking is untouched; the header bar only
   occupies the top strip and never overlaps the panel body. Mirrors the existing
   `:has(#ai-panel.ai-panel--open)` idiom used for the filter toolbar. */
@media (min-width: 768px) {
  body:has(#ai-panel.ai-panel--open) .app-navigation,
  body:has(#ai-panel.ai-panel--open) .ai-header {
    z-index: 9400;
  }
}

/* Phones: the panel is a full-screen bottom sheet that must cover the filter
   toolbar (forced to z-index: 10001 in filter-modal-spacing-fix.css). At ≥768px
   the panel docks side-by-side instead, so it keeps its base z-index (9300) and
   stays below the lifted header. */
@media (max-width: 767px) {
  .page-venues .ai-panel--open {
    z-index: 10100 !important;
  }
}

/* -----------------------------------------------------------------------------
   DESIGN TOKENS — width tokens on :root so siblings/parents can read them
   ----------------------------------------------------------------------------- */
:root {
  --cp-width:        440px;   /* panel open width (desktop) */
  --cp-width-tablet: 360px;   /* panel open width (tablet) */
}

.ai-panel {
  --cp-bg:            #1a1a1a;   /* panel surface */
  --cp-bg-raised:     #262626;   /* cards, tabs container */
  --cp-bg-dark:       #111111;   /* darkest bg (brand btn text) */
  --cp-border:        #262626;   /* panel edge border */
  --cp-border-inner:  #383838;   /* inner borders, separators */
  --cp-border-subtle: #1e1e1e;   /* card bottom divider */
  --cp-text-primary:  #e8e8e8;   /* active/primary text */
  --cp-text-muted:    #757575;   /* secondary text, tab labels */
  --cp-text-dim:      #4f4f4f;   /* disabled, separators */
  --cp-text-white:    #fafafa;   /* card title */
  --cp-text-empty:    #a8a8a8;   /* empty state title */
  --cp-text-empty-sub:#595959;   /* empty state subtitle */
  --cp-brand:         #c8d200;   /* brand accent */
  --cp-brand-hover:   #d4df00;   /* brand hover */
  --cp-brand-light:   #eef291;   /* badge label */
  --cp-font:          'Neue', sans-serif;
  --cp-radius:        8px;       /* standard border radius */
  --cp-radius-inner:  6px;       /* inner tab / card border radius */
  --cp-transition:    0.15s;     /* micro-interaction duration */
}

/* -----------------------------------------------------------------------------
   PAGE LAYOUT WRAPPER (flex row: main content + panel)
   ----------------------------------------------------------------------------- */
#page-layout {
  display: flex;
  align-items: stretch;
}

#page-layout > #page_container {
  flex: 1;
  min-width: 0;
}

/* -----------------------------------------------------------------------------
   PANEL SHELL — in-flow flex child, pushes content when open
   ----------------------------------------------------------------------------- */
.ai-panel {
  width: 0;
  flex-shrink: 0;
  overflow: hidden;
  background: var(--cp-bg);
  box-shadow: 0 1px 2px 0 rgba(10, 13, 18, 0.05);
  border-left: 0;
  display: flex;
  flex-direction: column;
  transition: width 0.3s ease;
  will-change: width;
}

.ai-panel--open {
  width: var(--cp-width);
  border-left: 1px solid var(--cp-border);
}

/* center, not flex-start: the close control is a 30px square holding a cross,
   with no label to line its baseline up against, so it belongs level with the
   middle of the heading rather than hanging from the top of the row. The heading
   is one line, its title and its counter, so this is the middle of the title.
   flex-start was correct while a subtitle sat under the title and the buttons
   would have floated halfway down both lines. */
.ai-panel__header {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
  padding: 12px 24px 16px 24px;  /* 16px bottom = permanent scroll gap */
  flex-shrink: 0;
}

/* Heading block. This was a tab container holding one static tab, left over from
   when a Map tab sat beside it: it still looked pressable and still said nothing
   about what the block does. */
.ai-panel__heading {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ai-panel__title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-family: var(--cp-font);
  font-size: 16px;
  font-weight: 600;
  line-height: 22px;
  color: var(--cp-text-white);
}

/* Shown at nought as well, so the cap is part of the heading rather than
   something that appears once it starts to matter. It used to be display:none
   until the first venue landed, which meant the one moment a planner could learn
   there is a limit of five was the moment they had already begun filling it. */
.ai-panel__count {
  background: var(--cp-border-inner);
  border: 0.5px solid var(--cp-text-dim);
  border-radius: 16px;
  padding: 2px 10px;
  font-family: var(--cp-font);
  font-weight: 600;
  font-size: 14px;
  line-height: 20px;
  color: var(--cp-brand-light);
  text-align: center;
  white-space: nowrap;
  flex-shrink: 0;
  display: inline-flex;
}

/* The panel's one way out, at every width. It replaced two bare crosses drawn at
   two sizes, 28px docked and 14px in the phone sheet, so the same job was drawn
   twice.

   A cross on a surface, not a bare glyph and not a labelled button. The surface
   is what does the work the word used to: every card in the list carries its own
   remove control, and a naked X in the corner reads as one more of them, while
   the same cross on the panel's own raised fill reads as the panel's. The name
   lives in title and aria-label, which is where a control with no visible label
   has to keep it.

   30px square, which clears the 24px minimum for a control that is not the
   primary action and stays a real press at every width. It went in at 40px with
   a label and 20px of header padding above it, and at that size it was the
   heaviest thing in a row whose job is to name the panel. */
.ai-panel__close {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  background: var(--cp-bg-raised);
  border: none;
  border-radius: var(--cp-radius);
  color: var(--cp-text-primary);
  line-height: 1;
  cursor: pointer;
  transition:
    background var(--cp-transition),
    color var(--cp-transition);
}

.ai-panel__close:hover {
  background: var(--cp-border-inner);
  color: var(--cp-text-white);
}

.ai-panel__close:focus-visible {
  outline: 2px solid var(--cp-brand);
  outline-offset: 2px;
}

.ai-panel__close svg {
  flex-shrink: 0;
}

/* Daily inquiry limit, shown in the panel before the planner presses Send.
   The cap is quality control, so this is an informational card and not the
   error tint it used to wear: in red it read as "the send is broken", which
   sent planners to support over something that is working as intended. Brand
   accent on the leading edge, panel-dark surface, same radius as the rest. */
.cp-limit-notice {
  flex-shrink: 0;
  margin: 0 12px 8px;
  padding: 12px 14px;
  border: 1px solid var(--cp-border-inner, #3a3a3a);
  border-inline-start: 3px solid var(--cp-brand, #c8d200);
  border-radius: 8px;
  background: rgba(200, 210, 0, 0.06);
  color: var(--cp-text-white, #ffffff);
  font-family: var(--cp-font);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.45;
}

.cp-limit-notice[hidden] {
  display: none;
}

.cp-limit-notice__title {
  margin: 0 0 4px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
}

/* Lighter than --cp-text-muted (#757575): that token is for labels a planner
   skims, and this is the one paragraph on the panel they have to actually read. */
.cp-limit-notice__body {
  margin: 0;
  color: #b5b5b5;
}

.cp-limit-notice a {
  color: var(--cp-brand, #c8d200);
  font-weight: 600;
  text-decoration: underline;
}

@media (min-width: 768px) {
  .cp-limit-notice {
    margin: 0 24px 8px;
  }
}

/* Collection messages (limit reached, send failed, added).
   Two things this used to get wrong. It was `left: 50%` with no `right`, so a
   fixed box shrink-to-fits into the remaining half of the viewport: on a 390px
   phone the limit message came out 195px wide and six lines tall, a raw red slab.
   And it sat at `bottom: 24px`, which on a phone is exactly where the panel's
   "Send inquiry" button is, so it covered the button that had just been pressed.
   Insets on both sides fix the width; --collection-toast-bottom is set by
   collection-panel.js to clear whatever action row is on screen. */
.collection-toast {
  position: fixed;
  bottom: var(--collection-toast-bottom, 24px);
  left: 16px;
  right: 16px;
  margin-inline: auto;
  width: fit-content;
  max-width: min(420px, calc(100vw - 32px));
  transform: translateY(12px);
  background: #fdf0ef;
  border: 1px solid #f3c9c4;
  color: #262626;
  font-family: var(--cp-font, 'Neue Text', sans-serif);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.45;
  padding: 14px 16px;
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(10, 13, 18, 0.18);
  text-align: left;
  z-index: 99999;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s ease, transform 0.22s ease, bottom 0.22s ease;
}

.collection-toast--visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.collection-toast--error {
  background: #fdf0ef;
  border-color: #f3c9c4;
  color: #b3261e;
}

.collection-toast--success {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: #f2f8ea;
  border-color: #cfe3b4;
  color: #2f5711;
}

.collection-toast__action {
  flex-shrink: 0;
  background: none;
  border: none;
  padding: 0;
  color: inherit;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  text-decoration: underline;
}

/* A message that has to name the problem AND the next step. The error variant is
   not a flex row, so without this the title, the body and the action run together
   on one line. */
.collection-toast--stacked {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

.collection-toast__title {
  font-weight: 700;
}

.collection-toast--stacked .collection-toast__action {
  margin-top: 6px;
}

/* Wrapper around everything below the header, so a guest's hidden collection can
   be revealed in one move after an in-page sign-in. It has to pass the panel's
   flex column through unchanged: .ai-panel__content still needs to be the part
   that grows and scrolls, and .ai-panel__actions the part pinned at the bottom. */
/* Wraps the heading and the collection body so the two are one card. Not scoped to
   the inline panel: the drawer on /venues and AI-Wise has the same wrapper, and
   without this it would be a plain block, so the body's flex: 1 would resolve
   against a non-flex parent and the list would stop scrolling. min-height: 0 lets
   it shrink below its content, which is what makes the inner scroll possible. */
.ai-panel__card--collection {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.ai-panel__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.ai-panel__body[hidden] {
  display: none;
}

.ai-panel__content {
  flex: 1;
  overflow-y: auto;
  padding: 20px 24px 16px;  /* 20px top + 16px from header = 36px initial gap */

  /* At five of five the list is taller than the column on a short viewport, and
     the cut fell mid-card, so the fifth property read as hidden rather than
     scrolled past. The fade says "there is more below", and the scroll padding
     lets the last card come fully clear of the edge instead of stopping flush
     against it. */
  scroll-padding-block-end: 16px;
  overscroll-behavior: contain;
}

/* Only masked while it actually overflows: an unnecessary fade on a short list
   dims a card for no reason. collection-panel.js sets the flag. */
.ai-panel__content--overflowing {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent 100%);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent 100%);
}

/* The mask above fades whatever sits at the bottom edge, including a card the
   planner has scrolled to. Drop it once they are at the end of the list. */
.ai-panel__content--overflowing.ai-panel__content--at-end {
  -webkit-mask-image: none;
  mask-image: none;
}


.ai-panel__empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  min-height: 210px;
  gap: 12px;
  text-align: center;
}

.ai-panel__empty-icon {
  width: 64px;
  height: 64px;
  opacity: 0.4;
}

.ai-panel__empty-title {
  font-family: var(--cp-font);
  font-weight: 400;
  font-size: 16px;
  line-height: 24px;
  color: var(--cp-text-empty);
  text-align: center;
  margin: 0;
}

.ai-panel__empty-subtitle {
  font-family: var(--cp-font);
  font-weight: 400;
  font-size: 14px;
  line-height: 20px;
  color: var(--cp-text-empty-sub);
  text-align: center;
  margin: 0;
  max-width: 260px;
}

/* -----------------------------------------------------------------------------
   ACTION BUTTONS
   ----------------------------------------------------------------------------- */
.ai-panel__actions {
  padding: 16px 24px 24px 24px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  flex-shrink: 0;
}

/* Send inquiry is the one thing this block exists to do, so it is filled, not
   outlined. As an outline it carried the same weight as the quiet controls around
   it and read as one option among several. Sized to match: 52px tall against the
   40px it was, bold rather than semibold. */
.ai-panel__action-btn {
  border-radius: var(--cp-radius);
  padding: 14px 18px;
  min-height: 52px;
  border: 1px solid var(--cp-border-inner);
  background: transparent;
  color: var(--cp-text-empty);
  font-family: var(--cp-font);
  font-weight: 700;
  font-size: 17px;
  line-height: 24px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  white-space: nowrap;
  transition: background var(--cp-transition), border-color var(--cp-transition), color var(--cp-transition);
}

/* Keeps its size when the label grows to "Send inquiry to 5 properties". */
.ai-panel__action-icon {
  flex-shrink: 0;
}

/* Unavailable, not invisible. --cp-text-dim (#4f4f4f) on the panel surface is
   about 1.9:1, which read as an empty box rather than a button waiting on a
   selection.

   aria-disabled is listed beside :disabled because an empty collection greys the
   button without switching it off: the click handler stays live so a press is
   answered with the reason, where a real `disabled` attribute would swallow it
   and say nothing. One rule for both, so the two states cannot drift apart. */
.ai-panel__action-btn:disabled,
.ai-panel__action-btn[aria-disabled="true"] {
  opacity: 1;
  background: var(--cp-bg-raised);
  border-color: var(--cp-border-inner);
  color: var(--cp-text-empty);
  cursor: not-allowed;
}

.ai-panel__action-btn:not(:disabled):not([aria-disabled="true"]) {
  background: var(--cp-brand);
  border-color: var(--cp-brand);
  color: var(--cp-bg-dark);
}

.ai-panel__action-btn:focus-visible {
  outline: 3px solid var(--cp-brand);
  outline-offset: 2px;
}

.ai-panel__action-btn:not(:disabled):not([aria-disabled="true"]):hover {
  background: var(--cp-brand-hover);
  border-color: var(--cp-brand-hover);
  color: var(--cp-bg-dark);
}

/* Nothing chosen yet. This used to be a brand outline that filled on hover, which
   invited a press the panel had nothing to honour, and it had to rename itself
   ("Add to an inquiry") to justify looking pressable. The button keeps one name
   at every count now, so the empty state is carried by the surface instead: the
   unavailable fill above, and the hint below saying what would make it live. */
.ai-panel__action-hint {
  grid-column: 1 / -1;
  margin: 0;
  text-align: center;
  font-family: var(--cp-font);
  font-weight: 400;
  font-size: 13px;
  line-height: 20px;
  color: var(--cp-text-empty-sub);
}

/* The panel sets `display` on several of its blocks, so `hidden` is spelled out
   here rather than left to the user-agent rule it would otherwise lose to. */
.ai-panel__action-hint[hidden] {
  display: none;
}

/* Route back to the planner's saved collections. A quiet text link, not a fourth
   button: the panel's job is the basket in front of you, this is a way out of it. */
.ai-panel__saved-link {
  grid-column: 1 / -1;
  padding-top: 4px;
  /* An earlier rule makes anchors in the panel flex containers, so text-align alone
     does not centre the label. */
  display: flex;
  justify-content: center;
  text-align: center;
  font-family: var(--cp-font);
  font-weight: 600;
  font-size: 13px;
  line-height: 20px;
  color: var(--cp-text-dim);
  text-decoration: underline;
}

.ai-panel__saved-link:hover {
  color: var(--cp-text-primary);
  text-decoration: underline;
}

/* Has-venues state: rearranges 4 buttons → 3-button layout */
.ai-panel__actions--has-venues {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas:
    "primary primary"
    "compare compare"
    "saved   saved";
  gap: 8px;
  padding: 12px 24px 16px;
}

.ai-panel__actions--has-venues .ai-panel__saved-link {
  grid-area: saved;
}

.ai-panel__actions--has-venues #btn-multi-inquire {
  grid-area: primary;
  background: var(--cp-brand);
  border-color: var(--cp-brand);
  color: var(--cp-bg);
  cursor: pointer;
  font-size: 15px;
}

.ai-panel__actions--has-venues #btn-multi-inquire:hover {
  background: var(--cp-brand-hover);
  border-color: var(--cp-brand-hover);
}

/* A full collection with the daily inquiry limit spent: the button is genuinely
   disabled, so it must not keep the brand fill that says "press me". The reason
   is in .cp-limit-notice directly above it. */
.ai-panel__actions--has-venues #btn-multi-inquire:disabled,
.ai-panel__actions--has-venues #btn-multi-inquire:disabled:hover {
  background: var(--cp-bg-raised);
  border-color: var(--cp-border-inner);
  color: var(--cp-text-muted);
  cursor: not-allowed;
}

.ai-panel__actions--has-venues #btn-compare {
  grid-area: compare;
  font-size: 13px;
  padding: 10px 8px;
  border-color: #4e5200;
}

/* -----------------------------------------------------------------------------
   VENUE CARDS
   ----------------------------------------------------------------------------- */
.collection-card {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 14px;
  background: var(--cp-bg-raised);
  border-radius: var(--cp-radius);
  border-bottom: 1px solid var(--cp-border-subtle);
  margin-bottom: 10px;
  /* Positions the stretched link below. */
  position: relative;
}

/* The whole card is the link to the property, so the target is the box and not a
   word inside it. An anchor stretched over the card rather than a click handler:
   it keeps the status bar, middle-click and one keyboard stop that says where it
   goes. Remove is raised above it, so the two never fight for the same press. */
.collection-card__link {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}

.collection-card:hover {
  background: #2e2e2e;
}

/* The focus ring belongs on the card, not on the invisible box that holds the
   href: an outline on a stretched overlay draws over the content it covers. */
.collection-card:focus-within {
  outline: 2px solid var(--cp-brand);
  outline-offset: 2px;
}

.collection-card__link:focus {
  outline: none;
}

.collection-card__thumb {
  flex: 0 0 auto;
  width: 116px;
  height: 72px;
  min-width: 116px;
  border-radius: 6px;
  background: var(--cp-bg-raised) center/cover no-repeat;
  overflow: hidden;
}

@media (max-width: 767px) {
  .collection-card__thumb {
    width: 82px;
    min-width: 82px;
    height: 64px;
  }
}

.collection-card__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.collection-card__top {
  display: flex;
  align-items: center;
  gap: 6px;
}

.collection-card__name-group {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
}

.collection-card__name {
  font-family: var(--cp-font);
  font-size: 12px;
  font-weight: 600;
  color: var(--cp-text-white);
  flex: 0 0 auto;          /* takes natural text width only, never grows */
  max-width: calc(100% - 24px); /* leaves the badge its 16px and the 6px gap */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.3;
}

.collection-card__badge {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
}

/* Remove sits at the card's right edge, on its own, outside the text column: a
   bin, not the labelled button it replaced, and not the unlabelled X before that.
   The X sat in the top corner beside the title and read as "dismiss this card";
   a bin at the edge of the row says the property leaves the inquiry, in the
   width a 360px panel can spare for it.

   The icon is 16px, the size the inquiry review view draws it at; the 8px of
   padding around it is target, not decoration, so the press stays 32px square
   and the card keeps the width it had at 24px. Muted at rest and red on hover:
   colour announces the consequence at the moment of the press rather than
   shouting through the whole list.

   z-index above .collection-card__link: the link is stretched over the whole
   card, including this, and without it the press would navigate instead. */
.collection-card__remove {
  position: relative;
  z-index: 2;
  align-self: center;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  color: var(--cp-text-empty);
  background: none;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition:
    background var(--cp-transition),
    color var(--cp-transition);
}

.collection-card__remove:hover {
  color: #ff6b6b;
  background: rgba(255, 107, 107, 0.12);
}

.collection-card__remove:focus-visible {
  outline: 2px solid #ff6b6b;
  outline-offset: 2px;
}

/* The address, on a line of its own. Two lines rather than one hard-truncated
   one: at 390px it fits on one either way, but the 360px tablet column cannot
   hold the longest of them, and an address cut mid-neighbourhood
   ("UAE - Dubai - DIP - Dub…") is the thing being fixed here. */
.collection-card__meta {
  font-size: 11px;
  margin: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.collection-card__meta-type {
  background: var(--cp-border-inner);
  color: #a8a8a8;
  border-radius: 4px;
  padding: 1px 6px;
  font-size: 10px;
  line-height: 16px;
  /* Shrinks and truncates before the venue count and Show do: on a narrow panel
     the category is the least useful of the three, and the address it used to
     share a line with is the most. */
  flex: 0 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.collection-card__meta-loc {
  color: var(--cp-text-muted);
}

.collection-card__footer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
}

/* The 360px panel: once the venue count and Show have taken their width there is
   about 15px left, and a category truncated to "R" says less than nothing. This
   is the only band where the panel is that narrow; the phone gets the full
   viewport width and the docked desktop column is 440px. */
@media (min-width: 768px) and (max-width: 1023px) {
  .collection-card__meta-type {
    display: none;
  }
}

.collection-card__count {
  font-size: 14px;
  font-weight: 500;
  color: #a8a8a8;
  flex: 0 0 auto;
}

.collection-card__sep {
  color: var(--cp-border-inner);
  font-size: 11px;
}

/* -----------------------------------------------------------------------------
   EMPTY SLOTS
   ----------------------------------------------------------------------------- */
/* Inert, and drawn as inert. The slot is a picture of the room left in the
   inquiry: it carries the default cursor and no hover, focus or active state,
   because :hover still matches a div and any of those would give feedback for a
   press that does nothing. The label is a position ("Venue 3 of 5") rather than
   an instruction for the same reason.

   Held back from the cards it sits under, but only held back: at 45% opacity over
   a #4f4f4f dash the row all but vanished on a dark panel, and a slot nobody can
   see does not show the room left in the inquiry. 70% over the panel's own muted
   grey keeps it quieter than a card without hiding it. */
.ai-slot--empty {
  height: 44px;
  border: none;
  border-radius: 6px;
  margin-top: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  cursor: default;
  opacity: 0.7;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25'%3e%3crect width='100%25' height='100%25' fill='none' rx='6' stroke='%23757575' stroke-width='1' stroke-dasharray='7 7' stroke-linecap='round'/%3e%3c/svg%3e");
}

.ai-slot--empty__label {
  font-family: var(--cp-font);
  font-size: 14px;
  font-weight: 400;
  color: var(--cp-text-empty);
}

/* First slot clears the last card by more than the inter-slot gap. Slots only
   render when at least one card is present, so this is always that boundary. */
#ai-empty-slots .ai-slot--empty:first-child {
  margin-top: 17px;
}

/* -----------------------------------------------------------------------------
   RESPONSIVE
   ----------------------------------------------------------------------------- */

/* Desktop: sticky panel — stays in viewport regardless of page length.
   Problem: mobile-overflow-fix.css sets overflow-x:hidden on html+body globally,
   which creates a new scroll container and silently breaks position:sticky.
   Fix: override to overflow-x:clip on desktop. Unlike 'hidden', 'clip' prevents
   overflow display WITHOUT creating a scroll container, so sticky works correctly. */
@media (min-width: 768px) {
  html, body {
    overflow-x: clip;
  }

  .ai-panel {
    position: sticky;
    top: var(--navigation-height, 80px);
    height: calc(100dvh - var(--navigation-height, 80px));
    align-self: flex-start;
    transform: translateZ(0);
    will-change: width;
  }

  /* Keep the sticky panel pinned while the quick-inquiry ("Matched to your
     search") modal is open. That modal locks scroll with `.quick-inquiry-modal-open
     { overflow: hidden }` on <body>, but overflow:hidden turns <body> into a scroll
     container and silently breaks the panel's position:sticky (the same trap the
     overflow-x:clip fix above works around). Restore clip from 768px up — where the
     panel is docked/sticky — so the panel and its action buttons stay pinned like in
     normal mode. Below 768px the panel is position:fixed, so the global
     overflow:hidden scroll-lock there is left untouched. */
  body.quick-inquiry-modal-open {
    overflow: clip;
  }

  /* Suppress intrinsic min-width on grid items so tracks can shrink freely */
  .listing__card-item {
    min-width: 0;
  }

  /* PLP PERFORMANCE — content-visibility skips layout/paint for off-screen cards,
     slashing the cost of every reflow triggered by panel animation.
     Off-screen cards (typically 8-9 of 12) are treated as opaque 380px boxes
     so grid track sizing is near-free for those slots. */
  .page-venues .listing__card-item {
    content-visibility: auto;
    contain-intrinsic-size: auto 380px;
  }
}

/* Desktop: 3 columns while docked — applied by JS via .cp-panel-open AFTER the
   open animation completes (transitionend), not during it, so the grid reflow is
   decoupled from the width animation. Tablet uses 2 columns (see below). */
@media (min-width: 1024px) {
  #page-layout.cp-panel-open .listing__content-cards,
  #page-layout.cp-panel-open .listing__content-static-cards {
    grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
  }
}

/* Tablet (768–1023px)
   - Listing (.page-venues): docks and pushes content like desktop, using the
     narrower tablet width and a 2-column grid so cards still fit.
   - Every other page (home/detail/AI-Wise): keeps the fixed right-side overlay
     drawer exactly as before.
   The compact panel chrome (tabs/header/close icon) applies to both. */
@media (min-width: 768px) and (max-width: 1023px) {
  /* Docked listing panel: narrower open width */
  .page-venues .ai-panel--open {
    width: var(--cp-width-tablet);
    border-left: 1px solid var(--cp-border);
  }

  /* Docked grid: 2 columns while the panel is open */
  #page-layout.cp-panel-open .listing__content-cards,
  #page-layout.cp-panel-open .listing__content-static-cards {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }

  /* Non-listing pages keep the fixed right-side overlay drawer */
  body:not(.page-venues) .ai-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: var(--cp-width-tablet) !important;
    height: 100dvh;
    border-left: 1px solid var(--cp-border);
    z-index: 9200;
    transform: translateX(100%);
    transition: transform 0.3s ease !important;
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }

  body:not(.page-venues) .ai-panel--open {
    width: var(--cp-width-tablet) !important;
    transform: translateX(0) !important;
  }

  /* Heading takes the row so it shares space with the collapse button rather
     than overflowing the 360px panel. */
  .ai-panel__heading {
    flex: 1;
    width: auto;
  }

  /* Tighten header padding to fit everything in 360px */
  .ai-panel__header {
    padding: 16px 16px 0;
    gap: 12px;
  }

}

/* -----------------------------------------------------------------------------
   OVERLAY MODE — pages without the push-drawer layout (home, detail, blog, …).
   collection-panel.js moves #ai-panel to <body> and adds .cp-overlay-mode, so
   the panel shows as a fixed right-side drawer (with backdrop) instead of a
   flex child that pushes content. Tablet/mobile widths already position the
   panel fixed above; this handles desktop (≥1024px).
   ----------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  .cp-overlay-mode .ai-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    height: 100dvh;
    width: var(--cp-width) !important;
    max-width: 100vw;
    align-self: auto;
    border-left: 1px solid var(--cp-border);
    z-index: 10100;                 /* above #ai-panel-backdrop (10050) */
    transform: translateX(100%);
    transition: transform 0.3s ease !important;
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }

  .cp-overlay-mode .ai-panel--open {
    width: var(--cp-width) !important;
    transform: translateX(0) !important;
  }
}

/* Mobile: full-screen overlay sliding up from bottom */
@media (max-width: 767px) {
  /* Panel shell — full-screen, off-screen by default, slides up when open */
  .ai-panel {
    position: fixed;
    inset: 0;
    width: 100% !important;
    height: 100dvh !important;
    border-radius: 0;
    border-left: none !important;
    border-top: none;
    z-index: 9200;
    transform: translateY(100%);
    transition: transform 0.3s ease;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background: #1a1a1a;
    box-shadow: 0 1px 2px 0 rgba(10, 13, 18, 0.05);
  }

  .ai-panel--open {
    width: 100% !important;
    border-top: none;
    transform: translateY(0);
  }

  /* Header: the pill is the close button here too, so nothing is hidden and
     nothing extra is drawn. */
  .ai-panel__header {
    padding: 10px 12px 0;
    gap: 8px;
  }

  .ai-panel__heading {
    flex: 1;
    min-width: 0;
  }


  /* Scrollable content area. 12px of top padding, not 20px: on a 664px viewport
     every reclaimed row is a fraction of a property card. */
  .ai-panel__content {
    flex: 1;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding-left: 12px;
    padding-right: 12px;
    padding-top: 12px;
  }

  /* Compact cards: all five have to fit, and the fifth was disappearing. Thumb
     size is set with the other thumb rules. */
  .collection-card {
    padding: 10px;
    gap: 10px;
    margin-bottom: 8px;
  }

  /* Actions stick to bottom */
  .ai-panel__actions,
  .ai-panel__actions--has-venues {
    flex-shrink: 0;
    padding: 10px 12px 12px;
  }
}

/* =============================================================================
   OFFICIAL CONTACT SECTION — pinned above the collection list.
   Shows the property's own contact on a premium venue page, the VenueWise
   contact everywhere else. Sized for the 440px panel (360px on tablet).
   ============================================================================= */
.cp-contact {
  flex-shrink: 0;
  padding: 4px 24px 20px 24px;
  font-family: var(--cp-font);
}

.cp-contact__title {
  color: var(--cp-text-white);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
  margin: 0;
}

.cp-contact__subtitle {
  color: var(--cp-text-empty);
  font-size: 14px;
  line-height: 1.4;
  margin: 2px 0 0 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cp-contact__person {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
}

.cp-contact__avatar {
  position: relative;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
}

.cp-contact__avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}

.cp-contact__avatar-initials {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: #f5f5f5;
  color: #555555;
  font-size: 13px;
  font-weight: 700;
}

.cp-contact__avatar-badge {
  position: absolute;
  bottom: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #e5cb00;
  transform: translate(25%, 25%);
}

.cp-contact__person-text {
  min-width: 0;
}

.cp-contact__person-name {
  color: #ffffff;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.25;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cp-contact__person-role {
  color: var(--cp-text-muted);
  font-size: 12px;
  line-height: 1.4;
  margin: 0;
}

.cp-contact__pills {
  display: flex;
  gap: 8px;
  margin-top: 16px;
}

.cp-contact__pill {
  flex: 1 1 0;
  min-width: 0;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--cp-radius);
  background: var(--cp-bg-raised);
  color: #d9d9d9;
  font-size: 13px;
  font-weight: 600;
  line-height: 20px;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--cp-transition) ease;
}

.cp-contact__pill:hover {
  background: var(--cp-border-inner);
}

.cp-contact__pill img {
  display: block;
  flex-shrink: 0;
}

.cp-contact__pill span {
  line-height: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Tablet panel is 360px: drop the pill gap and padding so three pills still fit
   on one row rather than wrapping. */
@media (min-width: 768px) and (max-width: 1023px) {
  .cp-contact {
    padding-left: 16px;
    padding-right: 16px;
  }

  .cp-contact__pills {
    gap: 6px;
  }

  .cp-contact__pill {
    padding: 8px 6px;
    gap: 6px;
    font-size: 12px;
  }
}

/* =============================================================================
   "INQUIRY SENT" NOTICE — relocated from the venue page sidebar.
   ============================================================================= */
.cp-sent-notice {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 24px 0 24px;
  font-family: var(--cp-font);
}

.cp-sent-notice__icon {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
}

.cp-sent-notice__text {
  color: #ffffff;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
  margin: 0;
}

.cp-sent-notice__text a {
  color: var(--cp-brand);
  text-decoration: underline;
}

/* Older than a day: no tick, no celebration, just the link back. */
.cp-sent-notice--quiet .cp-sent-notice__text {
  color: var(--cp-text-muted);
  font-weight: 400;
}

/* =============================================================================
   ADD TO COLLECTION CTA — the venue (room) page has no room cards of its own,
   so it carries a single toggle instead of a "+" on a card.
   ============================================================================= */
.cp-add-cta {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px 16px;
  border-radius: var(--cp-radius, 8px);
  background: #ffffff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}

.cp-add-cta__btn {
  width: 100%;
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 18px;
  border: 1px solid #c8d200;
  border-radius: 8px;
  background: #c8d200;
  color: #1a1a1a;
  font-family: 'Neue', sans-serif;
  font-size: 16px;
  font-weight: 700;
  line-height: 24px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.cp-add-cta__btn:hover {
  background: #d4df00;
  border-color: #d4df00;
}

.cp-add-cta__btn.is-selected {
  background: transparent;
  border-color: #1a1a1a;
  color: #1a1a1a;
}

.cp-add-cta__btn.is-selected:hover {
  background: #f5f5f5;
}

.cp-add-cta__hint {
  margin: 0;
  color: #757575;
  font-family: 'Neue', sans-serif;
  font-size: 13px;
  line-height: 1.5;
  text-align: center;
}

/* The reveal popup is appended to <body>, so it has to clear the panel it is
   triggered from: the panel is 9300 docked and 10100 as a phone bottom sheet,
   and the popup's own 10000 in main.css was written for the in-flow sidebar. */
.contact-info-popup {
  z-index: 10200;
}

/* As an overlay the panel has to outrank its own backdrop (10050). Only the
   listing page set this, via a !important rule scoped to .page-venues, so
   everywhere else the backdrop painted over the sheet and swallowed its taps.
   That was invisible while the venue pages carried their own contact block;
   now the panel is the only way to reach a contact, so it has to hold. */
@media (max-width: 1023px) {
  .ai-panel--open {
    z-index: 10100;
  }
}

/* =============================================================================
   INLINE PLACEMENT — the venue and room pages carry the collection view in their
   own right-hand column, in the slot the inquiry sidebar and its calendar used to
   hold, so it is always visible rather than opened from the nav.

   Below 1024px that column does not exist, so the same markup keeps its existing
   drawer/sheet behaviour and the nav collection button stays the way in.
   ============================================================================= */
@media (min-width: 1024px) {
  /* One height: a full viewport, always sticky, so the panel follows the reader
     and the send button never leaves the screen. Growing past the viewport is not
     an option, because a sticky box taller than the screen pins its top and
     buries its own footer, and sticking by the bottom edge does not help: sticky
     cannot drag an element down past where it started.

     So the room comes from the contents instead. The spacing below is tuned so a
     collection of four fits with nothing hidden; only a full five needs the list
     to scroll, and by a little. */
  /* Two cards, not one panel with a rule across it: the collection and the venue's
     contact details are different things, and a divider inside one box said they
     were sections of the same thing.

     The border and radius move off the panel onto each card, so the panel itself is
     just the sticky column that holds them. It stays a fixed-height sticky box, so
     both cards are on screen at once and the send action never needs scrolling to. */
  .ai-panel--inline {
    position: sticky;
    top: 4rem;
    width: var(--cp-width);
    max-width: 100%;
    align-self: flex-start;
    height: calc(100vh - 5rem);
    border: 0;
    border-radius: 0;
    background: transparent;
    overflow: visible;
    gap: 12px;
    transition: none;
  }

  .ai-panel--inline .ai-panel__card {
    background: var(--cp-bg);
    border: 1px solid var(--cp-border);
    border-radius: var(--cp-radius);
  }

  /* The collection is the part that gives: it holds the scrolling list, so it takes
     the leftover height. The flex behaviour itself is declared once outside this
     media query, because the drawer needs it too. */
  .ai-panel--inline .ai-panel__card--collection {
    overflow: hidden;
  }

  /* The contact card is only as tall as its content, and never squeezed: it is the
     thing the planner came for once the collection is built. */
  .ai-panel--inline .ai-panel__card--contact {
    flex: 0 0 auto;
  }

  .ai-panel--inline .ai-panel__content {
    flex: 1 1 auto;
  }

  /* Its own card now, so this is the padding inside that card rather than the gap
     that used to separate it from the collection above. */
  .ai-panel--inline .cp-contact {
    padding-top: 16px;
    padding-bottom: 22px;
  }

  .ai-panel--inline .cp-contact__person {
    margin-top: 12px;
  }

  .ai-panel--inline .cp-contact__pills {
    margin-top: 12px;
  }

  /* The one control that legitimately differs: an inline panel has nowhere to
     close to. Everything else about the block is deliberately identical to the
     drawer on the listing page. */
  .ai-panel--inline .ai-panel__close {
    display: none;
  }

  /* The icon stays on these pages too. It was hidden because they render the
     panel inline instead of as a drawer, so the button had nothing to open, but
     a header control that exists on the listing and vanishes on the property
     page reads as a bug and costs the planner the collection count they use to
     orient. collection-panel.js reveals and scrolls to the inline panel here
     instead of opening a drawer. */
}

/* =============================================================================
   NO LAYOUT SHIFT WHEN A DIALOG OPENS

   Every modal locks the page with `overflow: hidden; padding-right: <scrollbar>`
   on <body> (dist/main.js). Body is the layout container, so that padding does
   not preserve the width the scrollbar freed, it takes it away from the content:
   on the listing every card reflowed 339px to 336px the moment the sign-in modal
   appeared, and the same shift hit every page with a dialog.

   Reserving the gutter permanently means hiding the scrollbar changes nothing, so
   the compensation is not only unnecessary but is itself the shift.
   ============================================================================= */
html {
  scrollbar-gutter: stable;
}

body[style*="padding-right"] {
  padding-right: 0 !important;
}

/* The lock is applied to <body>, but on these pages <body> is not the scroller:
   the document element is. So `overflow: hidden` on body locked nothing, and the
   page carried on scrolling behind the open dialog while anything sticky stayed
   put, which is what made the venue page come apart when the sign-in view
   appeared. Lock the element that actually scrolls; it keeps its scroll offset,
   so nothing moves and sticky still resolves against it. */
html:has(body[style*="overflow: hidden"]) {
  overflow: hidden;
}

/* With the scroller locked above, body's own `overflow: hidden` is redundant, and
   it is the reason a sticky element comes unstuck while a dialog is open: it
   makes body a scroll container sitting at offset 0. Hand body back its default
   so anything sticky keeps working and nothing has to be pinned out of the flow
   to compensate. */
html:has(body[style*="overflow: hidden"]) > body {
  overflow: visible !important;
}

/* =============================================================================
   FLOATING SHEET — inline panel below 1024px

   The inline column only exists at ≥1024px. Below it the venue and room pages
   had no contact slot at all: the panel fell back to the nav-opened drawer, and
   the nav trigger is hidden on these pages (see above), so nothing reached it.

   Here the same markup is a sheet pinned to the bottom of the viewport. Collapsed
   it shows the contact block alone; the toggle expands it to the full panel and
   back. Height is driven inline by collection-panel.js so it can animate between
   the two, which is why nothing below sets `top`: with `bottom` already set that
   would over-constrain the box and invert the animation.

   The `!important`s override the drawer rules above, which are more specific
   (`body:not(.page-venues) .ai-panel`) and would otherwise slide this off-screen.
   ============================================================================= */
.ai-panel__sheet-toggle {
  display: none;
}

@media (max-width: 1023px) {
  .ai-panel--inline {
    position: fixed !important;
    top: auto !important;
    right: 0 !important;
    bottom: 0 !important;
    left: 0 !important;
    width: 100% !important;
    /* The phone rules above set `height: 100dvh !important`, which an inline
       style cannot beat, so the animated height arrives as a custom property
       that this declaration reads instead. Unset, it falls back to the
       collapsed content height. */
    height: var(--cp-sheet-height, auto) !important;
    max-height: 100dvh;
    transform: none !important;
    border: none !important;
    border-top: 1px solid var(--cp-border) !important;
    border-radius: 16px 16px 0 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    z-index: 9001;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
                border-radius 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
  }

  /* The sheet toggle is this panel's only open/close affordance, so the close
     button (which belongs to the drawer) stays out of the header here. */
  .ai-panel--inline .ai-panel__close {
    display: none;
  }

  /* Collapsed: the contact block and the send button, nothing else. */
  .ai-panel--inline .ai-panel__header,
  .ai-panel--inline .ai-panel__content {
    display: none;
  }

  /* The contact card follows the collection in the DOM, which is what the desktop
     split asks for. On the sheet that would put the send button above it and invert
     today's order, so it is pulled back to the top here only. */
  .ai-panel--inline .ai-panel__card--contact {
    order: -1;
  }

  .ai-panel--inline .ai-panel__sheet-toggle {
    background: transparent;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 40px;
    width: 40px;
    padding: 10px;
    position: absolute;
    right: 8px;
    top: 8px;
    z-index: 1;
    transition: transform 0.25s ease;
  }

  .ai-panel--inline .cp-contact {
    padding-top: 14px;
    padding-bottom: 14px;
  }

  /* Expanded: the whole panel, header and list included, filling the viewport.
     Above the backdrop (10050) and the sticky nav (9100), for the same reason
     `.ai-panel--open` is lifted above: at the collapsed z-index the backdrop
     painted over the sheet and swallowed its taps. */
  .ai-panel--inline.ai-panel--sheet-expanded {
    border-radius: 0;
    z-index: 10100;
  }

  .ai-panel--inline.ai-panel--sheet-expanded .ai-panel__sheet-toggle {
    transform: rotate(180deg);
  }

  .ai-panel--inline.ai-panel--sheet-expanded .ai-panel__header {
    display: flex;
  }

  .ai-panel--inline.ai-panel--sheet-expanded .ai-panel__content {
    display: block;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  .ai-panel--inline.ai-panel--sheet-expanded .cp-contact,
  .ai-panel--inline.ai-panel--sheet-expanded .ai-panel__header,
  .ai-panel--inline.ai-panel--sheet-expanded .ai-panel__actions {
    flex-shrink: 0;
  }

  /* Peek: the send button on its own.

     Reading down a venue page, the contact block is not what the planner is
     looking at and it costs them a fifth of a phone screen. On the way back up it
     is exactly what they want, so it comes back with them. The send button never
     leaves, because it is the one thing the sheet exists to offer.

     The height comes from the block's own measurement rather than a ceiling
     guessed high enough to clear it, which would spend the first part of the
     transition travelling through empty space and read as a stutter. */
  .ai-panel--inline .cp-contact {
    overflow: hidden;
    max-height: var(--cp-contact-height, 400px);
    opacity: 1;
    visibility: visible;
    transition: max-height 0.28s ease, opacity 0.16s ease 0.06s,
                margin 0.28s ease, padding 0.28s ease, visibility 0s;
  }

  .ai-panel--inline.ai-panel--sheet-peek .cp-contact {
    max-height: 0;
    opacity: 0;
    margin-top: 0;
    margin-bottom: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-width: 0;
    /* Out of the tab order, but only once it has finished leaving: taking it away
       at the start would drop focus mid-animation. */
    visibility: hidden;
    transition: max-height 0.28s ease, opacity 0.14s ease,
                margin 0.28s ease, padding 0.28s ease, visibility 0s linear 0.28s;
  }

  /* Asked not to be animated, the state still has to change, so it changes at
     once instead of sliding. */
  @media (prefers-reduced-motion: reduce) {
    .ai-panel--inline .cp-contact,
    .ai-panel--inline.ai-panel--sheet-peek .cp-contact {
      transition: none;
    }
  }

  /* The close control belongs to the docked panel; here the toggle is the
     only affordance, and the heading needs the width the button was holding. */
  .ai-panel--inline .ai-panel__close {
    display: none;
  }

  .ai-panel--inline .ai-panel__heading {
    width: 100%;
  }

  /* The toggle floats over this row, so the tabs stop short of it. */
  .ai-panel--inline .ai-panel__header {
    padding: 12px 56px 0 16px;
  }
}


/* -----------------------------------------------------------------------------
   PANEL LOAD STATES  (data-cp-state on .ai-panel)

   loading | empty | populated | error

   The empty state and the dashed slots both say "you have nothing", so neither
   may show before the read lands. Driven by the attribute alone, so the
   server-rendered HTML is already correct with no first-paint flash.

   collection-panel.js writes no inline display while loading, which is what lets
   these plain selectors win.
   ----------------------------------------------------------------------------- */
.cp-skeleton {
  display: none;
  padding-top: 2px;
}

.ai-panel[data-cp-state="loading"] .cp-skeleton {
  display: block;
}

.ai-panel[data-cp-state="loading"] #ai-empty-state,
.ai-panel[data-cp-state="loading"] #ai-empty-slots,
.ai-panel[data-cp-state="loading"] #ai-venues-list,
.ai-panel[data-cp-state="loading"] #ai-collection-error {
  display: none;
}

/* `error` has no collection to show either, so it suppresses the same ones. A
   failure message with five "Add your first venue" slots under it still says the
   collection is empty, which is what this state exists to avoid saying. */
.ai-panel[data-cp-state="error"] #ai-empty-state,
.ai-panel[data-cp-state="error"] #ai-empty-slots,
.ai-panel[data-cp-state="error"] #ai-venues-list,
.ai-panel[data-cp-state="error"] .cp-skeleton {
  display: none;
}

/* Same box as .collection-card, so the height does not jump on replacement. */
.cp-skeleton__card {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 12px;
  padding: 14px;
  border-radius: var(--cp-radius);
  border-bottom: 1px solid var(--cp-border-subtle);
  margin-bottom: 10px;
  animation: cp-shimmer 1.4s linear infinite;
  background: linear-gradient(to right, #222222 8%, #2e2e2e 18%, #222222 33%);
  background-size: 936px 100%;
}

.cp-skeleton__thumb {
  flex-shrink: 0;
  width: 116px;
  height: 72px;
  border-radius: 6px;
  background-color: rgba(255, 255, 255, 0.06);
}

.cp-skeleton__lines {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 4px;
}

.cp-skeleton__line {
  height: 12px;
  border-radius: 4px;
  background-color: rgba(255, 255, 255, 0.06);
}

.cp-skeleton__line--short {
  width: 55%;
}

.cp-skeleton__line--shorter {
  width: 35%;
}

@keyframes cp-shimmer {
  0% {
    background-position: -468px 0;
  }

  100% {
    background-position: 468px 0;
  }
}

/* Decoration only: the placeholder still reads as pending without it. */
@media (prefers-reduced-motion: reduce) {
  .cp-skeleton__card {
    animation: none;
  }
}

@media (max-width: 767px) {
  .cp-skeleton__thumb {
    width: 84px;
    height: 60px;
  }
}

/* -----------------------------------------------------------------------------
   LOAD FAILURE

   The collection's own content, not a banner over it: it stands in for the list
   rather than annotating it.
   ----------------------------------------------------------------------------- */
.cp-load-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 210px;
  padding: 16px;
  text-align: center;
}

.cp-load-error[hidden] {
  display: none;
}

.cp-load-error__title {
  margin: 0;
  font-family: var(--cp-font);
  font-size: 15px;
  color: var(--cp-text-empty);
}

.cp-load-error__body {
  margin: 0;
  font-family: var(--cp-font);
  font-size: 13px;
  color: var(--cp-text-empty-sub);
}

.cp-load-error__retry {
  margin-top: 4px;
  padding: 8px 18px;
  border: 1px solid var(--cp-border-inner);
  border-radius: var(--cp-radius-inner);
  background: var(--cp-bg-raised);
  color: var(--cp-text-primary);
  font-family: var(--cp-font);
  font-size: 13px;
  cursor: pointer;
  transition: background var(--cp-transition), border-color var(--cp-transition);
}

.cp-load-error__retry:hover {
  background: var(--cp-border-inner);
  border-color: var(--cp-text-dim);
}
