/* Shared styles for the public-facing surfaces (per-dealer mini-site,
   cross-dealer Marketplace) - loaded alongside theme.css, never instead
   of it. Structural reference for the mini-site's hero-search-then-grid
   pattern and card shape came from an early, now-superseded mockup (racing
   green/serif) - every color/font here is theme.css's current tokens
   (lime/white/black, Space Grotesk/Inter/Space Mono), not that mockup's. */

.public-page { max-width: 1100px; margin: 0 auto; }

.public-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 0; border-bottom: 1px solid var(--line); margin-bottom: 8px;
}
.public-header-brand { display: flex; align-items: center; gap: 10px; }
/* Item 4: dealer's own header logo, a small deliberate bump from 36px -
   this is the PRIMARY brand mark on a dealer's own mini-site page. */
.public-header-logo { width: 42px; height: 42px; border-radius: var(--radius-sm); object-fit: contain; background: var(--paper); }
.public-header-name { font-family: 'Space Grotesk', sans-serif; font-weight: 600; font-size: 1.05rem; color: var(--ink); }
/* Item 4: Motifiti's own wordmark, scoped to .public-header-brand only -
   this is the Marketplace's own header (marketplace_landing.html/
   marketplace_vehicle.html put Motifiti's brand-icon+name directly
   inside .public-header-brand), never the admin sidebar's own
   .brand-icon. Small, deliberate bump from the shared 14px .brand-icon
   default. */
.public-header-brand .brand-icon { height: 18px; margin-right: 6px; }
.public-header-brand .public-header-name { font-size: 1.15rem; }
/* Item: header/footer cleanup - _public_header.html's own wrapper for
   the shared _public_menu.html include, the only thing on the right of
   the mini-site header now ("Powered by Motifiti" moved to the footer,
   the top header's own copy removed entirely). */
.public-header-account { display: flex; align-items: center; gap: 10px; }
.lime-mark { color: var(--lime-deep); font-weight: 600; }

/* Item: consistent hamburger menu - the one real menu (_public_menu.html),
   now shared by every public page's header, not just marketplace
   landing's. A hamburger toggle revealing a dropdown, not an always-
   visible row (a real correction: the first cut of this was a cluttered
   always-visible row, against the stated no-clutter philosophy).
   .public-menu-item itself is a light-background
   equivalent of the sidebar's .nav-item/.soon-label pair, which is
   styled specifically for the dark sidebar (--sidebar-muted/--sidebar-
   faint) and would read wrong here, not reused directly. */
.public-menu-wrap { position: relative; }
.public-menu-toggle {
  background: none; border: none; padding: 6px; cursor: pointer; color: var(--ink);
  display: flex; align-items: center; border-radius: var(--radius-sm);
}
.public-menu-toggle:hover { background: var(--paper); }
.public-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 10;
  display: flex; flex-direction: column; gap: 2px; min-width: 160px;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.08);
}
/* Same [hidden]-vs-class-selector specificity tie as #search-clear-btn
   above - display:flex on the class beats the UA [hidden] rule on
   source order, so the panel stayed visible despite `hidden` being set.
   Caught live. */
.public-menu[hidden] { display: none; }
.public-menu-item { font-size: var(--font-size-small); color: var(--ink); text-decoration: none; display: flex; align-items: center; gap: 4px; padding: 6px 8px; border-radius: var(--radius-sm); }
.public-menu-item.disabled { color: var(--grey); cursor: not-allowed; }
.public-menu-item:not(.disabled):hover { background: var(--paper); color: var(--lime-deep); }
.public-menu-soon { margin-left: auto; font-size: 0.6rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--grey); }

.public-hero { padding: 40px 0 28px; max-width: 640px; }
.public-hero h1 { font-size: 1.9rem; line-height: 1.15; margin: 0; }
.public-hero p { margin: 12px 0 0; color: var(--grey); font-size: var(--font-size-body); }

.public-search-row { margin-top: 24px; display: flex; gap: 8px; max-width: 480px; position: relative; }
.public-search-row input { flex: 1; margin: 0; }
/* theme.css's .btn-secondary sets its own `display`, which ties the
   native [hidden] attribute selector on specificity and wins on source
   order (theme.css loads after the UA stylesheet) - caught live: the
   Clear button stayed visible despite the `hidden` attribute being
   present. This ID+attribute selector outranks .btn-secondary's class
   selector regardless of load order. */
#search-clear-btn[hidden] { display: none; }
.public-search-results {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 5;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-md);
  max-height: 320px; overflow-y: auto; display: none;
}
.public-search-results:not(:empty) { display: block; }
.public-search-result {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border-bottom: 1px solid var(--line-soft); color: var(--ink); font-size: var(--font-size-small);
}
.public-search-result:last-child { border-bottom: none; }
.public-search-result:hover { background: var(--row-hover); }
.public-search-result-thumb { width: 40px; height: 32px; border-radius: var(--radius-sm); object-fit: cover; background: var(--thumb-placeholder); flex-shrink: 0; }

.public-inventory-head { display: flex; align-items: baseline; justify-content: space-between; margin: 8px 0 20px; }
.public-inventory-head h2 { font-size: 1.2rem; margin: 0; }
.public-inventory-head span { color: var(--grey); font-size: var(--font-size-small); }
/* Item: a filtered count read the same as an unfiltered one - small and
   grey - so narrowing 277 cars to 27 looked like a small site rather
   than a filter that worked. Ink, not grey, when a filter is on: the
   difference is the whole point of the line. */
.public-inventory-head span.is-filtered { color: var(--ink); font-weight: 600; }

.public-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 18px; }
.public-vcard { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden; display: block; }
.public-vcard:hover { border-color: var(--lime-deep); }
.public-vcard-photo { height: 150px; background: var(--thumb-placeholder); }
.public-vcard-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.public-vcard-body { padding: 12px 14px 14px; }
.public-vcard-name { font-size: 0.92rem; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.public-vcard-sub { color: var(--grey); font-size: var(--font-size-label); margin-top: 2px; }
.public-vcard-price-row { display: flex; align-items: baseline; justify-content: space-between; margin-top: 10px; }
.public-vcard-price { font-family: 'Space Mono', monospace; font-size: var(--font-size-body); color: var(--ink); font-weight: 700; }

/* Marketplace's own LIST rows (Slice 5) - deliberately distinct from
   .public-vcard above rather than reused, per the confirmed grid-for-
   mini-site / list-for-Marketplace split. */
/* Item: vehicle card redesign - a real two-column split (image | text),
   not the previous wrapping layout (photo+heading shared row one, price+
   dealer wrapped to a row below - the image never actually spanned "one
   side" of the card at any width). align-items: stretch (the flex
   default, stated explicitly here since a later rule used to override it
   to center) makes .public-listing-photos stretch to match .public-
   listing-main's own full height - whatever that ends up being, driven
   by the stacked text content, not a fixed thumbnail size. */
.public-listing-row {
  display: flex; align-items: stretch; gap: 14px; padding: 14px 16px;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg);
  margin-bottom: 10px;
}
.public-listing-row:hover { border-color: var(--lime-deep); }
/* Wider than the old 88px inset thumbnail, and no fixed height - stretches
   via align-items: stretch above. min-height keeps a reasonable shape
   even for a listing with very little text (few specs set). */
/* Item: portrait photos were driving the row height. A seller shooting
   a car on a phone held upright uploads a 177x400 image, and this box
   had a width but no definite HEIGHT - so `height: 100%` on the img
   resolved to auto, the image rendered at its natural ratio, and
   object-fit: cover never got the chance to crop. Measured: a landscape
   row was 140px tall and the portrait one beside it 346px.

   aspect-ratio gives the frame a definite shape of its own, so the row
   height comes from the text as it was always meant to and every photo
   is cropped to the same window regardless of how it was shot. 4:3
   rather than square: a car is a wide object, and a square frame throws
   away more of it than it needs to. */
.public-listing-photos {
  width: 140px; aspect-ratio: 4 / 3; min-height: 110px; flex-shrink: 0;
  border-radius: var(--radius-sm); overflow: hidden;
}
/* On a phone the 140px photo left about 150px for everything else,
   which is what made the row so easy to overflow in the first place.
   Buying back 30px for the text is worth more than 30px of thumbnail. */
@media (max-width: 420px) {
  .public-listing-photos { width: 110px; min-height: 82px; }
}
.public-listing-photos img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* flex-direction: column - the real mechanism behind "everything stacks
   in this one column, nothing wraps below the image": heading/specs sit
   at the top, .public-listing-price-row's own margin-top: auto pushes
   price + dealer attribution to the bottom regardless of how much specs
   content sits above it. */
.public-listing-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.public-listing-heading { font-size: 0.92rem; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Item 1 redesign: engine_cc/fuel_type/transmission as an icon row -
   shared by both the Marketplace list row and the mini-site grid card
   (same class names on both templates, one rule covers both) - mirrors
   _public_vehicle_detail.html's own .vd-icon-row, just smaller since this
   repeats per row/card rather than once on a detail page. Item: vehicle
   card redesign - also now reused for the condition/location line
   (usage_condition + current_location_country), split onto its own line
   from the engine/mileage specs rather than mixed into one, mirroring
   _public_vehicle_detail.html's own badge-row/icon-row split. */
.public-listing-engine-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 3px; color: var(--grey); font-size: var(--font-size-label); }
.public-listing-icon-item { display: inline-flex; align-items: center; gap: 3px; }
.public-listing-icon-item .icon { width: 13px; height: 13px; }
.public-listing-condition { color: var(--grey); }
.public-listing-stats { font-size: var(--font-size-label); color: var(--grey); margin-top: 2px; }
/* Item: vehicle card redesign - price + dealer attribution's own row,
   bottom-anchored in the text column (see .public-listing-main's own
   margin-top: auto comment above). font-size dropped from --font-size-
   body (0.87rem) to --font-size-small (0.8rem) - the old size was only
   marginally smaller than the 0.92rem heading despite being a heavier
   weight (700 vs 600), which read as "about the same size", not
   deliberately smaller. Still bold. */
/* Item: the row scrolled the page sideways. This is a flex row holding
   a price that is white-space:nowrap and a logo that is flex-shrink:0 -
   so NEITHER child can give way, and when the two together exceed the
   column the logo is simply pushed out past the card, the viewport and
   the document. Reproduced at 390px on "More than 5M": the logo's left
   edge sat 15px beyond its own parent's right edge, and the document
   measured 398px against a 386px viewport.
   It showed up on that filter because the overflow needs a WIDE price
   to trigger it - and direct imports add a "taxes in" note beside the
   number, which is the extra width that tips it over. Nothing about the
   filter itself was broken; it just selected for expensive cars.
   flex-wrap lets the logo drop to its own line instead of leaving the
   card. */
.public-listing-price-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: auto; padding-top: 8px; flex-wrap: wrap; }
.public-listing-price { font-family: 'Space Mono', monospace; font-size: var(--font-size-small); font-weight: 700; color: var(--ink); white-space: nowrap; }
/* Item 4: small, deliberate bump from 32px. */
.public-listing-dealer-logo { margin-left: auto; width: 38px; height: 38px; border-radius: var(--radius-sm); object-fit: contain; background: var(--paper); flex-shrink: 0; }
/* Item: vehicle card redesign - a real "User: {name}" label for a
   private seller (see the template's own account_type branch) reads at
   --font-size-micro (0.65rem, this app's smallest real text size,
   otherwise reserved for uppercase badges/pills) - deliberately smaller
   than the plain dealer-name fallback (--font-size-label, 0.72rem, kept
   for the "Organization with no logo yet" case), so a private seller's
   name never carries the same visual weight as a real 38px org logo. */
/* Item: trimmed dealer name on marketplace cards. Capped at 22ch with
   an ellipsis - the longest real dealer name today is 19 characters, so
   nothing truncates yet; 22 leaves headroom for realistic future names
   ("Ride on deals KE Motors Ltd") while capping the clutter on a card
   whose job is to sell the CAR, not the dealership.

   Done in CSS rather than by slicing the string server-side, so the
   full name stays in the DOM for screen readers, copy-paste and the
   title tooltip - only the visual width is capped. The dealer's own
   mini-site page is untouched and still shows the name in full. */
.public-listing-dealer-fallback,
.public-listing-user-label {
  color: var(--grey); flex-shrink: 0; text-align: right;
  /* The real limit is now the TEXT (storefront.model.card_dealer_name());
     this stays only as a backstop so an unexpectedly long value can
     never push the price off the row. */
  min-width: 0; max-width: 18ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.public-listing-dealer-fallback { font-size: var(--font-size-label); }
.public-listing-user-label { font-size: var(--font-size-micro); }

/* Item 3: filter groups now sit above the search bar, each with its own
   helper label - a real "All" chip in both groups (theme.css's existing,
   previously-unused .filter-chip/.filter-chip.active pair) makes "no
   filter" an explicit, visible choice rather than an implied one. */
.public-filter-group { margin-top: 16px; }
.public-filter-group .public-filter-row { margin-top: 6px; }
.public-filter-label { font-size: var(--font-size-small); color: var(--grey); font-weight: 500; }
/* Marketplace's own filter row - location .filter-chip toggles plus
   min/max price inputs, all combined with the search box into one
   query string the page navigates to (see marketplace_landing.html's own
   script), so a filtered view is a shareable link. */
.public-filter-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 16px; }
.public-filter-row input[type=number] { width: 150px; margin: 0; }
/* Item: "Find a dealer". Reuses .public-search-results, the same
   dropdown the vehicle typeahead draws, so a suggestion looks the same
   wherever a suggestion appears. */
/* Item: the "Find a dealer" lookup sits directly under the search box
   now, not up among the car filters. The filters narrow a list of cars;
   this one leaves the list and jumps to a seller's own page, so it reads
   as part of the search rather than as a fourth filter.

   10px, not the search row's own 24px: close enough to the box above to
   read as belonging with it, rather than as a third band of controls. */
.dealer-lookup { margin-top: 10px; max-width: 480px; }
.dealer-lookup .dealer-search-row { margin-top: 8px; }
.dealer-search { position: relative; width: 100%; max-width: 360px; }
.dealer-search input { width: 100%; box-sizing: border-box; margin: 0; }
.dealer-search .public-search-results { left: 0; right: 0; }
.dealer-search-count { color: var(--grey); font-size: var(--font-size-small); margin-left: auto; padding-left: 12px; }
.dealer-search-none { color: var(--grey); font-size: var(--font-size-small); padding: 10px 12px; margin: 0; }
/* Item 5: sort select sits in the same flex row as the price-bucket
   chips - theme.css's generic `select { width: 100% }` would otherwise
   force it onto its own line inside a wrapping flex row. */
.public-sort-select { width: auto; min-width: 170px; margin: 0; }
/* Same [hidden]-vs-class-selector fix as .public-menu/.vd-contact-panel -
   .public-filter-row's own display:flex beats the native [hidden] rule
   on specificity, so the Custom price row stayed visible by default. */
#custom-price-row[hidden] { display: none; }

.public-close-link { display: inline-flex; align-items: center; gap: 4px; font-size: var(--font-size-small); color: var(--grey); margin: 14px 0; }
.public-close-link:hover { color: var(--black); }

/* _public_vehicle_detail.html reuses vehicle_detail.html's markup/classes
   for its read-view, but those classes' styles live in vehicle_detail.html's
   own <style> block, which the public pages never load - ported the exact
   subset the shared partial actually uses (the auth-only edit-panel/action-
   row classes are deliberately left out, since this partial never renders
   those). Caught live: without these, the stock-ref line ran into the
   close-link above it, the icon row lost its spacing, and the price lost
   its Space Mono/bold/large treatment entirely. */
.vd-stock-id { display: block; margin: 0 0 2px; }
/* Item 3: now up to 4 items (mileage/engine/fuel/transmission), not 2 -
   wrap added for narrow viewports, row-gap keeps wrapped rows from
   crowding. */
.vd-icon-row { display: flex; flex-wrap: wrap; gap: 16px; row-gap: 8px; margin: 0 0 10px; color: var(--grey); font-size: var(--font-size-small); }
.vd-icon-item { display: inline-flex; align-items: center; gap: 5px; }
/* Item 3: a real responsive grid replacing the single small photo strip -
   noticeably larger cells than vehicle_detail.html's own compact .thumbs
   (100x76px, admin-only, unchanged) since this is the buyer-facing photo
   gallery a purchase decision leans on. Roughly square cells, 2-4 per row
   depending on viewport width. */
.vd-photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; margin-top: 12px; }
.vd-photo-grid img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--line); cursor: pointer; display: block; }
.vd-more { margin-top: 8px; }
.vd-more > summary { cursor: pointer; font-weight: 600; font-family: 'Space Grotesk', sans-serif; padding: 7px 0; color: var(--ink); }
.vd-more[open] > summary { margin-bottom: 5px; }
/* Item: the Description spent a fifth of the page saying one line.
   Measured live on a real listing: 28 characters - one 21px line of
   text - inside a 140px block. The text was not the problem; the
   spacing around it was doubled.
   A <p> carries its own 14px top and bottom margin, and .card already
   pads by 20px, so every card here stacked padding on margin: 68px of
   vertical whitespace wrapped around 21px of words. The card's padding
   is the spacing - the first and last child's own margins are just a
   second, invisible copy of it.
   Scoped to .vd-more's cards, so the photo-grid card above (where 20px
   genuinely suits a gallery) is untouched. */
.vd-more .card { padding: 12px 16px; margin-bottom: 0; }
.vd-more .card > :first-child { margin-top: 0; }
.vd-more .card > :last-child { margin-bottom: 0; }
/* Item: UX updates round - 30% smaller than the shared --font-size-price
   (1.2rem) theme.css defines - a deliberate override scoped to just this
   public-facing rule, not the variable itself, since vehicle_detail.
   html's own internal admin copy of .vd-price (a completely different
   page/audience) was never part of this request and stays unchanged. */
.vd-price { font-family: 'Space Mono', monospace; font-weight: 700; font-size: 0.84rem; color: var(--black); margin-bottom: 20px; }
/* CTA row - same shape as vehicle_detail.html's own .vd-actions.
   Item: button-row rework - now two rows (primary/secondary - see
   _public_vehicle_detail.html's own comment on why), each wrapping
   independently. align-items: flex-start (not center) on the primary
   row specifically - .vd-chat-wrap's own helper-text <p> makes that one
   flex item taller than a plain button, and center-alignment was
   pulling the actual buttons off a shared baseline.

   Item: UX follow-up - all five buttons stay the same lime .btn style
   (color was never the actual problem here - reverted after a wrong
   diagnosis). The real, measured issue: with .btn's plain display:
   inline-block, each button shrink-wraps to its own label width alone -
   confirmed live, these ranged from 78px ("Share") to 167px ("Chat with
   Seller"), more than double, which is what actually read as disorderly/
   disproportionate, not the color. min-width gives every button in
   these two rows a shared floor so a short label doesn't look tiny and
   cramped next to a long one. min-height (rather than tuning line-height
   to try to match the 18px icon exactly) fixes the other measured gap -
   an icon-bearing button (Get Directions, 43px) was rendering 2-3px
   taller than a text-only one (40-41px) since theme.css's .icon is
   slightly taller than this font's own line-height. */
/* Item: CTA hierarchy, for real this time. The comment here used to say
   the second row was "back to .btn-secondary - real, lesser-weight
   utilities" while the markup gave all five buttons class="btn" - so
   every action on the page rendered in identical lime and "Chat with
   Seller" looked exactly like "Share". Three tiers now, and the markup
   matches what this says.

   On a phone the two real actions go full width and stack: a buyer
   decides between them, and side-by-side halves make that a guess at
   two truncated labels. */
.vd-cta-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 20px 0 8px; }
.vd-cta-row-primary {
  align-items: stretch; flex-direction: column; gap: 10px; margin-bottom: 14px;
}
.vd-cta-row-primary > .vd-chat-wrap,
.vd-cta-row-primary > .vd-contact-wrap { width: 100%; }

/* Tier 1 - the action this page exists to produce. */
.vd-cta-primary {
  width: 100%; min-height: 52px; box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  font-size: 0.95rem;
}
/* Tier 2 - the same goal by another channel, so it reads as an
   alternative rather than a rival. */
.vd-cta-secondary {
  width: 100%; min-height: 46px; box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
}
/* Tier 3 - utilities. Not buttons: they do not compete for the decision.
   Still 44px of tap target, which is the reason for the padding. */
.vd-cta-row-utility {
  gap: 18px; margin: 0 0 8px;
  padding-top: 14px; border-top: 1px solid var(--line);
}
.vd-cta-util {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px; padding: 0; border: none; background: none;
  font-family: 'Inter', sans-serif; font-size: 0.82rem; font-weight: 500;
  color: var(--grey); text-decoration: none; cursor: pointer;
}
.vd-cta-util:hover { color: var(--ink); }
.vd-cta-util .icon { width: 15px; height: 15px; }

@media (min-width: 560px) {
  /* Room for both: side by side, each sized to its own words. */
  .vd-cta-row-primary { flex-direction: row; align-items: flex-start; }
  .vd-cta-row-primary > .vd-chat-wrap,
  .vd-cta-row-primary > .vd-contact-wrap { width: auto; }
  .vd-cta-primary { width: auto; min-width: 210px; }
  .vd-cta-secondary { width: auto; min-width: 180px; }
}

/* Contact Seller reveal - same hidden-by-default dropdown pattern as the
   Marketplace header's .public-menu-wrap/.public-menu (see above), just
   opening below-left instead of below-right. */
.vd-contact-wrap { position: relative; }
.vd-contact-panel {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 10;
  display: flex; flex-direction: column; gap: 2px; min-width: 180px;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.08);
}
/* Same [hidden]-vs-class-selector fix as .public-menu above. */
.vd-contact-panel[hidden] { display: none; }
.vd-contact-option { display: flex; align-items: center; gap: 8px; font-size: var(--font-size-small); color: var(--ink); text-decoration: none; padding: 8px; border-radius: var(--radius-sm); }
.vd-contact-option:hover { background: var(--paper); color: var(--lime-deep); }

/* Item: real-time chat, Slice 3 - "Chat with Seller" entry point. Same
   dropdown-panel shape as .vd-contact-wrap/.vd-contact-panel just above
   (deliberately consistent - this is the same "reveal panel" pattern,
   just with a message thread inside instead of a list of links), wider
   to fit a real conversation instead of a handful of link rows. */
.vd-chat-wrap { position: relative; }
/* The old permanent helper under the button ("Use in-app chat to message
   the seller") explained the mechanism rather than the benefit, and sat
   there for everyone forever. What it was trying to say now lives inside
   the panel, at the moment it matters - see .vd-chat-intro-lead. */
.vd-chat-helper { font-size: 0.74rem; margin: 4px 0 0; }
.vd-chat-panel-header {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 12px 16px 10px; border-bottom: 1px solid var(--line-soft);
}
.vd-chat-panel-identity { display: flex; align-items: center; gap: 10px; min-width: 0; }
.vd-chat-panel-avatar {
  width: 34px; height: 34px; flex-shrink: 0; border-radius: 50%;
  background: var(--avatar-bg); color: var(--avatar-text);
  display: flex; align-items: center; justify-content: center;
  font-family: 'Space Grotesk', sans-serif; font-size: 0.78rem; font-weight: 700;
}
.vd-chat-panel-who { display: flex; flex-direction: column; min-width: 0; }
.vd-chat-panel-title { font-weight: 600; font-size: var(--font-size-small); color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Which car this thread is about. A buyer messaging three sellers about
   three cars could not otherwise tell these panels apart. */
.vd-chat-panel-about { font-size: 0.68rem; color: var(--grey); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vd-chat-close { background: none; border: none; padding: 6px; line-height: 1; font-size: 1.3rem; color: var(--grey); cursor: pointer; }
.vd-chat-close:hover { color: var(--ink); }

/* The way in, instead of an empty box. */
.vd-chat-intro { padding: 14px 16px 4px; display: flex; flex-direction: column; gap: 10px; }
.vd-chat-intro[hidden] { display: none; }
.vd-chat-intro-lead { margin: 0; font-size: 0.8rem; line-height: 1.5; color: var(--grey); }
.vd-chat-intro-label {
  margin: 0; font-size: 0.66rem; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--grey);
}
.vd-chat-openers { display: flex; flex-wrap: wrap; gap: 8px; }
.vd-chat-opener {
  min-height: 44px; padding: 10px 14px;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  font-family: 'Inter', sans-serif; font-size: 0.8rem; font-weight: 500; cursor: pointer;
}
.vd-chat-opener:hover { border-color: var(--lime-deep); background: var(--row-hover); }

/* Said before they type, not after they lose the message. */
.vd-chat-signin {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0 16px 12px; padding: 12px 14px;
  background: var(--badge-pending-bg); border-radius: var(--radius-md);
  font-size: 0.78rem; line-height: 1.5; color: var(--ink);
}
.vd-chat-signin[hidden] { display: none; }
.vd-chat-signin .icon { width: 17px; height: 17px; flex-shrink: 0; margin-top: 1px; color: var(--badge-pending-text); }

.vd-chat-panel {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 10;
  display: flex; flex-direction: column;
  width: min(360px, 90vw); background: var(--white); border: 1px solid var(--line);
  border-radius: var(--radius-md); box-shadow: 0 4px 16px rgba(0,0,0,0.08); overflow: hidden;
}

/* Item: on a phone a conversation was a 340px panel floating over the
   page, anchored to a button. A bottom sheet is the shape this already
   wanted - full width, thumb-reachable, and it cannot be clipped by an
   ancestor the way an absolutely-positioned panel can. */
@media (max-width: 559px) {
  .vd-chat-panel {
    position: fixed; top: auto; left: 0; right: 0; bottom: 0; z-index: 60;
    width: auto; max-height: 88vh;
    border: none; border-top: 1px solid var(--line);
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -6px 24px rgba(0,0,0,0.12);
  }
  .vd-chat-messages { max-height: 40vh; }
}
.vd-chat-panel[hidden] { display: none; }
.vd-chat-messages { display: flex; flex-direction: column; gap: 8px; padding: 12px; max-height: 320px; overflow-y: auto; }
.vd-chat-message { max-width: 80%; padding: 8px 10px; border-radius: var(--radius-sm); font-size: var(--font-size-small); line-height: 1.35; word-wrap: break-word; }
.vd-chat-message.mine { align-self: flex-end; background: var(--lime-deep); color: var(--white); }
.vd-chat-message.theirs { align-self: flex-start; background: var(--paper); color: var(--ink); }
.vd-chat-form { display: flex; gap: 8px; padding: 12px 16px 16px; }
.vd-chat-form input { flex: 1; min-width: 0; min-height: 46px; }
.vd-chat-send { flex-shrink: 0; min-height: 46px; display: inline-flex; align-items: center; justify-content: center; gap: 7px; }
.vd-chat-send .icon { width: 18px; height: 18px; }
.vd-chat-status { padding: 0 12px 8px; font-size: var(--font-size-small); }
.thumbs img { width: 100px; height: 76px; }

/* Item: UX updates round - the persistent mini-site "Contact" section
   (_public_contact_section.html) the hamburger's "Contact This Seller"
   now jumps to, replacing the old per-item WhatsApp/Email/Call/Get
   Directions links that used to live directly in the hamburger. */
.public-contact-section { padding: 20px 0; border-top: 1px solid var(--line); margin-top: 20px; }
.public-contact-section h2 { font-size: 0.95rem; margin: 0 0 10px; color: var(--ink); }
.public-contact-name { font-weight: 600; color: var(--ink); margin: 0 0 4px; }
.public-contact-address { color: var(--grey); font-size: var(--font-size-small); margin: 0 0 12px; }
.public-contact-options { display: flex; flex-wrap: wrap; gap: 10px; }
.public-contact-option { display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; border-radius: var(--radius-md); background: var(--paper); color: var(--ink); font-size: var(--font-size-small); text-decoration: none; }
.public-contact-option:hover { background: var(--lime); }

.public-footer { padding: 28px 0; border-top: 1px solid var(--line); color: var(--grey); font-size: var(--font-size-small); margin-top: 20px; }
/* Item: header/footer cleanup - "Powered by Motifiti" in the footer is
   now a real link back to the Marketplace (the top header's own copy
   was removed entirely, not just relocated). Same muted color as the
   rest of the footer line, an underline is the only "this is clickable"
   signal - matches .public-close-link's own understated treatment. */
.public-footer-link { color: inherit; text-decoration: underline; }
.public-footer-link:hover { color: var(--black); }

.public-empty { color: var(--grey); font-size: var(--font-size-body); padding: 24px 0; }


/* Item 9: marketplace footer - logo + tagline, whole block tappable. */
.public-footer-link {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 18px 16px; text-decoration: none; color: inherit;
}
.public-footer-link:hover .public-footer-tagline { color: var(--ink); }
.public-footer-text { display: flex; flex-direction: column; line-height: 1.25; text-align: left; }
.public-footer-text strong { font-size: 0.95rem; }
.public-footer-tagline { font-size: 0.72rem; color: var(--grey); }


/* Item 4: "Logged in as {account}" in the public hamburger. Stacked, so
   the label reads as a caption rather than competing with the name. */
.public-menu-identity { flex-direction: column; align-items: flex-start; gap: 0; line-height: 1.3; }
.public-menu-identity-label { font-size: 0.68rem; color: var(--grey); }
.public-menu-identity-name { font-weight: 600; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


/* Item 17: "Seller: {name}" in the vehicle-page chat panel - the label
   muted so the dealer's own name stays the emphasis. */
.vd-chat-panel-role { color: var(--grey); font-weight: 400; }


/* Item: one price field - the C&F / taxes-in qualifier beside a
   direct-import price on a card. Muted and small: it qualifies the
   number without competing with it. */
.public-listing-price-note { font-size: 0.68em; font-weight: 400; color: var(--grey); margin-left: 3px; }


/* Item: Marketplace pagination. Real links, thumb-sized, and the
   unavailable direction stays in place rather than disappearing - a row
   that changes width as you page through is disorienting. */
.public-pagination {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  margin: 22px 0 8px; flex-wrap: wrap;
}
.public-page-link {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--white); color: var(--ink);
  font-size: var(--font-size-small); font-weight: 600; text-decoration: none;
}
.public-page-link:hover { border-color: var(--lime-deep); }
.public-page-link.disabled { color: var(--grey); background: var(--paper); border-color: var(--line); }
.public-page-of { font-size: var(--font-size-small); color: var(--grey); }


/* Item: AI running-cost estimates. The notice is amber, not red -
   nothing is wrong, but a buyer must not mistake these for measured
   specs. It sits ABOVE the numbers deliberately: a caveat printed under
   a figure is read after the figure has already been believed. */
.vd-ai-notice {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0 0 14px; padding: 11px 13px;
  background: var(--badge-pending-bg); border-radius: var(--radius-md);
  font-size: 0.78rem; line-height: 1.5; color: var(--ink);
}
.vd-ai-notice .icon { width: 16px; height: 16px; flex-shrink: 0; margin-top: 1px; color: var(--badge-pending-text); }
.vd-running-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.vd-running-item {
  flex: 1 1 100px; min-width: 0; display: flex; flex-direction: column; gap: 2px;
  padding: 10px 12px; background: var(--paper); border-radius: var(--radius-md);
}
.vd-running-label { font-size: 0.72rem; color: var(--grey); }
.vd-running-value { font-family: 'Space Mono', monospace; font-size: 0.92rem; font-weight: 700; color: var(--ink); }
.vd-running-note { margin: 8px 0 0; font-size: 0.74rem; line-height: 1.5; color: var(--grey); }
.vd-running-insurance {
  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 2px;
}

/* Item: Google Analytics consent banner. Fixed to the bottom rather
   than a centre-screen modal - a buyer who landed on a car should be
   able to keep reading it while deciding, and a modal over the one thing
   they came for is its own kind of pressure.

   Accept and Decline are the SAME size, weight and font. Accept carries
   the brand fill only because it is the affirmative action, which is the
   convention everywhere else on this site; Decline is one click, in the
   same place, never hidden behind "manage preferences". */
.consent-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 12px 20px;
  padding: 14px 18px;
  background: var(--white); border-top: 1px solid var(--line);
  box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.10);
}
.consent-banner-text {
  margin: 0; flex: 1 1 320px; min-width: 0;
  font-size: 0.8rem; line-height: 1.55; color: var(--ink);
}
.consent-banner-actions { display: flex; gap: 10px; flex-shrink: 0; }
.consent-btn {
  min-height: 42px; padding: 10px 20px;
  border-radius: var(--radius-md); border: 1px solid var(--line);
  background: var(--white); color: var(--ink);
  font-family: inherit; font-size: 0.82rem; font-weight: 600;
  cursor: pointer;
}
.consent-btn-accept { background: var(--lime); border-color: var(--lime); color: var(--black); }
.consent-btn:hover { border-color: var(--grey); }
.consent-btn-accept:hover { background: var(--lime-deep); border-color: var(--lime-deep); }
.consent-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

@media (max-width: 560px) {
  /* Tighter on a phone, where this sits over the one thing the visitor
     came to see. The two buttons share a row rather than stacking - one
     full-width button above another is the layout that turned this into
     half a screen. */
  .consent-banner { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px 14px; }
  .consent-banner-text { flex: 0 0 auto; font-size: 0.78rem; }
  .consent-banner-actions { justify-content: stretch; }
  .consent-btn { flex: 1 1 0; min-height: 40px; padding: 9px 14px; }
}

/* Item: sold / reserved.

   Sold is grey, not red. A sold car is not an error or a warning - it is
   a listing doing its job, and colouring it like a problem would read as
   one to the dealer whose car it is. Reserved borrows the amber already
   used for "pending" elsewhere, because that is exactly what it is. */
.vd-availability {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
  margin: 0 0 14px; padding: 12px 14px; border-radius: var(--radius-md);
  font-size: 0.82rem; line-height: 1.5;
}
.vd-availability strong { font-size: 0.92rem; }
.vd-availability-sold { background: var(--line-soft); color: var(--ink); }
.vd-availability-reserved { background: var(--badge-pending-bg); color: var(--ink); }

.listing-availability {
  display: inline-block; margin-right: 6px; padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.03em;
  text-transform: uppercase; vertical-align: middle;
}
.listing-availability-sold { background: var(--line); color: var(--grey); }
.listing-availability-reserved { background: var(--badge-pending-bg); color: var(--badge-pending-text); }

/* The way out of a sold listing. Full width and primary weight, because
   on a page whose subject is gone this is the only thing left worth
   tapping. */
.vd-sold-next { margin-top: 20px; }
.vd-sold-next .vd-cta-primary { display: block; text-align: center; text-decoration: none; }

/* Item: numbered pagination. The current page is a span, not a link -
   a link to where you already are is a dead tap, and on a phone it is
   a dead tap right next to the two that matter. */
.public-page-link.current {
  background: var(--lime); border-color: var(--lime); color: var(--black); font-weight: 700;
}
.public-page-gap { padding: 0 4px; color: var(--grey); font-size: 0.82rem; }
@media (max-width: 480px) {
  /* Numbers wrap rather than pushing Prev and Next off the screen. */
  .public-pagination { flex-wrap: wrap; justify-content: center; gap: 6px; }
}

/* Item: report a listing. The quietest control on the page, on purpose.
   A prominent Report button beside Contact seller would read as a
   warning about that particular car and cost honest sellers enquiries.
   Someone looking for it finds it; nobody else is nudged toward it. */
.vd-report { margin: 28px 0 8px; text-align: center; }
.vd-report-link {
  background: none; border: none; color: var(--grey, #888); cursor: pointer;
  font-size: 0.76rem; text-decoration: underline; padding: 6px 10px;
}
.vd-report-link:hover { color: var(--ink, #333); background: none; }
.vd-report-panel {
  max-width: 420px; margin: 8px auto 0; text-align: left;
  border: 1px solid var(--line, #e8e8e8); border-radius: 10px; padding: 16px;
}
.vd-report-intro { font-size: 0.82rem; color: var(--grey, #888); margin: 0 0 12px; }
.vd-report-panel label { display: block; font-size: 0.76rem; color: var(--grey, #888); margin-bottom: 4px; }
.vd-report-panel select, .vd-report-panel textarea { width: 100%; margin-bottom: 12px; }
.vd-report-actions { display: flex; gap: 8px; }
.vd-report-thanks { font-size: 0.82rem; color: var(--grey, #888); margin: 8px 0 0; }

/* Item: make / model / body-type landing pages.
   Breadcrumbs and category pills. The pill borrows .filter-chip's shape
   deliberately: on this site a bordered pill already means "narrow the
   list down", and a landing link does exactly that, so it should not
   arrive looking like a fourth kind of control. */
.public-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 0.78rem; color: var(--grey); margin: 0 0 12px; }
.public-crumbs a { color: var(--grey); text-decoration: none; }
.public-crumbs a:hover { color: var(--black); text-decoration: underline; }

.public-sublinks { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 24px; }
.public-sublink {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 8px 15px; border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: var(--white); color: var(--ink); font-size: 0.84rem; font-weight: 500;
  text-decoration: none;
}
.public-sublink:hover { border-color: var(--lime-deep); }
.public-sublink-count { color: var(--grey); font-size: 0.74rem; font-family: 'Space Mono', monospace; }

.public-browse-section { margin: 0 0 8px; }
.public-browse-section h2 { font-size: 1.05rem; margin: 0 0 12px; }
/* Centred with the logo block above it. The footer's only other child is
   a centred flex row, so a plain inline-block link sat hard against the
   left edge looking like it had come loose from something. */
.public-footer-browse { display: block; text-align: center; color: var(--grey); font-size: var(--font-size-small); text-decoration: underline; }
.public-footer-browse:hover { color: var(--black); }

/* Item: the backup description, shown only when a seller wrote none.
   Deliberately quieter than a real description: it is the platform
   talking about a type of car, and it must never read as the seller's
   own words about this one. */
.vd-framing { margin: 0 0 8px; }
.vd-framing-specs { margin: 0 0 10px; font-family: 'Space Mono', monospace; font-size: 0.8rem; color: var(--grey); }
.vd-framing-tag { margin: 0; font-size: 0.74rem; color: var(--grey); font-style: italic; }

/* Item: the closest cars above a budget that found nothing. Set apart
   from a normal result list on purpose - these are deliberately NOT what
   the buyer asked for, and a heading that says so is the difference
   between a helpful offer and a filter that looks broken. */
.public-over-budget { margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--line); }
.public-over-budget h2 { font-size: 1.05rem; margin: 0 0 4px; }
.public-over-budget-note { margin: 0 0 14px; font-size: var(--font-size-small); color: var(--grey); }

/* Item: recording terms acceptance.

   _terms_notice.html is included by signup.html, login.html and
   become_seller.html, all three of which load this file. One rule here
   rather than the same block pasted into three <style> elements, which
   is how two pages end up with the sentence a different size after
   somebody tunes one of them.

   Quieter than the links around it, because it is a statement rather
   than somewhere to go. */
.terms-notice {
  text-align: center;
  margin: 18px 0 0;
  font-size: 0.78rem;
  color: var(--grey);
  line-height: 1.5;
}
