/* carlots.ng — Django port stylesheet.
 *
 * Every number here is traceable to docs/DESIGN-SYSTEM.md, which records where it was measured
 * on the live site. Read that file before changing a value: the point of this sheet is that the
 * design is a contract rather than a matter of taste.
 *
 * It is a rebuild, not a copy of the theme's compiled CSS — that bundle is 721KB across 6,793
 * rules for pages that use a fraction of them. This is a few KB and self-hosts one 36KB font
 * subset, which matters because Core Web Vitals feed the ranking this port exists to protect.
 * Class names are our own and semantic; the rendered result is eta's shape.
 */

/* ---------- tokens (§1) ---------- */
:root {
  --c-primary: #4285f4;
  --c-primary-hover: #5699ff;
  --c-primary-light: rgba(66, 133, 244, .2);
  --c-primary-lighter: rgba(66, 133, 244, .12);
  --c-accent: #f93301;
  --c-accent-hover: #ff4715;
  /* Text on the accent is white, so the label reads as part of the button rather than as dark
     type sitting on top of it. White on #f93301 measures 4.06:1, which clears AA for the bold
     and large type this token is actually used on (the publish button, the "premium" pill, the
     listing badge) but not the 4.5:1 bar for small body text -- so it stays a token for
     accent-backed chrome and is not a general text colour. */
  --c-accent-ink: #fff;

  --g0: #0c0c0c; --g1: #1c1c1c; --g2: #2c2c2c; --g3: #3c3c3c; --g5: #5c5c5c;
  --g6: #6c6c6c; --g7: #7c7c7c; --g8: #8c8c8c; --g9: #9c9c9c; --g11: #bcbcbc;
  --g12: #dcdcdc; --g13: #ececec; --g14: #fcfcfc; --white: #fff;

  --ink: #222;
  --ink-strong: var(--g1);
  --muted: var(--g6);
  --muted-soft: var(--g8);
  --bg: #f2f3f3;
  --field: #f0f0f0;
  --footer-bg: #23292f;
  --footer-ink: #999;

  /* Separation is a hairline ring against a grey page, not a drop shadow. */
  --ring: 0 0 0 1px #d5dae0;
  --lift: 0 1px 3px rgba(0, 0, 0, .2);

  --r-tile: 8px;
  /* The fixed mobile bar's height. A token because three rules depend on it agreeing: the bar
     itself, the body's bottom padding that lets the page scroll clear of it, and the cookie
     notice that has to sit above it. Three hardcoded 56s is how one of them ends up a 54. */
  --bottom-nav-h: 56px;
  --r-media: 12px;
  --r-card: 16px;
  --r-pill: 100px;

  --wrap: 1200px;
  --gutter: 16px;
}

@font-face {
  font-family: "Noto Sans";
  src: url("../fonts/noto-sans-normal-latin.10f8972e3579.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+20A6, U+2122;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 15px/1.3 "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
a:hover { text-decoration: none; }
img { max-width: 100%; }
h1, h2, h3, h4 { margin: 0; }
/* Lists carry no marker by default. Almost every `ul` on this site is a layout group -- the
   listing grid, the nav, the footer columns, the price stat row -- and each of those used to
   reset `list-style` for itself, which is exactly how one gets missed: `.price-stats` never did,
   so the four headline figures on every price page rendered as a bulleted list. Prose is the
   exception and asks for markers explicitly (`.static-body ul`). */
ul, ol { margin: 0; padding: 0; list-style: none; }
.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 40px; padding: 0 20px; border: 0; border-radius: var(--r-pill); cursor: pointer;
  background: var(--c-primary); color: var(--white);
  /* Longhands, not the `font` shorthand: `font: 600 15px/1 inherit` is invalid CSS (the family
     slot cannot take a keyword) and the whole declaration is dropped, which silently leaves
     buttons at weight 400. */
  font-family: inherit; font-size: 15px; font-weight: 600; line-height: 1; white-space: nowrap;
  transition: background .2s;
}
.btn:hover { background: var(--c-primary-hover); }
.btn-accent { background: var(--c-accent); color: var(--c-accent-ink); }
.btn-accent:hover { background: var(--c-accent-hover); }
.btn-ghost { background: var(--white); color: var(--g2); box-shadow: var(--ring); }
.btn-ghost:hover { background: var(--g14); }
.btn-sm { height: 32px; padding: 0 14px; font-size: 14px; }

/* ---------- header (§2.1) ---------- */
.site-header { background: var(--white); position: sticky; top: 0; z-index: 40; box-shadow: 0 1px 0 var(--g13); }
.header-inner { display: flex; align-items: center; gap: 14px; min-height: 70px; }
.logo { display: inline-flex; align-items: baseline; font-size: 22px; font-weight: 800; letter-spacing: -.03em; color: var(--g2); white-space: nowrap; }
.logo span { color: var(--c-primary); }
/* Live renders its logo at 105x46, but that file carries a "Buy & Sell Cars" strapline under the
   wordmark; ours is the clean wordmark, whose letterforms are ~29px tall inside live's 46. Height
   is set here and width left auto so the intrinsic ratio decides -- the width/height attributes
   on the tag are for CLS, not for layout. */
.img-logo { display: block; height: 30px; width: auto; }

.search { position: relative; display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0; max-width: 600px; }
.search-field { position: relative; flex: 1; min-width: 0; display: flex; align-items: center; }
/* `appearance: none` plus our own caret, because a native select keeps the platform's control
   chrome -- a square grey dropdown button on Windows, a thin double arrow on macOS -- which is
   the one element in the row that did not look like it belonged to the pill next to it. The
   right padding reserves the caret's space so a long option label cannot run under it. */
.search select {
  height: 40px; max-width: 170px; border: 0; border-radius: var(--r-pill);
  background: var(--field) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%236c6c6c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 14px center;
  background-size: 10px 6px;
  appearance: none; -webkit-appearance: none;
  padding: 0 32px 0 14px; font: inherit; font-size: 14px; color: var(--g5); cursor: pointer;
}
.search input {
  width: 100%; height: 40px; border: 0; border-radius: var(--r-pill); background: var(--field);
  padding: 0 16px; font: inherit; font-size: 15px; color: var(--g2); outline: 0;
}
.search input::placeholder { color: var(--g8); }
.search input:focus { box-shadow: 0 0 0 2px var(--c-primary-light); }
.search .btn { height: 40px; padding: 0 20px; }
/* Same story as the hero: `combo.js` converts this select too, so the control that actually
   renders is the shared searchable dropdown and the rules above are the no-JS baseline. The
   trigger takes the row's grey pill rather than the generic white tile, so the header search
   stays three matching shapes. */
.search .cs-combo { flex: 0 0 170px; }
.search .cs-combo-trigger {
  height: 40px; padding: 0 32px 0 14px; background: var(--field); box-shadow: none;
  border-radius: var(--r-pill); font-size: 14px; color: var(--g5);
}
.search .cs-combo-trigger::after { top: 16px; }
.search .cs-combo-panel { min-width: 240px; }

/* The theme ships these links hidden at desktop; we render them (delta 3).

   `margin-left: auto` on **every** page, so the nav group and the account control sit together
   at the end of the row opposite the logo. Previously only the homepage did this, which left
   every other page with the nav packed against the search box and the right half of the row
   empty. The account control deliberately does *not* take an auto margin of its own at this
   width: two auto margins in one flex row share the free space between them, which would put
   the nav in the middle of the header rather than at its end. */
.header-links { display: flex; gap: 2px; margin-left: auto; }
/* `nowrap`: the nav items can shrink below their content width when the search form squeezes
   them, and "Sell your car" is the first label here long enough to break across two lines. */
.header-links a { display: flex; align-items: center; height: 32px; padding: 0 10px; border-radius: var(--r-tile); font-size: 14px; font-weight: 600; color: var(--g3); white-space: nowrap; }
.header-links a:hover { background: var(--field); }

/* The publish call to action. A sibling of `.header-links` rather than one of its children, so
   `.header-links a` cannot flatten it back into a nav item -- that rule sets its own height,
   radius and padding and would win on specificity over `.btn`.

   White on a white header, held by the same hairline ring every card and the Register button
   use, so it reads as a control without competing with the accent. The plus matches the raised
   Sell target in the fixed mobile bar. */
.nav-sell {
  display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
  height: 36px; padding: 0 16px; border-radius: var(--r-pill);
  background: var(--white); color: var(--g2); box-shadow: var(--ring);
  font-size: 14px; font-weight: 600; white-space: nowrap;
  transition: background .2s, box-shadow .2s, color .2s;
}
.nav-sell svg { width: 15px; height: 15px; color: var(--c-accent); }
/* Hover promotes it rather than merely tinting it: the ring takes the brand blue and the label
   follows, which is the one moment the control is allowed to claim more than its neighbours. */
.nav-sell:hover { background: var(--c-primary-lighter); box-shadow: 0 0 0 1px var(--c-primary); color: var(--c-primary); }
.nav-sell:hover svg { color: var(--c-primary); }
/* The phone-width copy of the same control, inside the account group. Hidden here and shown in
   the 760px block; the desktop one is hidden there. Two elements rather than one that moves,
   because the header is cached chrome and the server has no viewport to choose with. */
.nav-sell-compact { display: none; }

/* ---------- page shell ---------- */
.content { padding: 16px 0 40px; }
.content .wrap { display: flex; flex-direction: column; gap: 16px; }
.breadcrumbs { font-size: 12.5px; line-height: 15px; color: var(--muted); }
.breadcrumbs a { color: var(--g5); }
.breadcrumbs a:hover { color: var(--c-primary); }
.breadcrumbs span:last-child { color: var(--g2); font-weight: 600; }
.breadcrumbs span + span::before { content: "/"; margin: 0 6px; color: var(--g11); font-weight: 400; }

/* The one surface primitive: a white 16px card held by a hairline ring. */
.card { position: relative; width: 100%; background: var(--white); border-radius: var(--r-card); box-shadow: var(--ring); padding: 24px; }
.card > h2 { font-size: 24px; font-weight: 700; margin-bottom: 20px; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
.section-head h2 { font-size: 24px; font-weight: 700; }
.section-head a { font-size: 14px; font-weight: 600; color: var(--c-primary); white-space: nowrap; }
.page-title { font-size: 22px; line-height: 28px; font-weight: 700; }
.page-desc { font-size: 14px; color: var(--muted-soft); }
.result-count { font-size: 14px; color: var(--muted-soft); margin: 0; }
/* The de-thinning intro. Reads as body copy, not as a system message: it is there for a
   person deciding whether this page has what they want, and for the crawler second.

   It spans the card rather than sitting in a 70ch measure, which is what it used to do. The
   measure is the right call for an article, and the wrong one here: the breadcrumb, the H1 and
   the chip rows above and below all run the full width of the card, so a paragraph stopping
   halfway across read as a column of text pushed against the left edge rather than as part of
   the card. Two or three lines at this width stay comfortable to read; the cap only paid for
   itself on prose long enough to need it. */
.page-intro { font-size: 14px; line-height: 1.6; color: var(--muted); margin: 8px 0 0; }
.empty-state { color: var(--muted); text-align: center; padding: 28px 0; margin: 0; }
.empty-state a { color: var(--c-primary); font-weight: 600; }
.no-match-banner { background: var(--field); border-radius: var(--r-tile); padding: 12px 16px; margin: 0 0 16px; font-size: 14px; color: var(--g6); }

/* ---------- hero (§1.5) ---------- */
.hero { background: none; box-shadow: none; padding: 18px 0 6px; text-align: center; }
.hero h1 { font-size: 32px; line-height: 1.15; font-weight: 700; }
.hero-sub { font-size: 15.5px; color: var(--muted); margin: 10px 0 0; }

/* One bar, three segments.

   The old hero rendered the keyword field, the category select and the button as three separate
   pills in a centred row, each capped at 300px, which left the button floating in white space
   with no visual claim on the fields it submits. They are one control now: a white pill holding
   the three segments, with the hairline divider doing the separating that three boxes used to.

   `padding: 6px` is what insets the button so it sits *inside* the bar rather than butting
   against its edge, and it is why the inner heights are 44 while the bar is 56. */
.hero-search { max-width: 760px; margin: 22px auto 0; }
.hs-bar, .pill-bar {
  display: flex; align-items: center; padding: 6px;
  background: var(--white); border-radius: var(--r-pill);
  box-shadow: var(--ring), 0 2px 10px rgba(0, 0, 0, .05);
  transition: box-shadow .16s;
}
/* The ring is on the bar, not on the segment that has focus: a 2px outline around one third of
   a control that reads as one control is worse than no indicator, because it suggests the
   boundary is somewhere it is not. `:focus-within` is what makes that possible without JS. */
.hs-bar:focus-within, .pill-bar:focus-within { box-shadow: var(--ring), 0 0 0 3px var(--c-primary-light); }

.hs-q, .pill-q { display: flex; align-items: center; gap: 10px; flex: 1 1 auto; min-width: 0; padding-left: 16px; }
.hs-icon { flex: 0 0 auto; width: 18px; height: 18px; color: var(--g8); }
/* `min-width: 0` on both the flex item and the input: without it the input's default intrinsic
   width (a browser default of roughly 20 characters) is a floor the flex item cannot shrink
   below, and the bar overflows its container on a narrow viewport instead of the field giving
   way. The same reason `.search-field` carries it in the header. */
.hs-q input, .pill-q input {
  flex: 1 1 auto; min-width: 0; height: 44px; border: 0; background: none; outline: 0;
  padding: 0; font: inherit; font-size: 15.5px; color: var(--g1);
}
.hs-q input::placeholder, .pill-q input::placeholder { color: var(--g8); }
/* WebKit draws its own clear button inside a `type=search`, which lands on top of the divider. */
.hs-q input::-webkit-search-cancel-button, .pill-q input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

/* The divider is a border on the select's wrapper rather than a `::before` on the select, so it
   sits at the segment boundary and inherits the bar's inner padding as its inset. */
/* A fixed basis rather than shrink-to-fit: the segment holds either the native select or the
   combo trigger depending on whether the script landed, and the two size themselves differently
   -- so pinning the segment is what keeps the bar from changing shape as the page finishes
   loading. */
.hs-cat, .pill-pick { flex: 0 0 180px; position: relative; margin-left: 14px; }
.hs-cat::before, .pill-pick::before {
  content: ""; position: absolute; left: 0; top: 9px; bottom: 9px; width: 1px; background: var(--g12);
}
/* Same `appearance: none` and the same caret as the header select, so the two forms match. */
.hs-cat select, .pill-pick select {
  height: 44px; max-width: 180px; border: 0; background: none; outline: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%236c6c6c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; background-size: 10px 6px;
  appearance: none; -webkit-appearance: none;
  padding: 0 32px 0 16px; font: inherit; font-size: 14.5px; font-weight: 600; color: var(--g3);
  cursor: pointer; text-overflow: ellipsis;
}
/* The searchable dropdown *is* the category control here.

   `combo.js` is loaded site-wide by base.html and converts every `<select>` on the page, so what
   renders in this slot is a `.cs-combo`, not the native select above -- those rules are the
   no-JS baseline and nothing more. The generic trigger is a white tile with a ring of its own,
   which inside a white pill read as a second box drawn on top of the bar: the one control on the
   site that did not look like it was using the shared dropdown was the most-seen instance of it.
   These rules make it a *segment* of the bar -- no surface, no ring, the bar's own height -- so
   it matches the keyword field beside it and the same panel opens as everywhere else. */
.hs-cat .cs-combo-trigger, .pill-pick .cs-combo-trigger {
  height: 44px; padding: 0 34px 0 16px; background: none; box-shadow: none;
  border-radius: var(--r-pill); font-size: 14.5px; font-weight: 600; color: var(--g3);
}
.hs-cat .cs-combo-trigger::after, .pill-pick .cs-combo-trigger::after { top: 18px; border-color: var(--g7); }
/* The panel hangs off a 180px segment, which is narrower than its own option labels want. It is
   pinned to the segment's right edge and allowed to be wider than it, so it opens inside the
   bar's footprint instead of overhanging the card. */
.hs-cat .cs-combo-panel, .pill-pick .cs-combo-panel { left: auto; right: 0; min-width: 260px; top: calc(100% + 10px); }
.hs-go, .pill-go { flex: 0 0 auto; height: 44px; padding: 0 26px; margin-left: 8px; }
/* The magnifier shows only once the bar has stacked, where the button is the full width of the
   card and the word alone reads as a banner rather than as a control. */
.hs-go-icon, .pill-go-icon { display: none; width: 17px; height: 17px; }

/* The homepage already has the hero search, so the header carries a second copy of the same
   form directly above it. It is hidden until the hero has scrolled away, at which point the
   header is the only search on screen and needs to offer one.

   Scoped to `.route-home`, so every other page keeps its header search at all times. Gated on
   script: with JavaScript off the homepage shows the hero form only, which is the form it was
   going to use anyway. */
body.route-home .site-header .search { display: none; }
/* The search form is the only flexible item in the header row, so hiding it would leave the
   logo, the category nav and the account links packed against the left edge with the whole
   right half of the row empty. `.header-links` now carries `margin-left: auto` unconditionally
   (see its own rule above), so the homepage needs no special case and the one that used to be
   here is gone -- it would have been a second auto margin fighting the first. */
body.route-home .site-header.show-search .search {
  display: flex;
  animation: search-in .16s ease-out;
}
@keyframes search-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

/* ---------- horizontal rails (§2.5) ---------- */
.rail { display: flex; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin; }
.rail::-webkit-scrollbar { height: 6px; }
.rail::-webkit-scrollbar-thumb { background: var(--g12); border-radius: var(--r-pill); }

/* ---------- brand strip (§2.3) ---------- */
.strip-card { padding: 12px 16px; }
.brand-strip { gap: 4px; padding: 0 0 2px; }
.brand-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  flex: 0 0 auto; width: 96px; min-width: 96px; gap: 4px;
  padding: 12px 3px; border-radius: var(--r-tile); background: var(--white); text-align: center;
  transition: background .2s;
}
.brand-tile:hover { background: var(--field); }
.brand-icon {
  display: grid; place-items: center; width: 40px; height: 40px; border-radius: var(--r-tile);
  background: var(--c-primary-lighter); color: var(--c-primary); font-size: 16px; font-weight: 800;
  transition: transform .2s;
}
.brand-tile:hover .brand-icon { transform: scale(1.1); }
/* A logo tile is the mark on nothing, not the mark on the monogram's tinted square: half of
   these PNGs are the manufacturer's own badge on a transparent background and a blue wash
   behind it reads as a mistake. The box keeps its 40px height so a logo tile and a monogram
   tile sit on the same baseline in the same row. */
.brand-icon.has-logo { width: 52px; background: none; }
.brand-icon img { max-width: 100%; max-height: 100%; object-fit: contain; }
/* Fixed height with a 2-line clamp, so a long brand name cannot change the row height. */
.brand-name {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  height: 30px; align-content: center; font-size: 13px; line-height: 15px; color: var(--g2);
}
.brand-count { font-size: 12px; color: var(--muted-soft); }
.brand-tile.is-all .brand-icon { background: var(--c-primary); color: var(--white); }
.brand-strip .strip-sep { flex: 0 0 auto; width: 1px; margin: 12px 6px; background: var(--g13); }

/* ---------- listing card (§2.4) ---------- */
.listings { list-style: none; margin: 0; padding: 0; display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr)); }
.listings.rail { display: flex; gap: 12px; grid-template-columns: none; }
.listings.rail .listing { flex: 0 0 190px; width: 190px; }
.listing { position: relative; }
.listing-link { display: block; }
.thumb { position: relative; display: block; width: 100%; padding-top: 125%; background: var(--g13); border-radius: var(--r-media); overflow: hidden; }
.thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: var(--r-media); transition: transform .2s; }
.listing:hover .thumb img { transform: scale(1.03); }
/* The missing-photograph plate, wherever it lands: inside a `.thumb`, inside the item gallery,
   or swapped in over a broken `<img>` by `img-fallback.js`. `contain`, not `cover`, because the
   plate is 4:3 and the boxes are not -- cropping a wordmark looks like the bug it replaces. The
   `!important` is for the second case only: it has to beat the `object-fit: cover` that the
   element's *own* context sets, and at that point there is no selector to be more specific
   than, because the element is whatever image just failed. */
.is-placeholder { object-fit: contain !important; background: var(--field); padding: 8%; }
.thumb-empty { position: absolute; inset: 0; display: grid; place-items: center; color: var(--g9); font-size: 12.5px; }
.badge {
  position: absolute; left: 6px; top: 9px; z-index: 2;
  display: inline-flex; align-items: center; gap: 3px; height: 24px; padding: 4px 8px;
  border-radius: var(--r-pill); font-size: 12px; font-weight: 600; line-height: 16px;
  color: var(--c-accent-ink);
  background: linear-gradient(to bottom, #fa5c33 0%, var(--c-accent) 55%, #d42b01 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4), inset 0 -1px 0 rgba(0, 0, 0, .15), 0 1px 3px rgba(0, 0, 0, .2);
}
/* Opposite corner from Premium -- the two are independent facts about the same car and a
   Tokunbo listing is as likely to be Premium as any other, so they have to coexist rather than
   compete for one slot. */
.badge-condition {
  left: auto; right: 6px; color: var(--white); background: rgba(17, 24, 39, .72); box-shadow: none;
}
.thumb .tool {
  position: absolute; bottom: 0; left: 0; z-index: 2; display: flex; align-items: center;
  justify-content: space-between; width: 100%; padding: 6px 10px;
  border-radius: 0 0 var(--r-media) var(--r-media);
  background: linear-gradient(0deg, rgba(0, 0, 0, .75), transparent);
  color: var(--white); font-size: 12px; font-weight: 600;
}
.meta { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; padding: 10px 4px; }
.meta .title { font-size: 15px; font-weight: 600; line-height: 1.25; color: var(--ink-strong); }
.listing:hover .meta .title { color: var(--c-primary); }
.meta .cat { font-size: 14px; color: var(--muted); }
.meta .price { font-size: 18px; font-weight: 800; color: var(--c-primary); }
.meta .price.on-request { font-size: 17px; font-weight: 600; letter-spacing: -.4px; color: var(--muted); }
.meta .place { font-size: 13px; line-height: 16px; color: var(--muted-soft); }
/* The premium rail is deliberately leaner than the grid (§2.4). */
.listings.rail .meta { gap: 2px; }
.listings.rail .meta .title { font-size: 14px; font-weight: 500; }
.listings.rail .meta .cat, .listings.rail .meta .place { display: none; }

/* ---------- pagination ---------- */
.pagination { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px; }
.pagination .page { display: inline-flex; align-items: center; height: 36px; padding: 0 16px; background: var(--white); box-shadow: var(--ring); border-radius: var(--r-pill); font-size: 14px; font-weight: 600; }
.pagination .page:hover { color: var(--c-primary); box-shadow: var(--ring), 0 0 0 1px var(--c-primary); }
/* A numbered button is square-ish so a row of them reads as a sequence rather than as a row of
   words; the labelled ones (First / Previous / Next / Last) keep their wider pill. */
.pagination .page.num, .pagination .page.is-current { min-width: 36px; padding: 0 10px; justify-content: center; }
.pagination .page.is-current { background: var(--c-primary); color: var(--white); box-shadow: none; cursor: default; }
.pagination .page.is-current:hover { color: var(--white); box-shadow: none; }
/* A pager that shares a panel with the list it pages needs air above it: its pills were sitting
   on the outline of the last row tile, on the listing manager, the promote page and the public
   seller profile alike. `:not(:only-child)` is the whole rule rather than a list of page names
   -- the public grids wrap the pager in a panel of its own, where a top margin would push it
   off-centre, and that is exactly the case this excludes. The parent is deliberately not named:
   those three pages spell it `.card`, `.card` and `.box.ibox`. */
.pagination:not(:only-child) { margin-top: 20px; }
.page-status { margin: 12px 0 0; font-size: 13px; color: var(--muted-soft); text-align: center; }

/* ---------- facet chips (apps/routing/facets.py) ----------
   The clickable year / model / state / price buttons under a landing page's heading. Pill
   shapes rather than the ::before-arrow links of the SEO cloud below, because these are
   filters a visitor applies, not a directory of other pages.

   `.is-folded` is inert until the script adds `.is-collapsible` to the row. That ordering is
   the no-JavaScript contract: the server always renders every chip, and a page with scripts
   blocked shows all of them instead of hiding links behind a button that cannot open. */
/* A grid, not a stack. Each block is one short row of chips, so stacking them full-width left
   most of a 1168px card empty on the right while the blocks ran down the left edge. carspace
   lays its brand page out two-up for the same reason.

   `auto-fit` rather than a fixed column count, because the number of blocks varies by page type
   (one on a model page, three on a brand or state page) and empty tracks collapse: two blocks
   share the width, three take a third each, one still spans the card. `align-items: start` so a
   tall block does not stretch a short neighbour's heading away from its chips. */
.facets { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 20px 28px; align-items: start; }
.facet-heading { font-size: 15px; font-weight: 700; color: var(--ink-strong); margin-bottom: 10px; }
.facet-chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.chip {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 7px 13px; border-radius: var(--r-pill);
  font-size: 14px; font-weight: 600; color: var(--g2);
  background: var(--white); box-shadow: var(--ring);
  white-space: nowrap; transition: color .12s, box-shadow .12s;
}
.chip:hover { color: var(--c-primary); box-shadow: var(--ring), 0 0 0 1px var(--c-primary); }
.chip-count { font-size: 12px; font-weight: 500; color: var(--muted-soft); }
.chip:hover .chip-count { color: var(--c-primary); }
/* The facet this page already applies: marked, and deliberately not a link. */
.chip.is-current {
  color: var(--white); background: var(--c-primary); box-shadow: none; cursor: default;
}
.chip.is-current .chip-count { color: rgba(255, 255, 255, .75); }
.facet-more {
  padding: 7px 13px; border: 0; border-radius: var(--r-pill); cursor: pointer;
  font: inherit; font-size: 13.5px; font-weight: 600;
  background: var(--c-primary-lighter); color: var(--c-primary);
}
.facet-more:hover { background: var(--c-primary-light); }
.facet-chips.is-collapsible .is-folded { display: none; }
.facet-chips.is-collapsible.is-open .is-folded { display: inline-flex; }

/* ---------- SEO link blocks (§2.6) ---------- */
.link-block + .link-block { margin-top: 24px; }
.link-block h3 { font-size: 15px; font-weight: 700; margin-bottom: 8px; }
.link-cloud { display: flex; flex-wrap: wrap; gap: 8px; }
.link-cloud a {
  flex: 0 0 calc(25% - 10px); font-size: 14px; color: var(--g2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.link-cloud a::before { content: "\25B8"; color: var(--c-primary); margin-right: 5px; font-size: 12px; }
.link-cloud a:hover { color: var(--muted); }

/* ---------- publish CTA (§2.7) ---------- */
.cta-publish {
  padding: 0; overflow: hidden; color: var(--white);
  background:
    radial-gradient(circle at 15% 10%, rgba(255, 255, 255, .18) 0%, transparent 45%),
    radial-gradient(circle at 85% 90%, rgba(0, 0, 0, .18) 0%, transparent 55%),
    radial-gradient(circle at 50% 0%, rgba(255, 255, 255, .10) 0%, transparent 60%),
    linear-gradient(180deg, rgba(255, 255, 255, .05) 0%, rgba(0, 0, 0, .06) 100%),
    var(--c-primary);
}
.cta-publish .cta-body { width: 100%; max-width: 620px; padding: 24px; display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.cta-publish h2 { font-size: 24px; font-weight: 700; }
.cta-publish p { margin: 0; font-size: 15px; line-height: 1.5; }

/* ---------- item detail (DESIGN-SYSTEM.md §2.11) ----------
 * Structural names here are the live theme's (.ibox, #seller, .feat, .details/.elem,
 * .mlinks, .swiper-*) rather than our own, which is the one place this sheet departs from the
 * convention in its header. §2.11 records every figure below against those selectors, so
 * renaming them would detach the measurements from the thing they measure.
 */

/* The panel primitive. Same treatment as .card -- live calls it .ibox and uses it for every
   panel on the page, section or sidebar. */
.ibox {
  position: relative; width: 100%; background: var(--white);
  border-radius: var(--r-card); box-shadow: var(--ring); padding: 24px;
}
/* Inert inside the shell's .wrap, which already provides the max-width and gutter. Kept so the
   markup matches the measured structure. */
.container { width: 100%; }
/* Flexible main column against a fixed 360px side column, 16px gap. The flex lives on
   .data-box.main, which is the element that actually holds the two columns -- .container.primary
   wraps it. */
.data-box.main { display: flex; align-items: flex-start; gap: 16px; }
#item-main { display: flex; flex-direction: column; gap: 16px; flex: 1 1 auto; min-width: 0; }
/* A column of panels, not a stack of them. `#item-main` has had `gap: 16px` since the port and
   this did not, so every panel in the sidebar sat flush against the next -- invisible while
   there were two and obvious at three, once a sold listing added its own box between the seller
   and the contact form. The gap is the main column's, so the two columns line up. */
#item-side {
  flex: 0 0 360px; position: sticky; top: 86px;
  display: flex; flex-direction: column; gap: 16px;
}

#breadcrumbs { font-size: 12.5px; line-height: 15px; color: var(--muted); margin-bottom: -4px; }
#breadcrumbs .breadcrumb { display: flex; flex-wrap: wrap; gap: 2px; list-style: none; margin: 0; padding: 0; }
#breadcrumbs .breadcrumb li { display: inline-flex; gap: 4px; }
#breadcrumbs a { color: var(--g5); }
#breadcrumbs a:hover { color: var(--c-primary); }
#breadcrumbs .last-child span { color: var(--g2); font-weight: 600; }

/* ---- gallery ---- */
#item-image { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
/* Live ships this markup and then hides it outright (`.mlinks{display:none}`, top level in the
   theme bundle) -- which is also why its Share link has no handler bound. Kept in the DOM and
   hidden the same way, so the rendered page matches and re-enabling it is a one-line change. */
#item-image .mlinks { display: none; position: absolute; z-index: 3; gap: 6px; }
#item-image .mlinks.top { top: 26px; right: 26px; }
#item-image .mlinks.bottom { bottom: 112px; right: 26px; }
.mlink {
  display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px;
  border-radius: var(--r-pill); background: rgba(0, 0, 0, .62); color: var(--white);
  font-size: 12px; font-weight: 600; backdrop-filter: blur(2px);
}
.mlink:hover { background: rgba(0, 0, 0, .8); color: var(--white); }
.mlink .icon { flex: 0 0 auto; }
#plabel {
  position: absolute; left: 22px; top: 25px; z-index: 3;
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px;
  border-radius: var(--r-pill); background: var(--c-accent); color: var(--c-accent-ink);
  font-size: 12px; font-weight: 700;
}

.swiper-container { position: relative; border-radius: var(--r-media); overflow: hidden; background: var(--g13); }
.swiper-wrapper { display: flex; margin: 0; padding: 0; list-style: none; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; }
.swiper-wrapper::-webkit-scrollbar { display: none; }
.swiper-slide { flex: 0 0 100%; scroll-snap-align: center; aspect-ratio: 4 / 3; }
.swiper-slide > a, .swiper-slide > a > img { display: block; width: 100%; height: 100%; }
.swiper-slide > a > img { object-fit: cover; }
/* The plate carries its intrinsic 800x600 on the tag (for CLS), so the empty slide has to size
   it down to the slide rather than letting it set the gallery's height. */
.swiper-slide.empty { display: grid; place-items: center; }
.swiper-slide.empty .is-placeholder { width: 100%; height: 100%; }
.swiper-button {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  display: grid; place-items: center; width: 36px; height: 36px; cursor: pointer;
  border-radius: var(--r-pill); background: rgba(255, 255, 255, .92); color: var(--g2);
  box-shadow: var(--lift);
}
.swiper-button:hover { background: var(--white); }
.swiper-prev { left: 10px; }
.swiper-next { right: 10px; }
.swiper-prev .icon { transform: rotate(180deg); }
/* The slide counter and Maximize are one shared pill style on live, measured from the theme
   bundle: 34px tall, 100px radius, 70% black behind a half-pixel white hairline. The counter
   sits bottom-left and Maximize bottom-right -- putting them on the same corner buries one
   under the other. */
#item-image .swiper-pg,
#item-image a.maximize {
  position: absolute; bottom: 8px; left: 8px; z-index: 2;
  display: flex; align-items: center; gap: 4px; height: 34px; min-width: 36px;
  padding: 8px 12px; border-radius: var(--r-pill);
  background: rgb(0 0 0 / .7); border: .5px solid rgb(255 255 255 / .2);
  color: var(--white); font-size: 14px; font-weight: 600; line-height: 15px;
}
#item-image a.maximize { left: auto; right: 8px; }
#item-image .swiper-pg[hidden] { display: none; }
#item-image a.maximize:hover { color: var(--white); text-decoration: none; }
.swiper-thumbs ul { display: flex; gap: 8px; margin: 0; padding: 2px; list-style: none; overflow-x: auto; scrollbar-width: thin; }
.swiper-thumbs li { flex: 0 0 auto; cursor: pointer; }
.swiper-thumbs img {
  display: block; width: 76px; height: 60px; object-fit: cover;
  border-radius: var(--r-tile); opacity: .7; transition: opacity .2s;
}
.swiper-thumbs li:hover img, .swiper-thumbs li.active img { opacity: 1; box-shadow: 0 0 0 2px var(--c-primary); }

/* ---- basic + detail rows ---- */
/* h1 is 24px/500 in a mid grey -- lighter than the listing-page h1, which is the opposite of
   what you would guess, so it is measured rather than assumed. */
.basic h1 { font-size: 24px; font-weight: 500; line-height: 1.3; color: var(--g3); }
/* A TWO-COLUMN grid, not a list -- `repeat(2, 1fr)`, gap 16, collapsing to one column at 767px.
   The price row spans both columns and is the one row with a separator under it. */
.details { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; width: 100%; margin-top: 8px; }
.details > .elem { padding: 6px 0; background: #fff0; }
.details .elem { display: flex; flex-direction: row; gap: 10px; line-height: 1.2; align-items: flex-start; }
/* The row icons are primary blue, not grey -- measured, and it is what gives the block its
   colour. */
.details .elem > .icon { width: 24px; height: 24px; flex-shrink: 0; margin: 3px 0; color: var(--c-primary); }
.details .tx { display: flex; flex-direction: column; gap: 1px; align-items: flex-start; min-width: 0; }
.details .tx > span { font-size: 12px; font-weight: 500; color: var(--g7); }
.details .tx > strong { font-size: 15px; font-weight: 700; color: var(--g2); }
.details .tx a { font-size: 12px; font-weight: 500; margin: 6px 0 0; color: var(--c-primary); }
.details .elem.price {
  grid-column: 1 / -1;
  padding-bottom: 16px; margin-bottom: 2px; border-bottom: 2px solid #e8e8e8;
}
.details .elem.price .tx > span { font-size: 13px; }
.details .elem.price .tx > strong { font-size: 26px; font-weight: 800; color: var(--g0); letter-spacing: -.02em; }
.details .elem.price .tx > strong.on-request { font-size: 18px; font-weight: 600; color: var(--muted); letter-spacing: 0; }
.details .elem.price .tx a { font-size: 13px; }

/* ---- attributes ---- */
.props h2 { font-size: 20px; font-weight: 700; margin-bottom: 16px; }
.custom-fields { display: flex; flex-wrap: wrap; gap: 8px; }
/* Radius 0 is measured, not an oversight -- the chips are the one square thing on the page. */
.custom-fields .field { display: flex; gap: 6px; padding: 10px; background: #eef1f6; border-radius: 0; font-size: 14px; }
.custom-fields .name { font-weight: 700; color: var(--g2); }
.custom-fields .value { font-weight: 500; color: var(--g3); }

/* ---- description ---- */
.description h2 { font-size: 22px; font-weight: 700; margin-bottom: 8px; }
.description .ref { margin: 0 0 12px; font-size: 15px; color: var(--g7); }
.description .ds-text { font-size: 15px; color: var(--g3); line-height: 1.5; }
#shortcuts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.btn-white { background: var(--white); color: var(--g3); box-shadow: var(--ring); height: 36px; padding: 0 14px; font-size: 14px; border-radius: var(--r-tile); gap: 7px; }
.btn-white:hover { background: var(--g14); color: var(--g1); }

/* ---- about this seller / sidebar ---- */
/* .card is the page's panel primitive; inside #seller2 live uses it as a plain grouping, so the
   panel treatment is reset rather than nested. */
#seller2 > .card { background: none; border-radius: 0; box-shadow: none; padding: 0; }
#seller2 h2 { font-size: 20px; font-weight: 700; margin-bottom: 16px; }
#seller2 .user { display: flex; align-items: center; gap: 12px; }
.uimg, #seller .img img, .uimg-empty {
  flex: 0 0 auto; width: 56px; height: 56px; border-radius: var(--r-pill); object-fit: cover;
}
.uimg-empty {
  display: grid; place-items: center; background: var(--c-primary-lighter);
  color: var(--c-primary); font-size: 20px; font-weight: 800;
}
#seller2 .data, #seller .data { min-width: 0; }
#seller2 .name, #seller .name { display: block; font-size: 16px; font-weight: 700; color: var(--g2); }
#seller2 a.name:hover, #seller a.name:hover { color: var(--c-primary); }
#seller2 .location, #seller2 .address { font-size: 13px; color: var(--g7); }
#seller2 .phones { margin-top: 16px; }
/* Chips, not a list: each fact is its own soft grey-blue card, wrapping across the row. */
.feat { display: flex; flex-wrap: wrap; gap: 6px; margin: 16px 0 0; }
.feat > .elem {
  display: flex; align-items: center; gap: 6px; padding: 5px 12px;
  background: #e6eaf0; border-radius: 8px; font-size: 13px; font-weight: 500; color: var(--g3);
}
.feat > .elem .icon { width: 18px; height: 18px; flex-shrink: 0; }
.more-listings { display: inline-flex; gap: 6px; margin-top: 18px; font-size: 14px; font-weight: 600; color: var(--c-primary); }

#seller { display: flex; flex-direction: column; gap: 16px; }
#seller .line1 { display: flex; align-items: center; gap: 12px; }
#seller .status { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--g7); }
.circle { width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--g11); }
.circle.online { background: #2ea84f; }
#blabel {
  display: inline-block; margin-top: 4px; padding: 1px 8px; border-radius: var(--r-pill);
  background: var(--c-primary-lighter); color: var(--c-primary); font-size: 11px; font-weight: 700;
}
#seller .line2 { display: grid; gap: 10px; }
#seller .line2 > div { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--g5); }
#seller .line2 .icon { flex: 0 0 auto; color: var(--g8); }
/* Item-page actions are 48px at a 12px radius, not the pill the header uses. */
.line-contact { display: flex; flex-direction: column; gap: 8px; }
.line-contact .btn, .master-button { width: 100%; height: 48px; border-radius: var(--r-media); gap: 8px; }
.btn-secondary { background: var(--white); color: var(--g2); box-shadow: var(--ring); }
.btn-secondary:hover { background: var(--g14); color: var(--g1); }
.phone.masked { cursor: pointer; }
.phone .pshow { display: inline-flex; align-items: center; gap: 3px; margin-left: 4px; font-size: 12px; font-weight: 600; color: var(--c-primary); }
.phone.revealed .pshow { display: none; }
#seller2 .phones .phone {
  display: inline-flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 700;
  color: var(--g2);
}

/* ---- related rail ---- */
.rel-items h2 { font-size: 20px; font-weight: 700; margin-bottom: 16px; }

/* ---- share modal ---- */
#item-share-content[hidden] { display: none; }
.share-backdrop {
  position: fixed; inset: 0; z-index: 90; display: grid; place-items: center;
  background: rgba(0, 0, 0, .5); padding: 16px;
}
.item-share-box {
  width: 100%; max-width: 540px; background: var(--white); border-radius: var(--r-card);
  overflow: hidden; box-shadow: 0 10px 40px rgba(0, 0, 0, .3);
}
.item-share-box .head { padding: 16px 20px; border-bottom: 1px solid var(--g13); font-size: 16px; font-weight: 700; color: var(--g1); }
.item-share-box .wrap { padding: 20px; display: flex; flex-direction: column; gap: 20px; }
.item-share-box .object { display: flex; gap: 12px; }
.item-share-box .object img { width: 96px; height: 72px; object-fit: cover; border-radius: var(--r-tile); }
.item-share-box .object .data { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.item-share-box .object .data span { font-size: 13px; color: var(--g7); }
.item-share-box .object .data strong { font-size: 15px; font-weight: 700; color: var(--g2); }
.item-share-box .buttons { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.btn-primary-outline {
  height: 42px; border-radius: var(--r-tile); background: var(--white); color: var(--g2);
  box-shadow: var(--ring); gap: 8px; font-size: 14px;
}
.btn-primary-outline:hover { background: var(--g14); color: var(--g1); }
.btn-primary-outline.whatsapp:hover { color: #25d366; }
.btn-primary-outline.facebook:hover { color: #1877f2; }
.btn-primary-outline.twitter:hover { color: #0f1419; }
.btn-primary-outline.pinterest:hover { color: #e60023; }
/* `fit-content`, so the figure is the width of the photo rather than of the modal shell. The
   chevrons are positioned against this box, and without it they sat against the backdrop's
   edges with a strip of nothing between each one and the image it pages. */
.lightbox {
  position: relative; margin: 0 auto; width: fit-content; max-width: 100%;
  display: flex; flex-direction: column; gap: 8px; align-items: center;
}
/* The stage clips, the image scales inside it. Without the clip a 3.5x photo paints over the
   caption, the close button and the rest of the page -- `overflow: hidden` is what turns a
   scaled image into a window onto it. */
.lightbox-stage {
  position: relative; overflow: hidden; border-radius: var(--r-media); background: var(--g13);
  line-height: 0; touch-action: pan-y;
}
.lightbox img {
  max-width: min(92vw, 1100px); max-height: 82vh; cursor: zoom-in; display: block;
  transition: transform .18s ease-out;
}
/* No transition while zoomed: the origin moves with every pointer event, and animating that
   turns panning into a lag the cursor is always ahead of. */
.lightbox-stage.is-zoomed img { cursor: zoom-out; transition: none; }
/* Zoomed, the stage owns the gesture -- otherwise a drag across the photo on a phone scrolls
   the page behind the modal instead of panning. */
.lightbox-stage.is-zoomed { touch-action: none; }
.lightbox figcaption { font-size: 13px; font-weight: 600; color: var(--white); }

/* The two chevrons. Previously the only way to the next photo was to click the image, which is
   now the zoom gesture; these and the arrow keys replace it. */
.lightbox-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 44px; height: 44px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(0, 0, 0, .45); cursor: pointer;
}
.lightbox-nav:hover { background: rgba(0, 0, 0, .7); }
.lightbox-nav:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }
/* The glyph is a rotated border corner rather than an SVG: it is two lines of CSS against a
   file and a request, and this is the one place on the page that needs it. */
.lightbox-nav::before {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 11px; height: 11px; border-top: 2px solid var(--white); border-right: 2px solid var(--white);
}
.lightbox-nav.next { right: 10px; }
.lightbox-nav.next::before { transform: translate(-70%, -50%) rotate(45deg); }
.lightbox-nav.prev { left: 10px; }
.lightbox-nav.prev::before { transform: translate(-30%, -50%) rotate(-135deg); }

.share-close {
  position: absolute; top: 10px; right: 10px; width: 32px; height: 32px; border: 0;
  border-radius: var(--r-pill); background: var(--white); box-shadow: var(--lift);
  font-size: 18px; line-height: 1; color: var(--g3); cursor: pointer;
}

/* ---------- public seller profile (DESIGN-SYSTEM.md §2.12) ---------- */
#public-main { display: flex; flex-direction: column; gap: 16px; width: 100%; min-width: 0; }
#about { display: flex; align-items: flex-start; gap: 20px; }
#about .img-wrap { flex: 0 0 auto; }
#about .uimg, #about .uimg-empty { width: 96px; height: 96px; font-size: 34px; }
#about .wrap { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
#about h1 { font-size: 24px; font-weight: 700; color: var(--g1); }
#about .line1 { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; font-size: 13px; color: var(--g7); }
#about .line1 .elem { display: flex; align-items: center; gap: 6px; }
/* The live profile uses an <em> dot here rather than the item page's <div class="circle">. */
.onoff { width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--g11); }
.onoff.online { background: #2ea84f; }
#about .line2 .elem { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--g5); }
#about .line2 .icon { flex-shrink: 0; color: var(--g8); }
#about .buttons { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
#about .buttons .btn { height: 40px; border-radius: var(--r-tile); padding: 0 14px; font-size: 14px; gap: 7px; }
#about .bio { margin-top: 4px; font-size: 14px; line-height: 1.5; color: var(--g3); }
#public-main .section-head h2 { font-size: 20px; font-weight: 700; margin-bottom: 16px; }
#public-main .list-empty { font-size: 15px; color: var(--g7); }

/* ---------- footer (§2.9) ---------- */
.site-footer { background: var(--footer-bg); color: var(--footer-ink); margin-top: 24px; padding: 46px 0 0; font-size: 13px; }
/* Flex, not grid, for two reasons. `grid-template-columns: 1.6fr repeat(auto-fit, …)` is
   INVALID CSS -- an auto-fit/auto-fill repeat may not sit beside a flexible track -- so the whole
   declaration was dropped and the footer collapsed into one stacked column. And the column count
   is data-driven (the chrome context processor), so a hardcoded `repeat(6, …)` would break the
   next time a column is added. Flex wrap handles any count, and it is what the live footer uses. */
.footer-grid { display: flex; flex-wrap: wrap; gap: 28px 36px; padding-bottom: 36px; }
/* The brand block does not grow; the columns share what is left. Bases are set so the brand
   plus six columns plus gaps fit 1168px with slack -- a growing brand would push the last
   column onto its own row, where `flex-grow` then stretches it to full width. */
.footer-brand { flex: 0 0 240px; min-width: 0; }
.footer-col { flex: 1 1 110px; min-width: 0; }
.site-footer .logo { color: var(--white); font-size: 20px; }
.site-footer .logo span { color: var(--g9); }
/* The mark is the same file the header uses: blue and red on transparency, so it reads on the
   dark footer without a second asset or a filter that would wreck the two-colour logo. */
.footer-brand .img-logo { height: 34px; }
.footer-brand p { margin: 10px 0 0; max-width: 32ch; line-height: 1.5; }
.social { list-style: none; display: flex; flex-wrap: wrap; gap: 14px; padding: 0; margin: 16px 0 0; }
.footer-col h4 { font-size: 13.5px; font-weight: 700; color: var(--white); margin-bottom: 12px; }
.footer-col ul { list-style: none; padding: 0; margin: 0; }
.footer-col li { margin-bottom: 9px; }
.site-footer a { color: var(--footer-ink); }
.site-footer a:hover { color: var(--white); }
.footer-legal { padding: 18px var(--gutter); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08); }
.footer-legal p { margin: 0; font-size: 12.5px; color: var(--g7); }

@media (max-width: 1080px) {
  /* Below this the six columns no longer fit beside the brand, so the brand takes its own row
     and the columns wrap three-up rather than one of them stranding on a line of its own. */
  .footer-brand { flex: 1 1 100%; max-width: 46ch; }
  .footer-col { flex: 1 1 140px; }
}
@media (max-width: 1000px) {
  .data-box.main { flex-direction: column; }
  .details { grid-template-columns: 1fr; }
  #about { flex-direction: column; gap: 12px; }
  .feat > .elem { flex-grow: 1; }
  #item-side { flex: 1 1 auto; position: static; width: 100%; }
  .link-cloud a { flex: 0 0 calc(50% - 10px); }
}
@media (max-width: 760px) {
  .card { padding: 16px; }
  .strip-card { padding: 8px 8px 2px; }
  .header-inner { flex-wrap: wrap; gap: 10px; padding-top: 10px; padding-bottom: 10px; }
  .search { order: 3; flex-basis: 100%; max-width: none; }
  /* Both spellings, and that is the bug this line fixes rather than a precaution: `combo.js`
     moves the native select out of the layout (`.cs-combo-native`) and renders a wrapper in its
     place, so hiding the `select` alone hid nothing anyone could see and the category control
     stayed in the header on every phone -- the row it was meant to make room for. */
  .search select, .search .cs-combo { display: none; }
  /* The category nav is hidden at this width, so the account control is the last item in row
     one and takes the auto margin itself -- which is what puts it at the opposite end from the
     logo instead of against it. The search form wraps to row two (`order: 3` above). */
  .header-links { display: none; }
  /* The nav's own publish button goes with the links it sat beside -- `> .nav-sell`, so this
     hides that one and not the compact copy inside the account group below. */
  .header-inner > .nav-sell { display: none; }
  /* Signed out, the second control beside Sign in is **Sell**, not Register.
     "Register" names a chore and promises nothing. The visitor worth something to this site is
     the one with a car to sell, and at 390px there is room for one button: make it the action
     rather than the paperwork, and let the account be something they meet on the way. The
     account path is not lost -- /sell-car is login-gated, so the tap lands on the sign-in form
     with its own "create an account to start selling" offer. */
  .nav-sell-compact { display: inline-flex; height: 32px; padding: 0 13px; }
  .nav-register { display: none; }
  /* `.site-header` is in the selector to outrank the base `.header-account` rule, which sits
     *later* in this file: at equal specificity source order wins over a media query, so a
     `margin-left` added to that rule later would silently undo this and put the account control
     back against the logo on every phone. */
  .site-header .header-account { margin-left: auto; }
  .hero h1 { font-size: 24px; }
  .hero-sub { font-size: 14px; margin-top: 8px; }
  /* Stacked, because three segments side by side on a 360px screen leaves the keyword field --
     the one people actually type in -- about 90px wide. The bar stays one card; its segments
     become three full-width rows inside it, each with its own grey field background so the tap
     target is visible rather than implied by a divider that no longer separates anything. */
  .hero-search { margin-top: 16px; }
  .hs-bar, .pill-bar { flex-direction: column; align-items: stretch; gap: 8px; border-radius: 20px; padding: 8px; }
  .hs-q, .hs-cat, .pill-q, .pill-pick { background: var(--field); border-radius: var(--r-pill); }
  .hs-q, .pill-q { padding: 0 14px; }
  .hs-cat, .pill-pick { flex: 0 0 auto; margin-left: 0; }
  .hs-cat::before, .pill-pick::before { display: none; }
  .hs-cat select, .pill-pick select { width: 100%; max-width: none; background-position: right 16px center; }
  .hs-cat .cs-combo-trigger, .pill-pick .cs-combo-trigger { padding-left: 18px; }
  /* Stacked, the segment is the full width of the card, so the panel is too -- no need for the
     right-aligned overhang the desktop 180px segment wants. */
  .hs-cat .cs-combo-panel, .pill-pick .cs-combo-panel { left: 0; right: 0; min-width: 0; }
  .hs-go, .pill-go { width: 100%; margin-left: 0; }
  .hs-go-icon, .pill-go-icon { display: block; }
  .brand-tile { width: 84px; min-width: 84px; padding: 6px 1px; }
  .brand-name { font-size: 12.5px; }
  .listings { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
  .item-title { font-size: 20px; }
  .details .elem.price .tx > strong { font-size: 22px; }
  .item-share-box .buttons { grid-template-columns: 1fr; }
  #item-image .mlinks.top { top: 22px; right: 22px; }
  #item-image .mlinks.bottom { bottom: 104px; right: 22px; }
  .cta-publish h2 { font-size: 22px; }
  .page-title { font-size: 20px; line-height: 26px; }
  .facets { grid-template-columns: 1fr; gap: 18px; }
  .pagination { gap: 6px; }
  .pagination .page { padding: 0 12px; font-size: 13.5px; }
  .pagination .page.edge { display: none; }
  .chip { padding: 6px 11px; font-size: 13.5px; }
  .facet-chips { gap: 6px; }
}

/* --- auth pages (sign in, register, recover) ---------------------------------------------
   A single narrow card rather than the two-column shell: these pages have one job, and the
   measured design's card, radius and ring tokens carry the look without new primitives. */
.auth-card { max-width: 480px; margin: 0 auto; }
.auth-form { display: flex; flex-direction: column; gap: 6px; }
.auth-form label { font-size: 14px; font-weight: 600; color: var(--g2); margin-top: 10px; }
.auth-form input[type="email"],
.auth-form input[type="text"],
.auth-form input[type="tel"],
.auth-form input[type="number"],
.auth-form input[type="search"],
.auth-form input[type="url"],
.auth-form input[type="password"] {
  height: 44px; padding: 0 12px; font-size: 15px; color: var(--g1);
  background: var(--white); border: 0; border-radius: var(--r-tile); box-shadow: var(--ring);
}
.auth-form input:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 1px; }
.auth-form .btn { margin-top: 20px; width: 100%; }
.auth-check { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.auth-check input { width: 16px; height: 16px; }
.auth-check label { margin-top: 0; font-weight: 400; }

/* The "Show"/"Hide" control password-reveal.js injects beside every password field.
   Scoped to .auth-form because that is where all eleven of them live -- sign in, register,
   recover, and the four settings forms that re-ask for the current password.

   The descendant selector is not an accident: it has to outweigh the input rules above, which
   set `padding: 0 12px` on both [type="password"] and [type="text"], and a revealed field is a
   text input. Same specificity, later in the file -- so this block must stay below them. */
.pw-field { position: relative; display: block; }
.auth-form .pw-field input { width: 100%; padding-right: 68px; }
.auth-form .pw-field .pw-reveal {
  position: absolute; top: 0; right: 0; height: 44px; padding: 0 12px;
  font-size: 13px; font-weight: 600; color: var(--c-primary);
  background: none; border: 0; border-radius: var(--r-tile); cursor: pointer;
}
.auth-form .pw-field .pw-reveal:hover { color: var(--c-primary-hover); }
.auth-form .pw-field .pw-reveal:focus-visible {
  outline: 2px solid var(--c-primary); outline-offset: -2px;
}
.auth-error {
  margin-bottom: 16px; padding: 12px 14px; border-radius: var(--r-tile); font-size: 14px;
  color: var(--g1); background: rgba(249, 51, 1, .12);
}
.field-error { font-size: 13px; color: var(--c-accent); }
.auth-alt { margin-top: 20px; font-size: 14px; color: var(--g6); text-align: center; }
.auth-alt a { color: var(--c-primary); font-weight: 600; }
/* Google's own pill (`.g_id_signin`) renders flush against whatever sits next to it in the
   DOM -- the markup has no margin of its own, on either side, so the gap has to come from the
   wrapper the button sits inside. */
.google-signin { margin: 20px 0; }
/* Hidden from the screen and *not* from assistive technology: a control that reads its name
   from a `<label>` can keep the label without the layout having to show it. Not
   `display: none`, which takes the element out of the accessibility tree along with the name,
   and not `.xact`'s off-screen box below, which is a decoy that must stay focusable to bots.
   One rule, so this stops being re-invented per page. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* The honeypot block. Off-screen rather than display:none, because some bots skip anything
   hidden that way -- and a decoy a bot skips is a decoy that catches nothing. */
.xact {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}
/* No auto margin here: `.header-links` carries it and pushes this along with it, so the two
   travel as one group at the end of the row. On a phone the nav is hidden, so the account
   control takes the auto margin itself -- see the <=760px block. */
.header-account { display: flex; align-items: center; gap: 12px; font-size: 14px; font-weight: 600; }
.header-account > a { color: var(--g2); }
.header-account > a:hover { color: var(--c-primary); }
/* The account dropdown. A <details>, so it opens with no script and the links inside are real
   links -- account-menu.js only adds closing on an outside click and on Escape. */
.acct-menu { position: relative; }
.acct-menu > summary {
  display: flex; align-items: center; gap: 7px; height: 34px; padding: 0 10px;
  border-radius: var(--r-pill); color: var(--g2); cursor: pointer; list-style: none;
  white-space: nowrap;
}
.acct-menu > summary::-webkit-details-marker { display: none; }
.acct-menu > summary:hover { background: var(--field); }
.acct-menu > summary:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 1px; }
/* The chevron, drawn rather than shipped, and rotated when open -- the same construction the
   listing form's sections use, so the two read as one idea. */
.acct-menu > summary::after {
  content: ""; width: 6px; height: 6px;
  border-right: 2px solid var(--g6); border-bottom: 2px solid var(--g6);
  transform: rotate(45deg) translateY(-1px); transition: transform .15s ease;
}
.acct-menu[open] > summary::after { transform: rotate(-135deg) translateY(-1px); }
.acct-menu[open] > summary { background: var(--field); }
/* Truncated rather than wrapped: a long business name would otherwise push the header to two
   rows on a phone, which is the reflow the logo's intrinsic size exists to prevent. */
.acct-name { overflow: hidden; text-overflow: ellipsis; max-width: 11ch; }
.acct-panel {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60; min-width: 196px;
  display: flex; flex-direction: column; padding: 6px;
  background: var(--white); border-radius: var(--r-tile);
  box-shadow: var(--ring), 0 10px 28px rgba(0, 0, 0, .14);
}
.acct-panel a, .acct-panel .btn-link {
  display: flex; align-items: center; height: 38px; padding: 0 10px;
  border-radius: var(--r-tile); font-size: 14px; font-weight: 600; color: var(--g2);
  text-align: left;
}
.acct-panel a:hover, .acct-panel .btn-link:hover { background: var(--field); color: var(--c-primary); }
/* Sign out is the destructive one and is separated from the navigation above it. */
.acct-panel .logout-form { display: block; margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--g13); }
.acct-panel .btn-link { width: 100%; }
@media (max-width: 760px) {
  /* Edge to edge of the viewport's gutter on a phone, where a 196px panel pinned to a narrow
     control leaves the links in a column barely wider than a thumb. */
  .acct-panel { min-width: 210px; }
  .acct-name { max-width: 8ch; }
}
.logout-form { display: inline; }
.btn-link {
  background: none; border: 0; padding: 0; font: inherit; color: var(--g2); cursor: pointer;
}
.btn-link:hover { color: var(--c-primary); }

/* --- dealer directory and company profile ------------------------------------------------
   Component names are the live plugin's (bpr-*), measured from var/capture/companies.html and
   from /company/nigerian-cars. The cover band with an icon overlapping its lower edge is the
   shape of both pages, so the two share these rules. */
.bpr-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
.bpr-comp {
  display: block; background: var(--white); border-radius: var(--r-card);
  box-shadow: var(--ring); overflow: hidden; color: inherit;
}
.bpr-comp:hover { box-shadow: 0 0 0 1px var(--c-primary); }
.bpr-cover { position: relative; }
.bpr-cover .bpr-wrap, .bpr-wrap-img { height: 110px; }
.bpr-wrap-img { background-size: cover; background-position: center; background-color: var(--g13); }
/* The live card's gradient scrim, so white text over a photo stays readable. */
.bpr-elip {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0) 40%, rgba(0,0,0,.35) 100%);
}
.bpr-icon-wrap { margin: -32px 0 0 16px; }
.bpr-icon {
  width: 64px; height: 64px; border-radius: var(--r-tile); overflow: hidden;
  background: var(--white); box-shadow: var(--ring);
  display: flex; align-items: center; justify-content: center;
}
.bpr-icon img { width: 100%; height: 100%; object-fit: cover; }
.bpr-initials { font-size: 24px; font-weight: 700; color: var(--g7); }
.bpr-info { padding: 10px 16px 16px; }
.bpr-title { font-size: 16px; font-weight: 700; color: var(--g1); }
.bpr-loc { margin-top: 2px; font-size: 13px; color: var(--g6); }
.bpr-labels { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.bpr-user-type {
  padding: 2px 8px; border-radius: var(--r-pill); font-size: 12px; font-weight: 600;
  background: var(--c-primary-lighter); color: var(--c-primary);
}
.bpr-count { font-size: 13px; color: var(--g6); }

/* the profile */
.bpr-body { padding: 0; overflow: hidden; }
.bpr-body .bpr-cover .bpr-wrap, .bpr-body .bpr-wrap-img { height: 200px; }
.bpr-contact-us { position: absolute; right: 20px; bottom: 20px; }
.bpr-profile { display: flex; gap: 24px; padding: 0 24px 24px; flex-wrap: wrap; }
.bpr-left { display: flex; gap: 16px; flex: 1 1 420px; margin-top: -36px; }
.bpr-left .bpr-icon { width: 100px; height: 100px; flex: 0 0 100px; }
.bpr-box { padding-top: 44px; }
.bpr-box h1 { font-size: 28px; font-weight: 700; color: var(--g0); }
.bpr-badges { display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.bpr-verified {
  padding: 2px 8px; border-radius: var(--r-pill); font-size: 12px; font-weight: 600;
  background: rgba(52, 168, 83, .14); color: #1e7e34;
}
.bpr-reg { margin-top: 8px; font-size: 14px; color: var(--g6); }
.bpr-about { margin-top: 12px; font-size: 15px; color: var(--g3); max-width: 60ch; }
.bpr-right { flex: 0 1 300px; padding-top: 24px; display: flex; flex-direction: column; gap: 12px; }
.bpr-phone, .bpr-location, .bpr-links { display: flex; gap: 10px; font-size: 14px; }
.bpr-right-box a { color: var(--c-primary); font-weight: 600; }
.bpr-comp-all { font-weight: 600; }
.bpr-hours { display: grid; grid-template-columns: max-content 1fr; gap: 6px 20px; font-size: 15px; }
.bpr-hours dt { font-weight: 600; color: var(--g2); }
.bpr-hours dd { color: var(--g5); }
.bpr-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; }
.bpr-gallery img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--r-media); }
.bpr-social { display: flex; gap: 12px; flex-wrap: wrap; }
.bpr-social a { color: var(--c-primary); font-weight: 600; }
.bpr-legal { font-size: 14px; color: var(--g5); max-width: 70ch; }
.bpr-top-search { margin-top: 16px; }
.bpr-search-left { display: flex; gap: 8px; max-width: 420px; }
.bpr-search-left input {
  flex: 1; height: 40px; padding: 0 12px; font-size: 15px; border: 0;
  border-radius: var(--r-tile); box-shadow: var(--ring);
}

@media (max-width: 720px) {
  .bpr-profile { padding: 0 16px 16px; }
  .bpr-left { flex: 1 1 100%; }
  .bpr-box { padding-top: 16px; }
  .bpr-box h1 { font-size: 22px; }
  .bpr-contact-us { position: static; padding: 16px 16px 0; }
}

/* --- price pages -------------------------------------------------------------------------
   /pricing, /pricing/<brand>, /pricing/<brand>/<model> and /car-price-checker. The live pages
   are the newest and best SEO work on the site; these are the same components rebuilt on our
   own tokens. */
.price-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(170px, 100%), 1fr)); gap: 12px; }
.price-stats li {
  padding: 14px 16px; border-radius: var(--r-tile); background: var(--g14); box-shadow: var(--ring);
}
.price-stats strong { display: block; font-size: 20px; font-weight: 700; color: var(--g0); }
.price-stats span { font-size: 13px; color: var(--g6); }
.price-basis { margin-top: 10px; font-size: 13px; max-width: 80ch; }
/* "How to read these figures": one lead sentence, then the three caveats as cards across the
   width. As a single capped paragraph this was the same lopsided block as above, and the
   caveats -- asking prices, unpriced listings, thin rows -- are the part a buyer most needs to
   see, so they are given their own shape rather than buried mid-sentence. */
.price-method { font-size: 16px; line-height: 1.55; color: var(--g3); margin-bottom: 16px; max-width: 80ch; }
.price-caveats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: 12px; }
.price-caveats > div {
  padding: 14px 16px; border-radius: var(--r-tile); background: var(--g14); box-shadow: var(--ring);
}
.price-caveats dt { font-size: 14px; font-weight: 700; color: var(--g1); margin-bottom: 4px; }
.price-caveats dd { margin: 0; font-size: 14px; line-height: 1.5; color: var(--g5); }
.price-bands { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
.price-bands a {
  display: block; padding: 14px 16px; border-radius: var(--r-tile); box-shadow: var(--ring);
  color: var(--g1);
}
.price-bands a:hover { box-shadow: 0 0 0 1px var(--c-primary); color: var(--c-primary); }
.price-bands strong { display: block; font-size: 16px; }
.price-bands span { font-size: 13px; color: var(--g6); }
/* Six columns of figures do not fit a phone, and the honest answer for a data table is to let
   it scroll rather than to reflow it into something that is no longer a table. The wrapper is
   focusable so the scroll is reachable from a keyboard, which an `overflow` box otherwise is
   not. */
.price-table-scroll { overflow-x: auto; }
.price-table-scroll:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }
.price-table { width: 100%; border-collapse: collapse; font-size: 15px; }
.price-table th, .price-table td { padding: 10px 12px; text-align: left; }
.price-table thead th {
  font-size: 13px; font-weight: 600; color: var(--g6); border-bottom: 1px solid var(--g12);
  white-space: nowrap;
}
.price-table tbody tr + tr th, .price-table tbody tr + tr td { border-top: 1px solid var(--g13); }
.price-table tbody th { font-weight: 600; color: var(--g1); }
.price-table a { color: var(--c-primary); font-weight: 600; }
.price-table .too-few { font-size: 13px; }
/* Two columns where there is room: eight question-and-answer pairs in a single 644px column
   inside a 1168px card is the lopsided block again, and a FAQ is the one part of the page a
   buyer scans rather than reads, so columns suit it. Each pair is wrapped in a `div` -- valid
   inside a `dl` -- so a question can never be split from its answer across the gap. */
/* `min(420px, 100%)` rather than a bare 420px: `minmax(420px, 1fr)` holds a track at 420px even
   when the container is narrower, which pushes a 358px-wide phone into horizontal page scroll.
   The `min()` lets the track shrink to the container and the grid falls to one column. */
.price-faq {
  display: grid; gap: 22px 40px;
  grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
}
.price-faq dt { font-weight: 700; color: var(--g1); }
.price-faq dd { margin: 6px 0 0; color: var(--g3); line-height: 1.55; }
.price-siblings { display: flex; flex-wrap: wrap; gap: 10px; }
.price-siblings li { display: flex; gap: 6px; align-items: baseline; }
.price-siblings a { color: var(--c-primary); font-weight: 600; }
.price-crumb { margin-top: 12px; font-size: 14px; }
.price-crumb a { color: var(--c-primary); font-weight: 600; }
/* The page-level heading. `.sec-title` is used by 15 templates -- the dashboard, the listing
   manager, the account forms, the static pages, the error page -- and until now **had no rule at
   all**. With the reset's `h1, h2, h3, h4 { margin: 0 }` above, every one of those pages rendered
   its title at the browser's default size with nothing under it, so the next element sat flush
   against the heading. That is what "the button for posting a new ad is too close to the text"
   was on the dashboard, and the same gap made every account form look unfinished.

   22px/28px is the measured search-page `h1` (DESIGN-SYSTEM.md §1), which is the closest
   analogue: a page-level heading inside a card. It makes a consistent step above `.sec-sub`
   below, which is the section heading at 18px. */
.sec-title {
  font-size: 22px; line-height: 28px; font-weight: 700; color: var(--g2); margin-bottom: 16px;
}
/* A heading that is the last thing in its card needs no trailing gap -- the card's own padding
   is the spacing. Without this the "Sell a car" card carried 16px of dead space under its
   button. */
.sec-title:last-child { margin-bottom: 0; }
/* `.card > h2` is 24px and this is the deliberate 18px, so the class has to outrank it: at
   equal weight (0,1,1 against 0,1,0) the descendant rule won, and every section heading in the
   account area rendered 24px -- *larger than the page's own h1 at 22px*. A page whose sections
   shout louder than its title has no hierarchy, which is most of why these forms read as a
   pile. Measured in the browser, not guessed: `getComputedStyle` on /user/business-profile. */
.sec-sub, .card > h2.sec-sub {
  font-size: 18px; font-weight: 700; color: var(--g2); margin-bottom: 12px;
}
.checker-form select {
  height: 44px; padding: 0 12px; font-size: 15px; border: 0; border-radius: var(--r-tile);
  box-shadow: var(--ring); background: var(--white);
}
@media (max-width: 720px) {
  /* A six-column price table does not fit a phone; scroll it rather than crush it. */
  .price-table { display: block; overflow-x: auto; white-space: nowrap; }
}

/* --- contact and static pages ------------------------------------------------------------ */
.auth-form textarea {
  padding: 10px 12px; font: inherit; font-size: 15px; color: var(--g1); background: var(--white);
  border: 0; border-radius: var(--r-tile); box-shadow: var(--ring); resize: vertical;
}
.auth-form textarea:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 1px; }
.contact-notice { margin-top: 20px; font-size: 13px; }
/* The reading-measure rule, in one place, because getting it wrong is what produced the
   "text is one-sided" report on the legal, static and price pages alike.

   A capped text column inside a card is fine **when something else in that card fills the
   width** -- a stat row, a table, a grid of cards. The eye then reads "intro, then content",
   and the space beside the intro is obviously intentional.

   It is lopsided when the capped paragraph is the *only* thing in the card. That was
   `.static-body`: 75ch of prose, 644px wide, sitting flush left in an 1168px card with its own
   heading running the full width above it and 45% of the card blank to its right.

   So: prose-only cards cap the **card** (`.card.prose`), and everything inside shares one left
   and right edge. Cards that mix prose with full-width content cap the prose and leave the card
   alone. Nothing caps a paragraph and then leaves it alone in a wide box. */
.card.prose { max-width: 820px; margin-inline: auto; }
.static-body { font-size: 16px; color: var(--g3); }
.static-body h2 { font-size: 20px; font-weight: 700; color: var(--g1); margin: 24px 0 8px; }
.static-body p { margin-bottom: 14px; }
.static-body ul { margin: 0 0 14px 20px; list-style: disc; }
.static-body a { color: var(--c-primary); font-weight: 600; }

/* --- cookie notice ----------------------------------------------------------------------- */
/* Fixed, so appearing costs no layout shift -- a banner that pushes the page down is a Core Web
   Vitals CLS hit on first view of every page, and this port exists to protect a ranking those
   feed into. Bottom-right on a desktop, where it covers nothing; a full-width bar across the
   bottom on a phone, because a floating card at that width covers the content instead. */
.cookie-notice {
  position: fixed; right: 16px; bottom: 16px; z-index: 60; width: min(380px, calc(100vw - 32px));
  display: flex; flex-direction: column; align-items: flex-start; gap: 12px;
  padding: 16px; background: var(--white); border-radius: var(--r-card);
  box-shadow: 0 6px 24px rgba(0, 0, 0, .18), var(--ring);
}
.cookie-notice[hidden] { display: none; }
.cookie-notice-text { margin: 0; font-size: 14px; line-height: 1.5; color: var(--g3); }
.cookie-notice-text a { color: var(--c-primary); font-weight: 600; }
@media (max-width: 760px) {
  .cookie-notice {
    /* Above the fixed bar, not under it. Both are `position: fixed` at the bottom of a phone
       screen, and the notice is the one that has to be dismissed before the bar is usable --
       so it sits on top of it and is offset by exactly the bar's height. */
    right: 0; bottom: var(--bottom-nav-h); width: 100%;
    border-radius: var(--r-card) var(--r-card) 0 0;
    flex-direction: row; align-items: center; gap: 14px;
    z-index: 71;
  }
  .cookie-notice .btn { flex-shrink: 0; }
}

/* --- the fixed mobile bar (DESIGN-SYSTEM.md §2.10) ----------------------------------------
 *
 * Five targets with the publish call to action raised in the middle, which is the shape §2.10
 * measured on the live site. Desktop never shows it: it has a header nav with room for the same
 * links, and a bar bolted to the bottom of a 1400px window is a phone pattern on a desktop.
 *
 * It exists for the dealer who is not a digital native. The three things they do -- check their
 * listings, answer a buyer, post a car -- are thumb-reachable from every page, each with a word
 * under the icon. There is no menu to discover and nothing to learn.
 */
.bottom-nav { display: none; }
@media (max-width: 760px) {
  .bottom-nav {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    /* The iPhone home-bar strip. Without this the labels sit under the gesture area and the
       whole bar reads as cut off, which is the single most common way this pattern looks
       broken on real hardware. */
    padding-bottom: env(safe-area-inset-bottom, 0);
    background: var(--white); border-top: 1px solid var(--g12);
    box-shadow: 0 -2px 10px rgba(0, 0, 0, .06);
  }
  .bottom-nav-item {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    /* 56px: comfortably past the 44px minimum touch target, with the label inside it rather
       than spilling below. */
    min-height: 56px; padding: 6px 2px 8px; color: var(--g5); text-align: center;
  }
  .bottom-nav-icon { display: block; width: 23px; height: 23px; }
  .bottom-nav-icon svg { display: block; width: 100%; height: 100%; }
  /* 11px with a tight line-height: a label, not body copy. Every target is named, because an
     icon on its own is a guess for exactly the user this bar is for. */
  .bottom-nav-label { font-size: 11px; font-weight: 600; line-height: 1.1; letter-spacing: -.01em; }
  .bottom-nav-item.is-current { color: var(--c-primary); }
  /* The raised Sell button: the one control on the screen that cannot be mistaken for anything
     else, which is the point of raising it. */
  .bottom-nav-item.is-raised { color: var(--g5); }
  .bottom-nav-item.is-raised .bottom-nav-icon {
    display: grid; place-items: center; width: 44px; height: 44px; margin-top: -14px;
    color: var(--white); background: var(--c-accent); border-radius: 50%;
    box-shadow: 0 4px 12px rgba(249, 51, 1, .34);
  }
  .bottom-nav-item.is-raised .bottom-nav-icon svg { width: 22px; height: 22px; }
  .bottom-nav-item.is-raised.is-current { color: var(--c-accent); }
  .bottom-nav-item:focus-visible { outline: 2px solid var(--c-primary); outline-offset: -3px; }
  /* The page has to be able to scroll clear of the bar, or the footer's last row sits under it
     permanently and looks like a rendering bug. */
  body { padding-bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0)); }
}

/* --- the legal pages -------------------------------------------------------------------- */
/* A policy is read to answer one question, so the prose is set for scanning: numbered headings
   that hold their own line, list items with room between them, and `strong` carrying the claim
   so the eye finds it without reading the paragraph. */
.legal-effective { font-size: 14px; color: var(--g5); margin-bottom: 14px; }
.legal-summary {
  margin-bottom: 24px; padding: 14px 16px; font-size: 15px; line-height: 1.55;
  color: var(--g3); background: var(--field); border-radius: var(--r-tile);
}
.legal-body h2 { scroll-margin-top: 86px; margin-top: 32px; }
.legal-body h2:first-child { margin-top: 0; }
.legal-body li { margin-bottom: 8px; }
.legal-body strong { color: var(--g1); font-weight: 700; }
.legal-body code {
  padding: 1px 5px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 14px;
  background: var(--field); border-radius: 4px;
}
.legal-crosslink { margin-top: 24px; font-size: 15px; }

/* --- publish form ------------------------------------------------------------------------ */
.sell-card { max-width: 640px; }
.auth-form select {
  height: 44px; padding: 0 12px; font-size: 15px; border: 0; border-radius: var(--r-tile);
  box-shadow: var(--ring); background: var(--white); color: var(--g1);
}

/* --- upload controls, everywhere -----------------------------------------------------------

   One rule for every `input[type=file]` on the site: six of them, across the publish form, the
   avatar, and the business profile's four.

   A file input is the one form control a browser styles entirely by itself, and the result is
   a platform button -- grey and square on Windows, a blue-grey capsule on macOS, something else
   again on Android -- sitting in the middle of a form where every other control is a 44px white
   field with our hairline ring. The dashboard had six of them and they were the only controls
   on it that did not look like they belonged to the same site.

   `::file-selector-button` is the standard hook. The WebKit spelling is a **separate rule**
   rather than a second selector on the same one, and that is load-bearing: a browser that does
   not recognise one pseudo-element in a selector list drops the entire rule, so pairing them
   would style the button in neither place rather than in both.

   Nothing here replaces the input with a label-and-hidden-input trick. That pattern loses the
   filename, loses drag-and-drop, and breaks the control entirely with JavaScript off -- all to
   gain what these twenty lines give for nothing. */
input[type="file"] {
  width: 100%; max-width: 100%; padding: 7px;
  font: inherit; font-size: 14px; color: var(--g5); cursor: pointer;
  background: var(--white); border: 0; border-radius: var(--r-tile); box-shadow: var(--ring);
}
input[type="file"]:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }
input[type="file"]::file-selector-button {
  margin-right: 12px; height: 34px; padding: 0 16px; border: 0; border-radius: var(--r-pill);
  background: var(--field); color: var(--g2);
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
  transition: background .2s, color .2s;
}
input[type="file"]::-webkit-file-upload-button {
  margin-right: 12px; height: 34px; padding: 0 16px; border: 0; border-radius: var(--r-pill);
  background: var(--field); color: var(--g2);
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
  transition: background .2s, color .2s;
}
input[type="file"]:hover::file-selector-button {
  background: var(--c-primary-lighter); color: var(--c-primary);
}
input[type="file"]:hover::-webkit-file-upload-button {
  background: var(--c-primary-lighter); color: var(--c-primary);
}
.price-states { margin-top: 16px; padding: 16px; border: 0; border-radius: var(--r-tile); box-shadow: var(--ring); }
.price-states legend { padding: 0 6px; font-size: 14px; font-weight: 600; color: var(--g2); }
.price-states .auth-check { margin-top: 6px; }
.price-states input[type="text"] {
  margin-top: 10px; width: 100%; height: 44px; padding: 0 12px; font-size: 15px; border: 0;
  border-radius: var(--r-tile); box-shadow: var(--ring);
}

/* --- searchable dropdown (the carspace.ng pattern, over a real <select>) ----------------- */
.cs-combo { position: relative; }
/* The select stays in the DOM and stays the form control; it is only visually replaced, so the
   form still posts and still works when this widget never initialises. */
.cs-combo-native { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.cs-combo-trigger {
  width: 100%; height: 44px; padding: 0 36px 0 12px; font: inherit; font-size: 15px;
  text-align: left; color: var(--g1); background: var(--white); border: 0;
  border-radius: var(--r-tile); box-shadow: var(--ring); cursor: pointer;
}
.cs-combo-trigger::after {
  content: ""; position: absolute; right: 14px; top: 19px; width: 8px; height: 8px;
  border-right: 2px solid var(--g7); border-bottom: 2px solid var(--g7);
  transform: rotate(45deg); pointer-events: none;
}
.cs-combo-trigger:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 1px; }
.cs-combo-panel {
  position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% + 4px);
  background: var(--white); border-radius: var(--r-tile);
  box-shadow: var(--ring), 0 8px 24px rgba(0, 0, 0, .12); padding: 8px;
}
.cs-combo-search {
  width: 100%; height: 38px; padding: 0 10px; font-size: 15px; border: 0;
  border-radius: var(--r-tile); box-shadow: var(--ring);
}
.cs-combo-list { max-height: 260px; overflow-y: auto; margin-top: 8px; }
.cs-combo-list li {
  padding: 9px 10px; border-radius: var(--r-tile); font-size: 15px; color: var(--g2);
  cursor: pointer;
}
.cs-combo-list li:hover, .cs-combo-list li[aria-selected="true"] {
  background: var(--c-primary-lighter); color: var(--c-primary);
}
.cs-combo-empty { padding: 10px; font-size: 14px; color: var(--g6); }
/* The listing form's sections (/sell-car and /item/edit/<id>).

   The form is 74 cap-* properties plus photos, price and location, and as one column it was a
   page a seller had to read to the end. Each section is a <details> card: one form, one POST,
   and it works with JavaScript off -- see the include's own comment for why that mattered more
   than a wizard would have.

   `.form-step` replaces `.spec-more` and `.spec-features`, which styled the two disclosures the
   sections absorbed. */
.listing-form { gap: 0; }
.form-step {
  margin-top: 14px; background: var(--white); border-radius: var(--r-tile);
  box-shadow: var(--ring);
  /* **No `overflow: hidden`.** It was here to clip the summary's hover tint to the card's
     rounded corners, and it silently broke every searchable dropdown inside a section: a
     `.cs-combo-panel` is absolutely positioned and an ancestor with `overflow: hidden` clips it,
     so the colour list opened 328px tall inside a box with 52px of room and 276px of it was cut
     off. The field looked like a dropdown that would not open. The corners are rounded on the
     summary itself instead, which costs nothing and clips nothing. */
}
.form-step > summary {
  display: flex; align-items: center; gap: 12px; padding: 14px 16px;
  font-size: 15px; font-weight: 600; color: var(--g1); cursor: pointer;
  /* Both spellings: Safari still needs the pseudo-element, everyone else takes list-style. */
  list-style: none;
  /* Closed, the summary *is* the card, so it carries the full radius; open, only the top. */
  border-radius: var(--r-tile) var(--r-tile) 0 0;
}
.form-step:not([open]) > summary { border-radius: var(--r-tile); }
.form-step > summary::-webkit-details-marker { display: none; }
.form-step > summary:hover { background: var(--g14); }
.form-step > summary:focus-visible { outline: 2px solid var(--c-primary); outline-offset: -2px; }
/* The number, so a collapsed stack still reads as an ordered set of steps rather than a pile
   of unrelated panels. */
.step-n {
  flex: 0 0 auto; display: grid; place-items: center; width: 24px; height: 24px;
  font-size: 13px; font-weight: 700; color: var(--c-primary);
  background: var(--c-primary-lighter); border-radius: 50%;
}
/* Pushed to the far end, and carrying the field count: "52 optional" on a closed panel is what
   tells a seller there is something in there worth opening. */
.step-hint { margin-left: auto; font-size: 13px; font-weight: 500; color: var(--g6); }
/* The chevron is drawn from a border rather than shipped as an asset, and rotates on open. */
.form-step > summary::after {
  content: ""; flex: 0 0 auto; width: 7px; height: 7px; margin-left: 10px;
  border-right: 2px solid var(--g6); border-bottom: 2px solid var(--g6);
  transform: rotate(45deg); transition: transform .15s ease;
}
.form-step[open] > summary::after { transform: rotate(-135deg); }
.form-step[open] > summary { border-bottom: 1px solid var(--g13); }
.form-step-body { display: flex; flex-direction: column; gap: 6px; padding: 4px 16px 18px; }
/* The body's first label would otherwise sit tight against the summary's rule. */
.form-step-body > label:first-child { margin-top: 12px; }
.form-step-body > .muted { margin-top: 8px; font-size: 13px; }
/* The cap-* controls are now section children rather than disclosure children, so they need the
   full-width rule the replaced `.spec-more > select, .spec-more > input` carried. Without it the
   selects shrink to their longest option and the column stops lining up. */
.form-step-body > input, .form-step-body > select, .form-step-body > textarea { width: 100%; }
/* A section whose own fields failed validation is forced open, so an error can never be hidden
   inside a collapsed panel. publish.js sets this; the server renders errors regardless. */
.form-step.has-error { box-shadow: 0 0 0 1px var(--c-danger, #d93025); }
.form-contact-note { margin-top: 18px; font-size: 13px; }

/* The equipment checkboxes: a grid rather than a 52-row column, because the list is the data
   buyers filter on and has to be scannable in one pass. auto-fit on its own -- no `fr` beside
   it -- for the reason the footer collapse taught us (A12). */
.feature-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  column-gap: 20px; margin-top: 6px;
}
.feature-grid .auth-check { margin-top: 10px; align-items: flex-start; }
.feature-grid .auth-check input { flex: 0 0 auto; margin-top: 2px; accent-color: var(--c-primary); }
.feature-grid .auth-check label { font-size: 14px; color: var(--g2); line-height: 1.35; }
.feature-grid .auth-check input:focus-visible {
  outline: 2px solid var(--c-primary); outline-offset: 2px;
}

/* --- the account area (ACCOUNT-AREA.md §2-§4) -------------------------------------------- */
/* Sidebar beside content, collapsing to stacked on a phone. `auto-fit` is not used here on
   purpose: this is a two-role layout, not a set of equal cells, so the columns are named. */
/* The account breadcrumb sits above the two-column layout, outside the cards, so it reads as
   the page's position rather than as part of the first panel's content. */
.account-crumbs { margin-bottom: 12px; }
.account-layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 16px; align-items: start; }
.account-main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
@media (max-width: 860px) {
  .account-layout { grid-template-columns: minmax(0, 1fr); }
}

.account-nav {
  padding: 16px; background: var(--white); border-radius: var(--r-card); box-shadow: var(--ring);
}
/* The sidebar's collapse. See the include for why it exists: below 860px the grid is one column,
   and the sidebar was a full screen of navigation above the page the seller asked for.
   The summary is hidden at desktop width, where the panel is just always open. */
.account-nav-toggle { display: none; }
@media (max-width: 860px) {
  /* The nav's own padding goes to the summary and the body, so the collapsed bar is the full
     width of the card and reads as a control rather than as text with a box around it. */
  .account-nav { padding: 0; }
  .account-nav-toggle {
    display: flex; align-items: center; gap: 10px; padding: 14px 16px; cursor: pointer;
    font-size: 15px; font-weight: 700; color: var(--g1); list-style: none;
  }
  .account-nav-toggle::-webkit-details-marker { display: none; }
  .account-nav-toggle:focus-visible { outline: 2px solid var(--c-primary); outline-offset: -2px; }
  /* A tinted bar rather than plain text in a white card: the dealer has to recognise this as a
     thing you press, and a row of text on white is not that. */
  .account-nav-panel:not([open]) > .account-nav-toggle { background: var(--c-primary-lighter); }
  .account-nav-toggle-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
  .account-nav-current { font-size: 13px; font-weight: 500; color: var(--g6); }
  /* The hamburger, drawn from three spans -- the shape people look for, beside the words that
     say what it does. */
  .account-nav-burger {
    flex: 0 0 auto; display: flex; flex-direction: column; justify-content: center; gap: 4px;
    width: 20px; height: 20px;
  }
  .account-nav-burger span {
    display: block; height: 2px; border-radius: 2px; background: var(--c-primary);
  }
  /* "Tap to open" / "Tap to close" -- the line that teaches the control rather than assuming
     it. Both labels are in the markup and CSS picks one, so the state cannot go stale. */
  .account-nav-hint { flex: 0 0 auto; font-size: 12px; font-weight: 600; color: var(--c-primary); }
  .account-nav-hint-close,
  .account-nav-panel[open] > .account-nav-toggle .account-nav-hint-open { display: none; }
  .account-nav-panel[open] > .account-nav-toggle .account-nav-hint-close { display: inline; }
  /* `:empty` and not a template conditional: the span is filled by a loop over the nav groups,
     so on a page that is not itself a nav entry it renders empty rather than absent. */
  .account-nav-current:empty { display: none; }
  .account-nav-toggle::after {
    content: ""; flex: 0 0 auto; width: 7px; height: 7px; margin-left: auto;
    border-right: 2px solid var(--g6); border-bottom: 2px solid var(--g6);
    transform: rotate(45deg); transition: transform .15s ease;
  }
  .account-nav-panel[open] > .account-nav-toggle::after { transform: rotate(-135deg); }
  .account-nav-panel[open] > .account-nav-toggle { border-bottom: 1px solid var(--g13); }
  .account-nav-body { padding: 4px 16px 16px; }
}
.account-me { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.account-avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; }
.account-avatar-blank {
  display: grid; place-items: center; font-size: 18px; font-weight: 700; color: var(--white);
  background: var(--c-primary);
}
.account-name { font-size: 15px; font-weight: 700; color: var(--g1); }
.account-edit { font-size: 13px; font-weight: 600; color: var(--c-primary); }
.account-group { margin-top: 18px; }
.account-group-title {
  font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--g6);
}
.account-group ul { list-style: none; margin: 6px 0 0; }
.account-group a {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 10px; margin: 0 -10px; border-radius: var(--r-tile);
  font-size: 14px; color: var(--g2);
}
.account-group a:hover { background: var(--c-primary-lighter); color: var(--c-primary); }
.account-group a.is-current {
  background: var(--c-primary-lighter); color: var(--c-primary); font-weight: 700;
}
.account-count {
  flex: 0 0 auto; min-width: 22px; padding: 1px 7px; border-radius: 999px;
  font-size: 12px; font-weight: 700; text-align: center;
  color: var(--g2); background: var(--field);
}
.account-group a.is-current .account-count,
.account-group a:hover .account-count { color: var(--c-primary); background: var(--white); }
.account-signout { margin-top: 18px; }

/* The hub header: greeting and primary action on one row, stacked on a phone. `gap` is what
   separates the button from the text, so it cannot go back to being flush against it. */
.dash-hero {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px;
}
.dash-hero-text { min-width: 0; }
/* The heading is inside a flex child here, so its own bottom margin would double up with the
   row's gap. The sub-line below it carries the spacing instead. */
.dash-hero .sec-title { margin-bottom: 4px; }
.dash-hero .muted { font-size: 14px; }
.dash-hero .btn { flex: 0 0 auto; }
@media (max-width: 560px) {
  /* Full width on a phone, where a 140px pill beside wrapped text reads as an afterthought. */
  .dash-hero { flex-direction: column; align-items: stretch; }
  .dash-hero .btn { width: 100%; }
}

/* Dashboard cards. A card is a link, so the whole tile is the target rather than the title
   alone -- a 3px-tall click target is the kind of thing that only ever gets found on a phone. */
.dash-cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px;
  margin-top: 12px;
}
.dash-card {
  display: flex; flex-direction: column; gap: 4px; padding: 14px;
  border-radius: var(--r-tile); box-shadow: var(--ring); background: var(--white);
}
.dash-card:hover { box-shadow: var(--ring), 0 6px 18px rgba(0, 0, 0, .08); }
.dash-count { font-size: 28px; font-weight: 800; line-height: 1.1; color: var(--c-primary); }
.dash-title { font-size: 15px; font-weight: 700; color: var(--g1); }
.dash-help { font-size: 13px; color: var(--g5); line-height: 1.4; }

/* The listing manager (/user/items). */
/* The head holds the heading, what is being shown and the filter, on one line. What was here
   was a heading card, a form of three stacked label-above-control pairs and an Apply button,
   and then a second card that opened with the count -- four things to read before a seller
   reached the one thing this page is for, and none of them resembling a control they had used
   anywhere else on the site. */
.items-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 14px 24px;
}
.items-head-text { min-width: 0; }
.items-head .sec-title { margin-bottom: 4px; }
.items-count { margin: 0; font-size: 14px; color: var(--g6); }
.items-count strong { font-weight: 700; color: var(--g2); }
/* The filter *is* the shared pill bar (`.pill-bar`) -- the same control, with the same
   searchable dropdown in it, that this seller already used to find a car on the public side.
   D-69's lesson in one line: the instance that does not look like the shared component is the
   one that teaches people the site is three sites. */
.items-bar { flex: 1 1 340px; max-width: 520px; }
.items-filter-note { margin: 12px 0 0; font-size: 14px; color: var(--g6); }
.items-filter-note a { color: var(--c-primary); font-weight: 600; }

.items-rows {
  list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px;
}
/* A row is a tile, not a line between hairlines. It carries a photograph, a title, a price, a
   state and up to four controls, and a 1px rule is not enough structure to hold that: the
   actions of one car read as the actions of the car above it, which on a page whose buttons
   hide and un-list cars is the wrong thing to be guessing about. */
.items-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center;
  gap: 10px 18px; padding: 12px 14px; border-radius: var(--r-tile);
  background: var(--white); box-shadow: var(--ring); transition: box-shadow .16s;
}
.items-row:hover { box-shadow: var(--ring), 0 6px 18px rgba(0, 0, 0, .07); }
.items-row-link { display: flex; align-items: center; gap: 14px; min-width: 0; }
.items-row-link:hover .items-title { color: var(--c-primary); }
.items-thumb {
  flex: 0 0 108px; width: 108px; height: 81px; border-radius: var(--r-tile); overflow: hidden;
  display: grid; place-items: center; background: var(--field);
}
.items-thumb img { width: 100%; height: 100%; object-fit: cover; }
.items-meta { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.items-title {
  font-size: 15.5px; font-weight: 600; color: var(--g1); transition: color .14s;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The price carries the public card's own weight and colour (DESIGN-SYSTEM.md §1.2): a seller
   looking at their own list is looking at the same number a buyer sees. */
.items-price { font-size: 16px; font-weight: 800; color: var(--c-primary); }
.items-sub { font-size: 13px; color: var(--g8); }
.items-state { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.state-chip {
  padding: 3px 9px; border-radius: 999px; font-size: 12px; font-weight: 700; white-space: nowrap;
}
/* Colour carries meaning here, so the label is always present too -- the chip reads correctly
   in greyscale and to a screen reader, which a dot or a bare colour would not. */
.state-chip.state-active { color: #0f6b3f; background: #e4f5ec; }
.state-chip.state-pending_validate { color: #8a5a00; background: #fdf1dc; }
.state-chip.state-expired { color: var(--g2); background: var(--field); }
.state-chip.state-hidden { color: var(--g2); background: var(--field); }
.state-chip.state-premium { color: var(--c-primary); background: var(--c-primary-lighter); }
/* Sold is an outcome rather than a state the clock put the listing in, so it reads as the
   accent -- and it is the one chip on this page a seller wants to see. */
.state-chip.state-sold { color: #8a2200; background: #fde8e2; }
/* Auto-refresh is a *running* thing rather than a state the listing is in, so it reads as the
   accent rather than as another blue chip beside Promoted -- the two often sit on the same row
   and two blues there say "one thing" when they are two purchases. */
.state-chip.state-refresh { color: #8a3a00; background: #fdeee4; }

/* What a promotion buys, on the promote page and on the publish confirmation. Markers, unlike
   almost every other list on this site, because this one *is* prose: four parallel claims a
   seller reads before spending money, and a bare indented block reads as one paragraph that
   lost its punctuation. */
.benefit-list { margin: 10px 0 18px; padding-left: 20px; list-style: disc; }
.benefit-list li { margin-bottom: 7px; font-size: 14.5px; line-height: 1.5; color: var(--g3); }
.benefit-list strong { color: var(--g1); }
.benefit-list a { color: var(--c-primary); font-weight: 600; }

/* ---------- action confirmations (the carspace.ng toast, measured 2026-10-17) -------------

   Top-right on a desktop and full-width under the header on a phone, which is where theirs
   sits and where a confirmation belongs: close enough to the chrome to read as the site
   answering, far enough from the thumb to not be dismissed by accident.

   `pointer-events: none` on the container and `auto` on each toast, so the empty column the
   container occupies never swallows a click on the page behind it -- it is fixed, full-height
   on a phone, and without this it would make the top of every page untappable. */
.toasts {
  position: fixed; top: 16px; right: 16px; z-index: 60;
  display: flex; flex-direction: column; gap: 10px;
  width: min(380px, calc(100vw - 32px));
  pointer-events: none;
}
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 13px 14px; border-radius: var(--r-tile);
  /* Shadow *and* a hairline ring: a success toast lands on a white header at this position,
     and a shadow alone does not separate them. */
  box-shadow: 0 12px 28px rgba(20, 24, 33, .18), 0 0 0 1px rgba(255, 255, 255, .22);
  color: var(--white); font-size: 14.5px; line-height: 1.45;
  pointer-events: auto;
  /* Starts hidden and is revealed by `toast.js` on the next frame. With no JavaScript the
     class never arrives, so the rule below gives it back -- a confirmation that renders
     invisible is worse than one that never animates. */
  opacity: 0; transform: translateX(14px);
  transition: opacity .25s ease, transform .25s ease;
}
.toast.is-visible { opacity: 1; transform: none; }
.toast.is-leaving { opacity: 0; transform: translateX(14px); }
/* No script, no animation, no disappearing act -- but the message is on the page. */
.no-js .toast { opacity: 1; transform: none; }
.toast-icon { flex: 0 0 auto; margin-top: 1px; }
.toast-icon svg { display: block; width: 17px; height: 17px; }
.toast-body { flex: 1 1 auto; min-width: 0; }
.toast-close {
  flex: 0 0 auto; padding: 0 2px; border: 0; background: none; color: inherit;
  font-size: 20px; line-height: 1; opacity: .75; cursor: pointer;
}
.toast-close:hover, .toast-close:focus-visible { opacity: 1; }
/* Our palette, not a traffic-light set borrowed from a framework. Success is a green dark
   enough to carry white type at 14.5px; error is the brand accent darkened to the same
   weight, so a failure reads as this site rather than as a browser dialog. */
.toast.is-success { background: #0f8b5f; }
.toast.is-error { background: #c2320b; }
.toast.is-warning { background: #a8700a; }
.toast.is-info, .toast.is-debug { background: var(--g1); }

@media (max-width: 760px) {
  /* Full width under the header rather than a narrow card hugging one edge, and clear of the
     fixed bottom bar -- a toast behind that bar is a toast nobody can dismiss. */
  .toasts { top: 12px; right: 12px; left: 12px; width: auto; }
}

/* The sign-in page's create-account offer, shown only to someone who arrived from Sell.

   Same shape as the publish confirmation's upsell below -- a rule and a block rather than a
   second card -- because both are the same move: the page has done its job, and this is the
   next thing the person actually came for. */
.auth-aside { margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--g13); }
.auth-aside-lead { margin: 0; font-size: 15.5px; font-weight: 600; color: var(--g1); }
.auth-aside-note { margin: 6px 0 14px; font-size: 14px; color: var(--muted); }
.auth-aside .btn { width: 100%; }

/* The publish confirmation's offer. Set off by a rule and a tint rather than by a card of its
   own, so it reads as a postscript to "it is live now" instead of competing with it. */
.upsell { margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--g13); }
.upsell .sec-sub { margin-bottom: 6px; }

/* ---------- error pages (404 / 403 / 410) ---------- */
/* Centred and narrow, because there is nothing on this page to scan -- it is one sentence and a
   way out. The link blocks below it keep the page-card rhythm of every other page. */
.error-card { text-align: center; max-width: 42rem; margin-inline: auto; }
/* The status number is decoration: `aria-hidden`, and sized so it reads as a watermark rather
   than competing with the heading that says what actually happened. */
.error-status {
  font-size: 56px; font-weight: 800; line-height: 1; color: var(--g12); margin-bottom: 8px;
}
.error-lead { color: var(--g5); margin: 0 auto 20px; max-width: 34rem; }
.error-search { display: flex; gap: 8px; max-width: 30rem; margin: 0 auto 16px; }
.error-search input {
  width: 100%; padding: 10px 14px; border: 0; border-radius: var(--r-tile);
  background: var(--field); font: inherit;
}
.error-actions { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin: 0; }
/* The recovery links sit in a row on a wide screen and stack on a phone. `auto-fit` alone, with
   no `fr` beside it -- the footer bug (D-26) was exactly that mistake. */
.link-blocks { display: grid; gap: 24px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.link-blocks .link-block + .link-block { margin-top: 0; }
.link-cloud { display: flex; flex-wrap: wrap; gap: 6px 10px; }
.link-cloud a { font-size: 14px; color: var(--c-primary); }

/* ---------- contact the seller (apps/leads) ---------- */
/* The form lives in the item sidebar and on its own page, from one include, so it is styled
   once and sized by its container rather than by a width of its own. */
#contact-box { margin-top: 16px; }
.ibox-title { font-size: 16px; font-weight: 700; margin-bottom: 12px; color: var(--g1); }
.contact-seller-form label {
  display: block; margin-top: 14px; margin-bottom: 6px; font-size: 14px; font-weight: 600;
}
.contact-seller-form input,
.contact-seller-form textarea {
  width: 100%; padding: 10px 12px; font: inherit; color: var(--ink);
  background: var(--white); border: 0; border-radius: var(--r-tile); box-shadow: var(--ring);
}
.contact-seller-form textarea { resize: vertical; min-height: 96px; }
.contact-seller-form input:focus-visible,
.contact-seller-form textarea:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 1px; }
.contact-seller-form input[readonly] { background: var(--field); box-shadow: none; }
.contact-seller-form .btn { margin-top: 16px; width: 100%; }
.field-help { display: block; margin-top: 4px; font-size: 13px; color: var(--g6); }
.contact-as { font-size: 14px; margin: 0 0 4px; }
.contact-notice { font-size: 12px; line-height: 1.5; margin: 12px 0 0; color: var(--g6); }
.contact-notice a { color: var(--c-primary); }
/* The confirmation after a send. Green would say "transaction complete"; this is a message in
   flight, so it reads as information and the copy does the work. */
/* Green, and a tick, because this is a success state and it was rendering as plain grey body
   copy indistinguishable from the rest of the sidebar -- a buyer could not tell at a glance
   whether anything had happened. Same green as the success toast, so "it worked" means one
   colour everywhere on the site. */
.contact-sent {
  padding: 13px 14px; border-radius: var(--r-tile);
  background: #e6f6ef; box-shadow: inset 0 0 0 1px #b6e2cf;
}
.contact-sent strong {
  display: flex; align-items: center; gap: 7px;
  font-size: 15px; color: #0b6b49; margin-bottom: 6px;
}
/* The tick, drawn rather than fetched: one glyph does not justify a request or an icon font. */
.contact-sent strong::before {
  content: ""; flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
  background: #0f8b5f url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 12.5 5 5L20 7'/%3E%3C/svg%3E") no-repeat center;
}
.contact-sent p { margin: 0; font-size: 14px; color: #246b53; }
/* The seller's own address, printed only when they ticked the box. */
.line-email {
  display: flex; align-items: center; gap: 8px; margin-top: 12px;
  font-size: 14px; word-break: break-all;
}
.line-email a { color: var(--c-primary); font-weight: 600; }
/* The standalone page's restatement of which car this is about. */
.contact-item-summary { display: flex; gap: 14px; align-items: center; margin-bottom: 20px; }
.contact-item-summary img { border-radius: var(--r-tile); object-fit: cover; }
.contact-item-summary > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.contact-item-title { font-size: 16px; font-weight: 700; color: var(--g1); }
.contact-item-price { font-size: 15px; font-weight: 700; color: var(--c-primary); }
/* The dealer page's reveal (D-42). A button, because it posts -- the thing it used to be was a
   link to a login that led back to the same link. */
.bpr-reveal-form { display: inline; }
.bpr-reveal-form .btn-link { color: var(--c-primary); font-weight: 600; }
.bpr-phone-number { font-weight: 700; color: var(--g1); }

/* ---------- the messenger (apps/messaging) ---------- */
/* The usage line. Not an error at rest -- it is a number the sender should already know -- so it
   reads as information until it is spent, and only then takes the warning treatment. */
.usage-line { font-size: 14px; color: var(--g5); margin: 8px 0 0; line-height: 1.6; }
.usage-line strong { color: var(--g1); }
.usage-line-spent strong { color: var(--c-accent); }
.thread-rows, .block-rows { list-style: none; margin: 0; padding: 0; }
.thread-row + .thread-row { border-top: 1px solid var(--g13); }
.thread-row-link {
  display: grid; gap: 2px 12px; padding: 14px 0; align-items: baseline;
  grid-template-columns: 1fr auto;
}
.thread-who { font-size: 15px; font-weight: 600; color: var(--g1); }
/* Colour never carries the meaning alone: the word "New" is always present, so the row reads
   correctly in greyscale and to a screen reader. Same rule as the listing state chips. */
.thread-new {
  display: inline-block; margin-left: 8px; padding: 2px 8px; border-radius: var(--r-pill);
  font-size: 11px; font-weight: 700; color: var(--white); background: var(--c-primary);
}
.thread-about {
  font-size: 14px; color: var(--g5); grid-column: 1;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.thread-when { font-size: 13px; color: var(--g8); grid-column: 2; grid-row: 1; text-align: right; }
.thread-unread .thread-about { color: var(--g2); }
.thread-subject { font-size: 15px; margin: 0; }
.thread-subject a { color: var(--c-primary); font-weight: 600; }
/* The conversation. Mine on the right, theirs on the left -- the one convention everybody
   already knows, so it needs no legend. */
.message-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.message { max-width: 42rem; padding: 12px 14px; border-radius: var(--r-media); background: var(--field); }
.message-mine { margin-left: auto; background: var(--c-primary-lighter); }
.message-head { display: flex; gap: 10px; align-items: baseline; font-size: 13px; margin-bottom: 4px; }
.message-head strong { font-size: 14px; color: var(--g1); }
.message-body { font-size: 15px; line-height: 1.6; color: var(--ink); word-break: break-word; }
.reply-form label { display: block; margin-bottom: 6px; font-size: 14px; font-weight: 600; }
.reply-form textarea {
  width: 100%; padding: 10px 12px; font: inherit; resize: vertical; min-height: 96px;
  background: var(--white); border: 0; border-radius: var(--r-tile); box-shadow: var(--ring);
}
.reply-form textarea:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 1px; }
.reply-form .btn { margin-top: 12px; }
.block-row {
  display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; padding: 10px 0; font-size: 14px;
}
.block-row + .block-row { border-top: 1px solid var(--g13); }

/* ---------- the seller's row actions and the editor ---------- */
/* The actions sit under the row on a phone and beside it on a wide screen. `flex-wrap`, because
   four controls plus a state chip does not fit a 360px row and must not scroll sideways. */
.items-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 640px) {
  /* One column. At 360px the three-column row left the title about 90px and the controls
     wrapped into a ragged pile under a half-cropped price. Stacked, the photograph and the
     title keep one line, the state sits under them, and the controls get a row of their own. */
  .items-row { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .items-state, .items-actions { justify-content: flex-start; }
  .items-thumb { flex-basis: 84px; width: 84px; height: 63px; }
  .items-bar { max-width: none; }
}
.inline-action { display: inline; margin: 0; }
.btn-sm { padding: 5px 10px; font-size: 13px; }
/* Destructive controls read as text, not as a button. A red button beside three grey ones is
   the easiest thing on the page to hit by accident, and this one goes to a confirmation page. */
.btn-link-danger { color: var(--c-accent); font-weight: 600; font-size: 13px; }
.btn-link-danger:hover { text-decoration: underline; }
.btn-danger { background: var(--c-accent); color: var(--c-accent-ink); }
.btn-danger:hover { background: var(--c-accent-hover); }
/* The existing photos on the edit form: a thumbnail with its keep checkbox under it. */
.edit-photos {
  list-style: none; margin: 0 0 14px; padding: 0; display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
}
.edit-photo { display: flex; flex-direction: column; gap: 6px; }
.edit-photo img { width: 100%; height: auto; border-radius: var(--r-tile); object-fit: cover; }
.edit-photo .auth-check { margin-top: 0; font-size: 13px; }
.edit-photo .auth-check label { font-size: 13px; }

/* ---------- account settings (ACCOUNT-AREA.md section 6) ---------- */
.form-saved {
  margin: 8px 0 0; padding: 8px 12px; border-radius: var(--r-tile); font-size: 14px;
  color: #0f6b3f; background: #e4f5ec;
}
/* The read-only "Login data" block. A definition list, because that is what it is: three labels
   and three values, two of which are a credential standing in for itself. */
.settings-readonly { margin: 0; display: grid; gap: 4px 16px; grid-template-columns: max-content 1fr; }
.settings-readonly dt { font-size: 14px; font-weight: 600; color: var(--g5); }
.settings-readonly dd { margin: 0 0 8px; font-size: 15px; color: var(--g1); }
.settings-readonly dd a { margin-left: 10px; font-size: 14px; color: var(--c-primary); font-weight: 600; }
/* Said plainly, because the page still shows the old address after a change is requested --
   which reads as the form failing if nothing explains it. */
.settings-pending { display: block; margin-top: 4px; font-size: 13px; color: #8a5a00; }
.settings-avatar { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
.settings-avatar img { border-radius: 50%; object-fit: cover; }
.settings-avatar-blank { width: 96px; height: 96px; font-size: 32px; }
.settings-avatar input[type="file"] { margin: 6px 0 10px; max-width: 380px; }
@media (max-width: 560px) {
  /* Two columns of definition list do not fit a phone; the label stacks above its value. */
  .settings-readonly { grid-template-columns: 1fr; }
  .settings-readonly dd { margin-bottom: 12px; }
}

/* ---------- saved searches (ACCOUNT-AREA.md section 8) ---------- */
/* The save control sits above the grid, so it has to be quiet enough not to compete with the
   first row of cards and present enough to be found. A ghost button and one line of help. */
.grid-tools { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.save-search-form { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 0; }
.save-search-form .field-help { display: inline; margin: 0; }
.alert-rows { list-style: none; margin: 0; padding: 0; }
.alert-row {
  display: flex; gap: 12px; justify-content: space-between; align-items: flex-start;
  flex-wrap: wrap; padding: 14px 0;
}
.alert-row + .alert-row { border-top: 1px solid var(--g13); }
.alert-meta { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.alert-what { font-size: 15px; font-weight: 600; color: var(--g1); }
.alert-what:hover { color: var(--c-primary); }
.alert-when { font-size: 13px; color: var(--g8); }
/* A paused alert is dimmed *and* says so: a colour change alone does not survive greyscale or a
   screen reader, which is the rule the listing state chips and the message badge follow. */
.alert-paused .alert-what { color: var(--g6); }
.alert-state { font-size: 13px; color: #8a5a00; }
.alert-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

/* ---------- the account form system (DESIGN-SYSTEM.md section 5) ---------- */
/* Why this exists. `.auth-form` is a flex column in which every control is the full width of
   the card, which is right for a sign-in box of two fields and wrong for an editor of thirty.
   The business profile was nine cards of page-wide boxes -- a hex colour, seven opening hours
   and four video IDs, each as wide as the page -- with labels whose only separation from the
   control above was a 10px margin, and a Save button that inherited
   `.auth-form .btn { width: 100% }` and sat at the bottom of whichever card happened to be
   last, looking like it saved that card.
   These four primitives are what the editors share, so a page stops arranging its own: a
   **field** (the label/control/help/error unit), a **grid** to put fields beside each other, an
   **actions bar** that belongs to the form rather than to a card, and an **upload** row. */

/* The field. `gap` rather than margins, so the rhythm cannot drift between two pages that
   happen to order label, help and error differently. */
.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.field > label { margin: 0; font-size: 14px; font-weight: 600; color: var(--g2); }
.field > input, .field > select, .field > textarea { width: 100%; }
.field > .field-help, .field > .field-error { margin: 0; }
/* The help text under a field sits closer to its control than to the next label, which is the
   whole of what makes a stack of fields readable as pairs rather than as a list. */
.field > .field-help { margin-top: -1px; }

/* Fields beside each other. `auto-fit` with a 220px floor and no `fr` of its own -- the footer
   collapse (D-26) was exactly that mistake. A day of opening hours and a YouTube ID are short
   values, and a column of page-wide boxes for them reads as a form that does not know what it
   is asking for. */
.form-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px 18px;
}
/* For values that are shorter still: a time range, a hex colour. */
.form-grid.is-tight { grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); }
.field-wide { grid-column: 1 / -1; }
/* A grid that follows a heading or a line of help needs the same space a field would have. */
.card > .form-grid, .form-grid + .form-grid { margin-top: 14px; }

/* The actions bar. One bar at the end of the form, with the control at its natural width and a
   line saying what it does -- a save button belongs to the form, not to the card it fell into.
   The `.auth-form` override is the point: that rule makes every button a full-width banner. */
.form-actions {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px 18px;
}
.auth-form .form-actions .btn, .form-actions .btn { width: auto; margin-top: 0; }
.form-actions-note { margin: 0; font-size: 13.5px; color: var(--g6); max-width: 46ch; }
/* The save, kept on screen. The business profile is nine sections and about 4,000px tall, so a
   button at the end is a button a dealer has to go looking for -- and the one thing they must
   never fail to find is the one that keeps their work. Sticky with a bottom offset: it floats
   at the foot of the viewport for the whole of the form and settles into place at the end of
   it, which costs one declaration and no JavaScript. The card's own white background and ring
   are what make it read as a bar over the content rather than part of it. */
.form-actions-card { position: sticky; bottom: 12px; z-index: 5; }
.form-actions-card { box-shadow: var(--ring), 0 6px 20px rgba(0, 0, 0, .10); }
@media (max-width: 560px) {
  /* Stacked, the button is the widest thing in the bar and takes the full width -- which is
     what the thumb expects at the end of a long form. */
  .form-actions { flex-direction: column-reverse; align-items: stretch; }
  .auth-form .form-actions .btn, .form-actions .btn { width: 100%; }
}

/* An upload. What is there now, the control that replaces it, and the way to remove it, on one
   line: a bare file input beside a 64px logo is two unrelated things sitting next to each
   other, and the relationship between them is the only thing a dealer needs to understand. */
.upload-field { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
.upload-current {
  flex: 0 0 auto; display: grid; place-items: center; padding: 6px; background: var(--field);
  border-radius: var(--r-tile);
}
.upload-current img { display: block; border-radius: 6px; max-width: 100%; height: auto; }
.upload-controls { flex: 1 1 240px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
/* The browser's own file control is a grey system button with the file name beside it, and it
   is the one control on these pages that does not belong to this site. Only the button half is
   restyled: the text beside it is the browser's and is where "no file chosen" has to stay. */
input[type="file"] { font-family: inherit; font-size: 14px; color: var(--g5); }
input[type="file"]::file-selector-button {
  margin-right: 12px; height: 34px; padding: 0 14px; border: 0; border-radius: var(--r-pill);
  background: var(--white); box-shadow: var(--ring); color: var(--g2);
  font-family: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
  transition: background .16s;
}
input[type="file"]::file-selector-button:hover { background: var(--g14); }
input[type="file"]:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }

/* ---------- business-profile editor (ACCOUNT-AREA.md section 7) ---------- */
/* A thumbnail of what is already there. Shown because a file input cannot say "there is
   already a logo", and a dealer who cannot see one uploads a second. It is a plate inside
   `.upload-current` now, so the picture and the control that replaces it read as one field. */
.brand-current { display: block; }
.brand-current img { display: block; border-radius: 6px; background: var(--white); }
/* The colour field: the hex a dealer typed, shown as the colour it is. A text input saying
   "#1a73e8" is a string; the swatch is the answer to "is that the blue I meant". Painted by
   bpr.js from the input's own value, and only when that value is a hex colour. */
.swatch-field { display: flex; align-items: center; gap: 12px; }
.swatch-field input { flex: 0 1 180px; }
.swatch {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: var(--r-tile);
  box-shadow: var(--ring); background: var(--field);
}
/* The category blocks. The parent/child nesting is structural, not decoration -- the cascade is
   about that relationship -- so the group is a panel with the make list inside it rather than
   sixty checkboxes separated by hairlines. Sixty, on one page, is the longest list in the
   account area and the one most worth being able to skim. */
.bpr-box-check {
  padding: 12px 14px; border-radius: var(--r-tile); background: var(--white);
  box-shadow: var(--ring);
}
.bpr-box-check + .bpr-box-check { margin-top: 10px; }
.bpr-box-check > .auth-check { margin-top: 0; }
.bpr-box-check > .auth-check label { font-size: 15px; color: var(--g1); }
/* Indented and set off by a rule, so the makes read as belonging to the category above them
   rather than as a second list that happens to follow it. */
.bpr-children {
  margin: 10px 0 2px; padding: 10px 0 0 14px; border-top: 1px solid var(--g13);
  border-left: 2px solid var(--c-primary-lighter);
}

/* ---------- the money surface (ACCOUNT-AREA.md section 11) ---------- */
.wallet-balance { font-size: 14px; color: var(--g5); white-space: nowrap; }
.wallet-balance strong { color: var(--g1); }
/* The cart. Live renders this as a CSS grid rather than a table and the class names are its
   own; on a phone the row stacks, because five columns at 360px is a horizontal scroll. */
.osp-cart { display: flex; flex-direction: column; }
.osp-cart-head-row, .osp-cart-row {
  display: grid; gap: 10px; align-items: baseline; padding: 12px 0;
  grid-template-columns: 3rem 1fr 3rem 7rem 5rem;
}
.osp-cart-head-row { font-size: 13px; font-weight: 700; color: var(--g6); border-bottom: 1px solid var(--g12); }
.osp-cart-row + .osp-cart-row { border-top: 1px solid var(--g13); }
.osp-cart-row .prod { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.osp-cart-row .pric { font-weight: 700; color: var(--g1); }
.osp-pay-button { list-style: none; margin: 0 0 8px; padding: 0; }
.osp-pay-button .auth-check { align-items: flex-start; padding: 10px 0; }
.osp-pay-button input[disabled] + label { color: var(--g8); }
@media (max-width: 640px) {
  .osp-cart-head-row { display: none; }
  .osp-cart-row { grid-template-columns: 1fr auto; row-gap: 4px; }
  .osp-cart-row .code { display: none; }
}
/* Membership and pack cards. `auto-fit` with no `fr` beside it -- the footer bug (D-26) was
   exactly that mistake, and three tier cards is where it would show again. */
.tier-cards {
  list-style: none; margin: 0; padding: 0; display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.tier-card { padding: 20px; border-radius: var(--r-card); box-shadow: var(--ring); background: var(--white); }
.tier-card h3 { font-size: 18px; margin-bottom: 6px; }
.tier-price { font-size: 22px; font-weight: 800; color: var(--g1); margin-bottom: 12px; }
.tier-price .muted { font-size: 13px; font-weight: 400; }
/* "Which plan am I on" is the first question this page answers and it was the quietest thing
   on it -- one muted sentence above a grid of loud cards. */
.plan-now { display: flex; flex-direction: column; gap: 2px; }
.plan-now-label {
  margin: 0; font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--g5);
}
.plan-now-name { margin: 0 0 2px; font-size: 26px; font-weight: 800; color: var(--g1); line-height: 1.15; }
.plan-now .muted { margin: 0; font-size: 14px; }
/* And again on the card itself, because the grid is where the comparison happens. */
.tier-card.is-current { outline: 2px solid var(--c-primary); outline-offset: -2px; }
.tier-flag {
  display: inline-block; margin-bottom: 8px; padding: 2px 10px; border-radius: var(--r-pill);
  background: var(--c-primary); color: var(--white); font-size: 11px; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase;
}
.tier-saving { font-size: 13px; margin: -8px 0 12px; color: var(--c-primary); font-weight: 600; }
/* A plan's benefits have to *read* as a list. They did not: the global reset at the top of
   this file sets `list-style: none` on every ul, this rule repeated it, and the result was
   four benefits rendering as four unmarked lines that looked like a paragraph. Ticks rather
   than discs, because on a pricing card the marker is doing a second job -- it says "included"
   -- and a withheld row gets a muted dash instead, which is the comparison the ladder is
   selling. */
/* A plan's benefits have to *read* as a list. They did not: the global reset at the top of
   this file sets `list-style: none` on every ul, so eight benefits rendered as eight unmarked
   lines that scanned as a paragraph — on the card where the whole job is comparing one tier
   against the next.

   The marker is a real element in the template, not a `::before`. It is doing semantic work
   (tick = included, dash = withheld) and it survives a stale stylesheet, which is how the
   first attempt at this shipped looking unchanged. */
.tier-benefits { list-style: none; margin: 0 0 14px; padding: 0; font-size: 14px; color: var(--g5); }
.tier-benefits li { display: flex; gap: 8px; align-items: baseline; padding: 4px 0; line-height: 1.45; }
.tier-benefits .tick { flex: 0 0 auto; color: var(--c-primary); font-weight: 700; }
.tier-benefits .tick-no { color: var(--g5); font-weight: 400; }
.tier-card .auth-form { margin: 0; }
.tier-card .btn { margin-top: 10px; width: 100%; }

/* The boost picker: three durations compared side by side, then the checklist. */

/* The search belongs to the list it filters, so it sits in that card's header rather than in
   a full-width panel of its own above the page -- which made a filter look like a step in the
   flow.

   The control itself is `.pill-bar`, the one `/user/items` already uses to search listings.
   The first attempt here was a bare `<input type="search">` with a ghost button, which looked
   broken for a reason worth remembering: **this stylesheet has no global input rule**. Form
   fields are styled inside `.auth-form` and nowhere else, so an input outside it renders as
   the raw browser widget. Reusing the pill gets the border, the icon, the focus ring and the
   mobile stacking for free, and makes the two listing-search boxes on this site look alike. */
.boost-head {
  display: flex; gap: 16px; align-items: center; justify-content: space-between;
  flex-wrap: wrap; margin-bottom: 10px;
}
.boost-head .sec-sub { margin: 0; flex: 0 0 auto; }
.boost-head .boost-search { margin: 0; }
.boost-cleared { font-size: 13px; margin: -4px 0 10px; }
/* The confirmation screen: one price per row, then the totals block. */
.boost-line-price { flex: 0 0 auto; font-weight: 700; color: var(--g1); }
.boost-sum { margin-top: 14px; border-top: 1px solid var(--g6, #e4e4e7); }
.boost-sum .pay-row { display: flex; justify-content: space-between; gap: 12px; }
.boost-sum .pay-row-total { border-top: 1px solid var(--g6, #e4e4e7); font-size: 16px; }
.boost-confirm { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 16px; }
.boost-options {
  list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.boost-radio { position: absolute; opacity: 0; width: 0; height: 0; }
.boost-option {
  display: block; position: relative; cursor: pointer; padding: 14px;
  border: 2px solid var(--g6, #e4e4e7); border-radius: var(--r-card); background: var(--white);
}
.boost-radio:checked + .boost-option { border-color: var(--c-primary); }
/* Focus has to survive the visually-hidden input: without this the whole control is
   keyboard-operable and invisible while you operate it. */
.boost-radio:focus-visible + .boost-option { outline: 2px solid var(--c-primary); outline-offset: 2px; }
.boost-flag {
  position: absolute; top: -10px; right: 10px; font-size: 11px; font-weight: 700;
  background: var(--c-primary); color: var(--white); padding: 2px 8px; border-radius: var(--r-pill);
}
.boost-name { display: block; font-weight: 700; color: var(--g1); }
.boost-price { display: block; font-size: 19px; font-weight: 800; color: var(--g1); margin: 4px 0; }
.boost-price small { font-size: 12px; font-weight: 400; color: var(--g5); }
.boost-rate { display: block; font-size: 13px; line-height: 1.45; }
.boost-row { display: flex; align-items: center; gap: 12px; }
.boost-row-head { border-bottom: 1px solid var(--g6, #e4e4e7); padding-bottom: 10px; }
.boost-row.is-boosted { opacity: 0.62; }
.boost-row .items-meta { flex: 1 1 auto; min-width: 0; }
.boost-total {
  display: flex; gap: 12px; align-items: center; justify-content: space-between;
  flex-wrap: wrap; position: sticky; bottom: 0; z-index: 2;
}
.boost-total-sum { margin: 0; font-size: 16px; }
.boost-total .btn { margin: 0; }
/* The payment and wallet statements. Same stacking rule as the cart. */
.pay-rows { list-style: none; margin: 0; padding: 0; }
.pay-row {
  display: grid; gap: 4px 12px; padding: 12px 0; font-size: 14px;
  grid-template-columns: 8rem 1fr 7rem 4rem;
}
.pay-row + .pay-row { border-top: 1px solid var(--g13); }
.pay-source { font-weight: 600; color: var(--g1); }
.pay-ref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--g6); grid-column: 1 / -1; }
.pay-what { color: var(--g5); }
.pay-amount { font-weight: 700; text-align: right; }
.pay-when { color: var(--g8); text-align: right; }
.referral-link { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; word-break: break-all; }
@media (max-width: 640px) {
  .pay-row { grid-template-columns: 1fr auto; }
}

/* ---------- the two-month availability check (D-74) ---------- */
/* A real <dialog>, so the browser owns the backdrop, the Escape key and the focus trap once
   static/js/listing-confirm.js has called showModal(). Styled to work in both states: open as a
   modal it is centred by the first block, and open *without* the script -- the `open` attribute
   is in the markup -- it is a panel in the page flow that still answers. */
.confirm-modal {
  border: 0; padding: 0; background: transparent; max-width: min(560px, 100%); width: 100%;
  margin: 24px auto; color: var(--ink);
}
.confirm-modal::backdrop { background: rgba(0, 0, 0, .5); }
.confirm-box {
  background: var(--white); border-radius: var(--r-card); box-shadow: 0 10px 40px rgba(0, 0, 0, .3);
  display: flex; flex-direction: column;
}
.confirm-head { padding: 20px 20px 0; display: flex; flex-direction: column; gap: 6px; }
.confirm-title { font-size: 18px; font-weight: 700; color: var(--g1); }
.confirm-sub { font-size: 13px; color: var(--g5); }
.confirm-list { display: flex; flex-direction: column; padding: 16px 20px 0; gap: 4px; }
.confirm-row {
  display: flex; align-items: center; gap: 12px; padding: 10px 0;
  border-bottom: 1px solid var(--g13);
}
.confirm-row:last-child { border-bottom: 0; }
.confirm-thumb { flex: 0 0 auto; width: 72px; height: 54px; display: block; }
.confirm-thumb img {
  width: 72px; height: 54px; object-fit: cover; border-radius: var(--r-tile); display: block;
}
.confirm-facts { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.confirm-name {
  font-size: 14px; font-weight: 700; color: var(--g2); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.confirm-name:hover { color: var(--c-primary); }
.confirm-meta { font-size: 12px; color: var(--g7); }
/* The two answers carry the same weight on purpose -- see the template's comment. The sold
   button gets the accent on its *text* only, which separates the two without making either of
   them the one a hurried seller presses without reading. */
.confirm-actions { display: flex; gap: 6px; flex: 0 0 auto; }
.confirm-sold { color: var(--c-accent); }
.confirm-sold:hover { color: var(--c-accent-hover); }
.confirm-more { padding: 10px 20px 0; font-size: 13px; color: var(--g7); }
.confirm-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 16px 20px 20px;
}
@media (max-width: 560px) {
  .confirm-row { flex-wrap: wrap; }
  .confirm-actions { width: 100%; }
  .confirm-actions .btn { flex: 1 1 0; }
}

/* ---------- a sold listing's page (D-77) ---------- */
/* The page stays, the car does not. Red-brown rather than the accent: this is the one banner on
   the site that is neither a success nor a warning -- it is a fact about the thing the visitor
   came for, and it has to read as such at a glance, above the title. */
.sold-banner {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
  padding: 10px 14px; margin-bottom: 14px; border-radius: var(--r-tile);
  background: #fde8e2; color: #8a2200;
}
.sold-banner strong { font-size: 15px; font-weight: 700; }
.sold-banner span { font-size: 13px; }
.sold-tag {
  display: inline-block; vertical-align: middle; padding: 3px 9px; border-radius: 999px;
  background: #fde8e2; color: #8a2200; font-size: 12px; font-weight: 700;
}
#sold-box .muted { font-size: 14px; }

/* --- /pricing, the hub -----------------------------------------------------------------------
   Built to the measured grammar of edmunds.com/sedan/articles/sedan-buying-guide: H1 40/700 with
   one sentence under it, H2 24/700 per section, and cards that are a 3:2 photograph, a small
   uppercase label and a 16px/700 title -- with no body copy under any of them. That last part is
   the discipline: the whole reference page is 3,597 characters of text, and a card with a
   paragraph on it turns a guide back into a report. */

.pricing-hero h1 { font-size: 40px; line-height: 1.1; font-weight: 700; letter-spacing: -.01em; }
.hero-dek { margin: 10px 0 22px; max-width: 60ch; font-size: 17px; line-height: 1.5; color: var(--g5); }
.pricing-section > h2, .pricing-hero + .card .ask-title, #faq > h2 {
  font-size: 24px; line-height: 1.25; font-weight: 700; color: var(--g1); margin-bottom: 6px;
}
.section-dek { margin: 0 0 18px; max-width: 70ch; font-size: 15px; line-height: 1.5; color: var(--g6); }
.pricing-section > h2 + .pcards, .pricing-section > h2 + .chips { margin-top: 18px; }
.pricing-section, #faq { scroll-margin-top: 88px; }

/* The search. It was a flex row of three unlabelled pill controls that collapsed into each
   other -- a grid with real labels is both legible and the only version that survives a phone. */
.pricing-ask .ask-form {
  display: grid; gap: 12px; margin-top: 16px; align-items: end;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.2fr) minmax(0, .6fr) auto;
}
.ask-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ask-field label { font-size: 13.5px; font-weight: 600; color: var(--g3); }
.ask-field select, .ask-field input {
  width: 100%; height: 46px; padding: 0 14px; border-radius: var(--r-tile);
  border: 1px solid #7e838d; background: var(--white); font: inherit; font-size: 15px;
  color: var(--ink); appearance: none; -webkit-appearance: none;
}
.ask-field select {
  padding-right: 36px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%236c6c6c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 10px 6px;
}
.ask-field select:focus-visible, .ask-field input:focus-visible {
  outline: 2px solid var(--c-primary); outline-offset: 1px; border-color: var(--c-primary);
}
.pricing-ask .btn-accent { height: 46px; padding: 0 24px; border-radius: var(--r-tile); white-space: nowrap; }

/* The card. One component for every row on the page -- budget, brand, model, guide -- because
   the reference page uses one too, and six card shapes is six things to look at before you have
   looked at a car. */
.pcards {
  list-style: none; margin: 0; padding: 0; display: grid; gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
}
.pcard { display: block; color: var(--g1); }
.pcard-media {
  position: relative; display: block; aspect-ratio: 3 / 2; overflow: hidden;
  border-radius: var(--r-media); background: var(--g13);
}
.pcard-media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .35s ease; }
.pcard:hover .pcard-media img { transform: scale(1.04); }
/* No photograph is better than the wrong photograph: a flat field, never a stand-in car. */
.pcard-empty {
  display: block; width: 100%; height: 100%;
  background: linear-gradient(135deg, var(--g13), var(--field));
}
.pcard-badge {
  position: absolute; left: 10px; bottom: 10px; display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: var(--r-tile); background: rgba(255, 255, 255, .94);
  box-shadow: 0 1px 4px rgba(0, 0, 0, .18);
}
.pcard-badge img { width: 26px; height: 26px; object-fit: contain; }
.pcard-body { display: block; padding: 12px 2px 0; }
.pcard-eyebrow {
  display: block; margin-bottom: 4px; font-size: 11.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--g8);
}
.pcard-title {
  display: block; font-size: 16px; line-height: 1.3; font-weight: 700; color: var(--g1);
}
.pcard:hover .pcard-title { color: var(--c-primary); }
.pcard-meta { display: block; margin-top: 4px; font-size: 13.5px; color: var(--g6); }

/* Plain links where a list would otherwise sprawl -- the reference page does exactly this with
   its sedan types, and it is what keeps eleven low-stock brands off the picture grid. */
.chips { list-style: none; margin: 20px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.chips a {
  display: inline-flex; align-items: baseline; gap: 6px; padding: 8px 14px;
  border-radius: var(--r-pill); box-shadow: var(--ring); background: var(--white);
  font-size: 14px; font-weight: 600; color: var(--g2);
}
.chips a span { font-size: 12.5px; font-weight: 400; color: var(--g8); }
.chips a:hover { box-shadow: 0 0 0 1px var(--c-primary); color: var(--c-primary); }
.chips-wide a { border-radius: var(--r-tile); }

/* The arithmetic, in a drawer. Open it and the old block is there unchanged. */
.method-details > summary {
  cursor: pointer; font-size: 16px; font-weight: 700; color: var(--g2); list-style: none;
}
.method-details > summary::-webkit-details-marker { display: none; }
.method-details > summary::before { content: "＋ "; color: var(--g8); }
.method-details[open] > summary::before { content: "－ "; }
.method-details[open] > summary { margin-bottom: 18px; }

@media (max-width: 760px) {
  .pricing-hero h1 { font-size: 30px; }
  .hero-dek { font-size: 16px; }
  .pricing-section > h2, #faq > h2 { font-size: 21px; }
  .pricing-ask .ask-form { grid-template-columns: 1fr 1fr; }
  .pricing-ask .ask-field:first-child { grid-column: 1 / -1; }
  .pricing-ask .btn-accent { grid-column: 1 / -1; width: 100%; }
  .pcards { gap: 16px; }
}

/* --- the blog -------------------------------------------------------------------------------
   These classes existed in `blog_article.html` and `blog_index.html` and had **no rules at all**
   -- `grep -c "blg-" ` returned 0 -- so 762 articles were rendering at browser defaults: 16px
   Times-ish body, 1.2 line-height, no measure, headings running into their paragraphs. On the
   pages carrying citations from twelve Wikipedia articles.

   The reading scale is carspace's (`.post-body`, main.css §blog), which is a measured 1.05rem at
   1.85 and is the right call: a long article wants a taller line than a listing card does. It is
   restated on our own tokens rather than imported, and scoped under `.blg-body` so none of it
   leaks onto a page that happens to render a table. */

.blg-article { max-width: 760px; margin-inline: auto; }
.blg-h1 { font-size: 36px; line-height: 1.18; font-weight: 700; letter-spacing: -.015em; color: var(--g0); }
.blg-byline { margin-top: 12px; font-size: 13.5px; color: var(--g6); }
.blg-hero {
  display: block; width: 100%; max-height: 440px; object-fit: cover;
  border-radius: var(--r-media); margin-bottom: 28px;
}
/* The dek is the article's own one-sentence summary, so it reads as a standfirst rather than as
   a second headline -- which is what an unstyled `h2` directly under an `h1` looks like. */
.blg-dek {
  font-size: 20px; line-height: 1.5; font-weight: 400; color: var(--g5);
  margin: 0 0 28px; padding-left: 16px; border-left: 3px solid var(--c-primary);
}

.blg-body { font-size: 17px; line-height: 1.85; color: var(--g2); }
.blg-body > p { margin: 0 0 1.35em; }
.blg-body h2 { font-size: 25px; line-height: 1.3; font-weight: 700; margin: 2.1em 0 .9em; color: var(--g0); }
.blg-body h3 { font-size: 20px; line-height: 1.35; font-weight: 700; margin: 1.8em 0 .7em; color: var(--g1); }
.blg-body h2:first-child, .blg-body h3:first-child { margin-top: 0; }
.blg-body ul, .blg-body ol { margin: 0 0 1.35em; padding-left: 1.4em; }
.blg-body li { margin-bottom: .5em; line-height: 1.75; }
.blg-body a { color: var(--c-primary); text-decoration: underline; text-decoration-color: var(--c-primary-light); text-underline-offset: 2px; }
.blg-body a:hover { text-decoration-color: var(--c-primary); }
.blg-body strong { font-weight: 700; color: var(--g0); }
.blg-body img {
  display: block; max-width: 100%; height: auto; border-radius: var(--r-media); margin: 1.8em 0;
}
.blg-body figure { margin: 1.8em 0; }
.blg-body figcaption { margin-top: 8px; font-size: 13.5px; color: var(--g6); }
.blg-body blockquote {
  margin: 1.8em 0; padding: 2px 0 2px 20px; border-left: 3px solid var(--c-primary);
  color: var(--g5); font-style: italic;
}
/* A legacy article's table arrives with no classes of its own, so without this it renders as
   unspaced columns running off the measure. Scrollable rather than reflowed: a price table that
   has been turned into a list is no longer a table. */
.blg-body table { width: 100%; margin: 0 0 1.6em; border-collapse: collapse; font-size: 15px; display: block; overflow-x: auto; }
.blg-body th, .blg-body td { padding: 10px 14px; text-align: left; border-bottom: 1px solid var(--g13); }
.blg-body th { font-weight: 700; color: var(--g1); white-space: nowrap; }
.blg-body hr { margin: 2.2em 0; border: 0; border-top: 1px solid var(--g13); }

/* The index. A row of text links was all this had. */
.blg-row { display: flex; gap: 16px; padding: 18px 0; border-top: 1px solid var(--g13); }
.blg-row:first-child { border-top: 0; padding-top: 0; }
.blg-thumb { flex: 0 0 168px; }
.blg-thumb img {
  width: 168px; aspect-ratio: 3 / 2; object-fit: cover; border-radius: var(--r-tile); display: block;
}
.blg-meta { min-width: 0; }
.blg-title { font-size: 18px; line-height: 1.35; font-weight: 700; margin: 0 0 6px; }
.blg-title a { color: var(--g1); }
.blg-title a:hover { color: var(--c-primary); }
.blg-dek-row { margin: 0 0 6px; font-size: 14.5px; line-height: 1.5; color: var(--g6); }

@media (max-width: 760px) {
  .blg-h1 { font-size: 27px; }
  .blg-dek { font-size: 17px; }
  .blg-body { font-size: 16px; }
  .blg-body h2 { font-size: 21px; }
  .blg-body h3 { font-size: 18px; }
  .blg-row { gap: 12px; }
  .blg-thumb { flex: 0 0 110px; }
  .blg-thumb img { width: 110px; }
}

/* The hero banner. The reference page puts a photograph beside its headline rather than under
   it, so the first thing on the page is a car and not a paragraph. Ours is a real listing --
   named, priced and clickable -- which makes the banner a way into the inventory as well as a
   picture. Text and picture side by side above 900px, stacked below it, and the stat row runs
   the full width underneath both. */
.pricing-hero { display: grid; gap: 24px; grid-template-columns: minmax(0, 1fr) minmax(0, 420px); align-items: center; }
.hero-text { min-width: 0; }
.hero-stats { grid-column: 1 / -1; }
.hero-banner { position: relative; display: block; border-radius: var(--r-media); overflow: hidden; background: var(--g13); }
/* `height: auto` is load-bearing: the tag carries width/height attributes so the browser can
   reserve the space before the image arrives, and `aspect-ratio` is ignored outright when both
   dimensions are already specified. Without this the banner rendered at the file's own 765x504
   -- a portrait box in a landscape slot. */
.hero-banner img {
  display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover;
  transition: transform .4s ease;
}
.hero-banner:hover img { transform: scale(1.03); }
/* A gradient rather than a solid bar: the caption has to stay readable over a photograph we
   have never seen, and the plate is often the brightest thing in the lower third. */
.hero-caption {
  position: absolute; inset: auto 0 0 0; padding: 28px 16px 14px; color: #fff;
  background: linear-gradient(to top, rgba(0, 0, 0, .78), rgba(0, 0, 0, 0));
}
.hero-caption strong {
  display: block; font-size: 15px; font-weight: 700; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hero-caption span { font-size: 13px; opacity: .9; }

@media (max-width: 900px) {
  .pricing-hero { grid-template-columns: 1fr; }
  .hero-banner { order: -1; }
}
