
  :root {
    --bg: #fafaf8;
    --ink: #1a1a1a;
    --ink-2: #4a4a4a;
    --ink-3: #8a8a8a;
    --line: #e8e6e1;
    --card: #ffffff;
    --accent: #b45309; /* amber-700 */
    --ok: #15803d;
    --warn: #b45309;
    --info: #6b7280;
  }
  /* ONE CTA element that MORPHS between a bottom bar and a centred card.
     The morph works because both states share `left:50%` + a translate, so only
     `bottom`, `width`, `transform`, `border-radius` and the shadow animate —
     all cheap, all interpolatable. Switching between `bottom:0;left:0;right:0`
     and `top:50%` would not animate at all. */
  #gp-cta { position: fixed; left: 50%; bottom: 0; transform: translate(-50%, 0);
            width: 100%; z-index: 40; background: #fff; border-radius: 0;
            border-top: 1px solid var(--line);
            box-shadow: 0 -6px 24px -8px rgba(0,0,0,.16);
            padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
            /* NO TRANSITION AT ALL BY DEFAULT — i.e. on phones (2026-08-04).
               Two reasons, both learned the hard way. Any POSITIONAL property in
               the list turns iOS moving a fixed element (which it does every
               time the browser toolbar slides) into a 400ms animated wander. And
               animating only the non-positional half is worse than either
               extreme: the box teleports to its new spot at the old width and
               then squeezes, while Google's pixel-width button — redrawn when
               the width transition ENDS — pops a beat later. Instant is
               coherent. The desktop rule below puts the full morph back where
               there is no toolbar to fight. */
            transition: none; }
  #gp-cta.hidden { display: none; }
  /* VISUAL-VIEWPORT PINNING (touch only, and only once JS confirms support).
     iOS does not reposition a fixed element continuously while a scroll gesture
     runs: it lays fixed boxes out against the LAYOUT viewport and re-syncs in
     steps, so a bar anchored to `bottom` steps down the screen as the toolbar
     slides (choppy on a slow scroll) and overshoots then snaps on a fling. With
     `top:0` plus a translate we compute from visualViewport, Safari is no longer
     deciding where this box goes — _gpPinBar() is. */
  #gp-cta.as-bar.gp-vv { top: 0; bottom: auto;
                         transform: translate(-50%, var(--gp-bar-y, 0px));
                         will-change: transform; }
  @keyframes gp-morph-in { from { opacity: .3; } to { opacity: 1; } }
  #gp-cta.gp-morphed { animation: gp-morph-in .18s ease-out; }
  @media (prefers-reduced-motion: reduce) { #gp-cta.gp-morphed { animation: none; } }
  /* THE CARD HANGS FROM THE TOP, NOT THE BOTTOM (Anwar's call, 2026-08-04).
     Any `bottom` anchor is measured from the bottom EDGE of the viewport, and
     that edge is the one thing Safari's toolbar moves — so the box shifted every
     time the toolbar came back, whatever unit the distance was in. `svh` fixed
     the length and changed nothing, because the length was never the problem.
     Measured from the TOP the reference does not move at all.
     `50svh` is "half a viewport down", so it still reads as centred, and svh
     ignores browser chrome so the number itself never changes mid-scroll.
     `top:50%` is the fallback for anything older than iOS 15.4. */
  #gp-cta.as-card { bottom: auto; top: 50%; top: 50svh; transform: translate(-50%, -50%);
            width: min(27rem, calc(100vw - 2rem)); border-radius: 18px;
            border-top: 1px solid var(--line);
            padding: 26px 24px 24px;
            box-shadow: 0 26px 64px -14px rgba(0,0,0,.38); }
  /* POINTER DEVICES GET THE MORPH BACK. A desktop viewport does not grow and
     shrink under the box, so every reason the phone path is instant is absent
     here: `bottom` can be animated safely, and the card can go back to being
     bottom-anchored so the slide interpolates (auto <-> length does not).
     `hover: hover and pointer: fine` is the standard "this is a mouse" test. */
  @media (hover: hover) and (pointer: fine) {
    #gp-cta { transition: bottom .40s cubic-bezier(.4,0,.2,1),
                          width .40s cubic-bezier(.4,0,.2,1),
                          transform .40s cubic-bezier(.4,0,.2,1),
                          border-radius .40s cubic-bezier(.4,0,.2,1),
                          padding .40s cubic-bezier(.4,0,.2,1),
                          box-shadow .40s ease; }
    #gp-cta.as-card { top: auto; bottom: 50%; transform: translate(-50%, 50%); }
  }
  /* Someone with the keyboard open mid-email must not have the box fly across
     the screen; the controller freezes state while the input has focus, and
     this kills any in-flight animation too. */
  @media (prefers-reduced-motion: reduce) { #gp-cta { transition: none; } }

  #gp-cta .gpc-inner { position: relative; max-width: 720px; margin: 0 auto; }
  #gp-cta.as-card .gpc-inner { max-width: none; text-align: center; }
  /* THE CARD MIRRORS THE HOMEPAGE CTA: one centred column, every control the
     same width — pill, then the OR rule, then the email box, then its button.
     `flex-direction: column` is what centres the pill (a plain `justify-content`
     laid the pill, its status line and the OR divider out in a ROW, which is
     what pinned "OR" to the right edge). The 240px is the pill's own width, so
     the column is exactly as wide as the button that heads it. */
  #gp-cta.as-card #smg-gp { display: flex; flex-direction: column; align-items: center; }
  #gp-cta.as-card #smg-gp .smg-or { width: 240px; margin: 14px auto; }
  #gp-cta.as-card #gp-email-form { max-width: 240px; margin: 0 auto; flex-wrap: wrap; }
  #gp-cta.as-card #gp-email,
  #gp-cta.as-card #gp-email-btn { flex: 1 1 100%; }

  .gpc-bar-copy { font-size: .9rem; font-weight: 600; color: var(--ink);
                  line-height: 1.3; margin-bottom: 8px; }
  .gpc-bar-copy span { display: block; font-weight: 400; color: var(--ink-3);
                       font-size: .8rem; margin-top: 2px; }
  .gpc-eyebrow { font-size: .68rem; letter-spacing: .14em; text-transform: uppercase;
                 color: var(--accent); font-weight: 600; margin-bottom: 4px; }
  .gpc-head h2 { font-family: 'Newsreader', Georgia, serif; font-size: 1.5rem;
                 font-weight: 600; margin-bottom: 6px; }
  .gpc-head p { font-size: .875rem; color: var(--ink-2); line-height: 1.5;
                margin-bottom: 16px; }

  #gp-email-form { display: flex; gap: 8px; }
  #gp-email { flex: 1 1 auto; min-width: 0; padding: 10px 12px; font-size: 16px;
              border: 1px solid #d4d1cb; border-radius: 8px; }
  #gp-email-btn { padding: 10px 16px; border-radius: 8px; background: #1a1a1a;
                  color: #fff; font-weight: 600; white-space: nowrap; }
  #gp-cta .gp-msg { font-size: .8rem; margin-top: 8px; min-height: 0; color: var(--ink-2); }
  #gp-cta .gp-ok { color: var(--ok); } #gp-cta .gp-err { color: #b91c1c; }

  #gp-plans { display: grid; gap: 10px; }
  .gpc-plan { position: relative; display: flex; align-items: baseline;
              justify-content: space-between; gap: 12px; width: 100%;
              padding: 14px 16px; border: 1px solid var(--line); border-radius: 12px;
              background: #fff; text-align: left; transition: border-color .15s, box-shadow .15s; }
  .gpc-plan:hover { border-color: #c9c6bf; box-shadow: 0 4px 14px -6px rgba(0,0,0,.14); }
  .gpc-plan.featured { border-color: var(--accent); }
  .gpc-plan .pn { font-weight: 600; font-size: .95rem; }
  .gpc-plan .pp { font-family: 'Newsreader', Georgia, serif; font-size: 1.35rem; font-weight: 600; }
  .gpc-plan .pp s { color: var(--ink-3); font-size: .85rem; font-weight: 400; margin-right: 6px; }
  .gpc-plan .pp i { font-style: normal; font-size: .8rem; color: var(--ink-3); margin-left: 2px; }
  .gpc-plan .pb { position: absolute; top: -9px; left: 16px; background: var(--accent);
                  color: #fff; font-size: .62rem; letter-spacing: .1em; text-transform: uppercase;
                  font-weight: 700; padding: 2px 8px; border-radius: 999px; }

  /* Which content shows, by geometry and by mode. */
  #gp-cta.as-card .gpc-bar-copy { display: none; }
  #gp-cta.as-bar  .gpc-head     { display: none; }
  /* THE BAR IS ONE LINE: [ Continue with Google ] or [ you@email.com ][Continue].
     Stacking the button above the form (which is what the card does) would
     roughly double the height of a strip that sits over the guide the whole way
     down, so here the two options run side by side and the "or" collapses from a
     full-width rule to a single word between them. Giving .smg-btn an explicit
     width is also what SIZES Google's button: the widget measures that element,
     and its ResizeObserver redraws when the CTA morphs bar <-> card. */
  #gp-cta.as-bar .gpc-inner     { display: flex; flex-wrap: wrap; align-items: center; gap: 0 10px; }
  #gp-cta.as-bar .gpc-bar-copy  { flex: 1 1 100%; margin-bottom: 8px; }
  /* ONE PILL WIDTH, EVERY STATE. Google's button is a fixed-pixel render, so any
     state that wants a different width forces a RE-RENDER — and a re-render is
     literally `slot.innerHTML = ''` followed by Google rebuilding the node. That
     is the flash on every morph. Pinning the width here means the button is
     drawn once, on mount, and never touched again by the bar<->card change.
     Pinning it also makes the slot narrower than the card it sits in, so it
     needs auto side margins to sit centred rather than hard left. */
  #gp-cta .smg-btn              { width: 240px; overflow: hidden;
                                  margin-left: auto; margin-right: auto; }
  #gp-cta.as-bar #smg-gp        { display: flex; align-items: center; margin: 0; flex: 0 0 auto; }
  #gp-cta.as-bar #smg-gp .smg-msg { display: none; }
  #gp-cta.as-bar #smg-gp .smg-or  { margin: 0 2px 0 10px; }
  #gp-cta.as-bar #smg-gp .smg-or::before,
  #gp-cta.as-bar #smg-gp .smg-or::after { display: none; }
  #gp-cta.as-bar #gp-email-form { flex: 1 1 240px; }
  #gp-cta.as-bar #gp-email-msg  { flex: 1 1 100%; }
  /* PHONE, in the bar: ONE obvious tap. A 360px screen leaves ~328px, which is
     not enough for the Google pill AND a usable email field, so at rest the pill
     takes the line and email is a small chip beside it. Tapping the chip opens
     the email box on a second row; the pill stays exactly where it was, so
     changing your mind back to Google is one tap, not a hunt. */
  /* The Email control is a PEER of the Google pill, not a tag beside it: same
     42px height, and it takes all the width the pill leaves so the row reads as
     two deliberate choices instead of a button with a scrap next to it. */
  #gp-email-toggle { display: none; flex: 1 1 auto; height: 42px; padding: 0 16px;
                     align-items: center; justify-content: center;
                     border: 1px solid #d4d1cb; border-radius: 999px;
                     background: #fff; color: var(--ink);
                     font-size: .95rem; font-weight: 600; white-space: nowrap; }
  @media (max-width: 560px) {
    /* The pill is NARROWER on a phone so the chip fits beside it: 200 + gap +
       chip is about 300 of the ~328 a 360px screen leaves. At 240 they
       overflowed, the row wrapped, and the bar silently became two rows tall —
       which is what pushed its second row off the bottom of the screen. This is
       a media-query constant, not a per-state value, so the button is still
       drawn exactly once and the morph still never re-renders it. */
    /* One pill width on a phone too (the global 240px), so the card's column and
       the bar's button are the same object and nothing has to be re-rendered
       between them. 240 + 8 gap + the chip still fits a 360px screen, and the
       chip flexes down to whatever is left. */
    #gp-cta.as-bar .gpc-inner         { gap: 0 8px; }
    #gp-cta.as-bar #gp-email-toggle   { padding: 0 12px; min-width: 0; }
    #gp-cta.as-bar #smg-gp .smg-or    { display: none; }
    #gp-cta.as-bar #gp-email-toggle   { display: flex; }
    #gp-cta.as-bar #gp-email-form     { display: none; }
    #gp-cta.as-bar.email-open #gp-email-toggle { display: none; }
    #gp-cta.as-bar.email-open #gp-email-form   { display: flex; flex: 1 1 100%; margin-top: 8px; }
  }
  #gp-cta.mode-signup    [data-for="subscribe"],
  #gp-cta.mode-signup    #gp-plans,
  #gp-cta.mode-signup    #gp-plan-msg { display: none; }
  #gp-cta.mode-subscribe [data-for="signup"],
  #gp-cta.mode-subscribe .gpc-bar-copy,
  #gp-cta.mode-subscribe #gp-email-form,
  #gp-cta.mode-subscribe #smg-gp,
  #gp-cta.mode-subscribe #gp-email-msg { display: none; }
  html, body { font-family: 'Figtree', system-ui, sans-serif; background: var(--bg); color: var(--ink); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
  .serif { font-family: 'Newsreader', Georgia, serif; font-feature-settings: "ss01"; }
  .tile { background: var(--card); border: 1px solid var(--line); transition: transform 120ms ease, box-shadow 120ms ease, border-color 120ms ease; cursor: pointer; }
  .tile:hover { border-color: #c9c6bf; box-shadow: 0 4px 16px -4px rgba(0,0,0,0.08); transform: translateY(-1px); }
  .tile-img { aspect-ratio: 16 / 9; background: linear-gradient(135deg, #2a2a2a 0%, #4a4a4a 100%); color: white; display: flex; align-items: center; justify-content: center; font-family: 'Newsreader', serif; font-weight: 600; letter-spacing: 0.02em; position: relative; overflow: hidden; }
  .tile-img::after { content: ''; position: absolute; inset: 0; background: radial-gradient(circle at 70% 40%, rgba(255,255,255,0.08), transparent 60%); }
  /* Brand-logo make tiles: warm "stone" surface (carries the paper/amber
     palette, not the dark gradient) with a soft top sheen so it reads as
     material, not flat white. The logo is box-fit at 103% so it fills the
     16:9 band and the tallest emblems get a slight top/bottom edge bleed. */
  /* Browse mode toggle: a two-up segmented slider. Both buttons are equal-width
     (flex:1 + a min-width), so the thumb is exactly half the track and moves by
     its own width — no JS measurement, correct at any font size or zoom. */
  .seg {
    position: relative; display: inline-flex; padding: 3px;
    background: #efeae2; border: 1px solid #e4ddd1; border-radius: 999px;
  }
  .seg-btn {
    position: relative; z-index: 1; flex: 1 1 0; min-width: 96px;
    border: 0; background: transparent; cursor: pointer;
    font-size: .82rem; font-weight: 500; color: #6b625a;
    padding: 6px 14px; border-radius: 999px; white-space: nowrap; text-align: center;
    transition: color 200ms cubic-bezier(.3,0,.2,1);
  }
  .seg-btn:hover { color: #3f3a34; }
  .seg-btn.active { color: #1c1917; }
  .seg-btn:focus-visible { outline: 2px solid #b45309; outline-offset: 2px; }
  .seg-thumb {
    position: absolute; top: 3px; bottom: 3px; left: 3px; width: calc(50% - 3px);
    border-radius: 999px; background: #fff;
    box-shadow: 0 1px 2px rgba(28,25,23,.10), 0 1px 6px rgba(28,25,23,.06);
    transition: transform 260ms cubic-bezier(.3,0,.2,1);
  }
  .seg[data-mode="model"] .seg-thumb { transform: translateX(100%); }
  @media (prefers-reduced-motion: reduce) {
    .seg-thumb, .seg-btn { transition: none; }
  }
  /* Brand section divider for the grouped Browse results: the logo + brand name
     sit centred with a hairline running out to both page edges. The rules are
     ::before/::after with flex:1 so they always fill whatever space is left,
     at any width, without measuring anything. */
  .brand-rule { display: flex; align-items: center; gap: 18px; margin: 40px 0 20px; }
  .brand-rule:first-child { margin-top: 8px; }
  .brand-rule::before, .brand-rule::after {
    content: ''; flex: 1 1 0; height: 1px; background: #e4ddd1;
  }
  .brand-rule-inner { display: flex; align-items: center; gap: 11px; flex: 0 0 auto; }
  .brand-rule-logo { height: 30px; width: auto; max-width: 92px; object-fit: contain; display: block; }
  .brand-rule-name { font-family: 'Newsreader', serif; font-weight: 600; font-size: 1.35rem; letter-spacing: -.01em; }
  .brand-rule-count { font-size: .78rem; color: #8a8279; }
  @media (max-width: 640px) {
    .brand-rule { gap: 12px; margin: 30px 0 16px; }
    .brand-rule-logo { height: 24px; max-width: 72px; }
    .brand-rule-name { font-size: 1.15rem; }
  }
  .tile-img-logo { background: linear-gradient(160deg, #efeae2 0%, #e4ddd1 100%); }
  .tile-img-logo::after { background: radial-gradient(120% 90% at 50% 0%, rgba(255,255,255,0.45), transparent 60%); }
  /* Explicit 101% box + object-fit:contain (NOT max-width/height:auto): a
     percentage max-height doesn't resolve inside the flex + aspect-ratio tile on
     mobile Safari, so the logo would size by width alone and over-zoom. A definite
     101% box with contain scales every logo correctly on desktop AND mobile,
     keeping the slight edge bleed. */
  .make-logo { position: relative; z-index: 1; width: 101%; height: 101%; object-fit: contain; object-position: center; display: block; }
  /* Model/generation tile car photo: the pre-cropped 16:9 press shot fills the
     tile (object-fit cover). Source art is already center-cropped to 16:9 by
     tools/model_photos, so the car sits in-frame and the sky/road is trimmed. */
  .tile-img-photo { background: #14110e; padding: 0; }
  .tile-img-photo::after { display: none; }
  .model-photo { position: relative; z-index: 1; width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
  /* Style B treatment, applied to model tiles and guide hero. In production this class sits on top of an <img> of the car; here it's gradient-only as an approximation. */
  .tile-img-duotone {
    background:
      radial-gradient(ellipse 70% 90% at 72% 48%, rgba(217,119,6,0.45) 0%, transparent 55%),
      radial-gradient(ellipse 90% 100% at 15% 80%, rgba(10,10,10,0.7) 0%, transparent 60%),
      linear-gradient(165deg, #1a1a1a 0%, #2a2018 60%, #3a2410 100%) !important;
    color: #f5f5f0;
  }
  /* Insights tile PHOTO (2026-09-29): the article's own photo, full colour, no
     overlay. His rulings the same day: sepia tone off, headline numbers off. */
  .tile-img-insight { background: #14110e; padding: 0; }
  .tile-img-insight::after { display: none; }
  .tile-img-insight img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 60%; }
  .tile-img-duotone::after {
    background:
      radial-gradient(ellipse 30% 40% at 85% 30%, rgba(255,220,180,0.22) 0%, transparent 60%),
      repeating-linear-gradient(0deg, transparent 0, transparent 2px, rgba(255,255,255,0.012) 2px, rgba(255,255,255,0.012) 3px);
  }
  .badge { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.15rem 0.5rem; border-radius: 999px; font-size: 0.7rem; font-weight: 500; letter-spacing: 0.02em; }
  .badge-ok { background: #dcfce7; color: #14532d; }
  .badge-warn { background: #fef3c7; color: #78350f; }
  .badge-info { background: #e5e7eb; color: #374151; }
  .badge-bad { background: #fee2e2; color: #7f1d1d; }
  .val-locked { border:1px solid #e5e7eb; border-radius:12px; padding:2rem;
                background:#fff; }
  .val-lock-btn { display:inline-flex; align-items:center; padding:.6rem 1.1rem;
                  border:1px solid #e5e7eb; border-radius:8px; font-size:.95rem;
                  font-weight:500; color:#1a1a1a; text-decoration:none; background:#fafaf8; }
  .val-lock-btn:hover { border-color:#d6d3ce; background:#fff; }
  .val-lock-btn.primary { background:#1a1a1a; color:#fff; border-color:#1a1a1a; }
  .val-lock-btn.primary:hover { background:#333; }
  .cta-guide-link { display:inline-flex; align-items:center; padding:.5rem .9rem;
                    border:1px solid #e5e7eb; border-radius:8px; font-size:.9rem;
                    font-weight:500; color:#1a1a1a; text-decoration:none;
                    background:#fafaf8; }
  .cta-guide-link:hover { border-color:#d6d3ce; background:#fff; }
  .trend-up { color: var(--ok); }
  .trend-down { color: #b91c1c; }
  .trend-flat { color: var(--ink-3); }
  .crumb a { color: var(--ink-3); }
  .crumb a:hover { color: var(--ink); text-decoration: underline; }
  /* Sticky breadcrumb, the buyer's guide is a long page; the
     breadcrumb should follow the user down so they can navigate back
     without scrolling to the top of the document. Stuck BELOW the
     top header (which is itself sticky at top:0 with z-40), so the
     crumb top is offset by the header height (~3.5rem) and its z-index
     is lower than the header so it slides cleanly under, not over. */
  .crumb-sticky {
    position: sticky;
    top: 4rem;   /* sits right under the 64px header; 56px on mobile (below) */
    z-index: 20;
    background: var(--bg);
    padding-top: 0.75rem;
    padding-bottom: 0.75rem;
    margin-left: -1.5rem;
    margin-right: -1.5rem;
    padding-left: 1.5rem;
    padding-right: 1.5rem;
    border-bottom: 1px solid var(--line);
    margin-bottom: 1.5rem;
  }
  /* The sticky ON-THIS-PAGE aside needs its own internal scroll when
     the TOC is taller than the viewport (otherwise items below the fold
     are unreachable). Bounding the container's height to viewport
     minus the sticky offset (top-20 = 5rem) gives the inner overflow
     room to scroll. */
  .toc-sticky-frame {
    max-height: calc(100vh - 6rem);
    overflow-y: auto;
    padding-right: 0.5rem;
  }
  /* Defensive: ensure body always scrolls (some layouts inside the
     guide briefly bind wheel events; the body should always handle
     overscroll). */
  html, body { overflow-anchor: none; }
  body { overflow-y: auto; }
  .divider { border-top: 1px solid var(--line); }
  .nav-link { color: var(--ink-2); font-size: 0.875rem; }
  .nav-link:hover { color: var(--ink); }
  .nav-link.active { color: var(--ink); font-weight: 500; }
  /* Guide two-column layout: minmax(0,·) lets the content column shrink to the
     viewport on mobile instead of being forced wide by tables/rows/charts. */
  #guide-grid { grid-template-columns: minmax(0, 1fr); }
  @media (min-width: 768px) { #guide-grid { grid-template-columns: minmax(0, 1fr) 200px; } }
  #guide-content { min-width: 0; }
  /* Mobile: keep the guide from overflowing the viewport. Charts drop their
     desktop min-width, and the driver/trim rows shrink their fixed-width label
     and value columns (w-52/w-64/w-32) so the whole row fits a phone. */
  @media (max-width: 640px) {
    .chart-fit { min-width: 0 !important; }
    .driver-row { gap: 8px; }
    .driver-row .driver-label { width: 120px; }
    .driver-row .driver-value { width: 68px; }
    /* Snapshot scatters: drop the fixed 36rem height so the taller mobile
       viewBox fills the width (bigger chart) instead of being letterboxed. */
    #snapshot .chart-mock { height: auto !important; padding: 14px !important; }
    #snapshot .chart-mock > svg { height: auto !important; }
    /* Brand "prices over time" legend: two columns below the chart, not one long
       list (the entries keep their hover-highlight handlers). */
    #brand-chart-legend { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 14px; }
    /* Sticky breadcrumb sits under the 56px mobile header (not the 64px desktop one). */
    .crumb-sticky { top: 3.5rem; }
  }
  .toc-link { color: var(--ink-2); font-size: 0.8rem; padding: 0.3rem 0; border-left: 2px solid transparent; padding-left: 0.75rem; margin-left: -0.75rem; display: block; }
  .toc-link:hover { color: var(--ink); border-left-color: var(--line); }
  .toc-link.active { color: var(--ink); border-left-color: var(--ink); font-weight: 500; }
  /* No CSS background gridlines, the SVG draws its own gridlines at
     the actual yTick positions, so they always align with the data.
     A fixed-pitch CSS gradient (the old `repeating-linear-gradient`
     at 21px) couldn't line up with the SVG's logical-coord ticks
     across viewport sizes, so it just looked like noise. */
  .chart-mock { background: #ffffff; border: 1px solid var(--line); border-radius: 6px; }
  .hbar { height: 18px; border-radius: 3px; }
  .persona-card { border-left: 3px solid var(--ink); }
  /* --- Chart hover interactions ---
     The scatter dots fade smoothly between full opacity and the dim
     state used when a year legend entry is hovered (was an abrupt
     attribute swap).
     The HTML bar charts (drivers, trims) get a soft expand + slight
     saturation lift when the row is hovered.
     The SVG box chart (price by year) bar + tick + label scale up
     together so the whole row reads as the focus. */
  .scatter-dot,
  .scatter-dot-date {
    transition: opacity 240ms cubic-bezier(0.4, 0, 0.2, 1);
  }
  /* Both scatter trendlines are <line> elements whose endpoints get
     re-fitted (OLS) when the user hovers a legend year. Modern
     browsers transition the x1/y1/x2/y2 attributes when set via CSS,
     so the slope/position morphs smoothly between fits. Opacity
     fades the line out for years with too few points to fit. */
  .scatter-trendline {
    transition: x1 240ms cubic-bezier(0.3, 0, 0.2, 1),
                y1 240ms cubic-bezier(0.3, 0, 0.2, 1),
                x2 240ms cubic-bezier(0.3, 0, 0.2, 1),
                y2 240ms cubic-bezier(0.3, 0, 0.2, 1),
                opacity 240ms cubic-bezier(0.3, 0, 0.2, 1);
  }
  /* Slope readout that rides the right end of each scatter's
     trendline. Same amber as the line, Inter semibold to match the
     site's body type, white halo via paint-order so it stays legible
     when it lands over data dots. The x/y attributes transition in
     sync with the trendline endpoints so it slides smoothly when the
     legend hover re-fits the line. */
  .scatter-slope-label {
    font-family: 'Figtree', system-ui, sans-serif;
    font-weight: 700;
    font-size: 18px;
    pointer-events: none;
    /* Fill is set inline per-fit so it can swap between green
       (positive slope) and red (negative slope), same palette
       the comp-row % Diff badges use. Label sits in the right-side
       gutter OUTSIDE the plot area, so no halo / paint-order needed
       (no data dots underneath). */
    transition: x 240ms cubic-bezier(0.3, 0, 0.2, 1),
                y 240ms cubic-bezier(0.3, 0, 0.2, 1),
                opacity 240ms cubic-bezier(0.3, 0, 0.2, 1),
                fill 240ms cubic-bezier(0.3, 0, 0.2, 1);
  }
  /* Sale-price-vs-mileage year legend: hovered entry's text grows and
     darkens to call out the active year. transform-box: fill-box keeps
     the right-anchored text growing leftward from its anchor point so
     the visual layout doesn't shift. */
  .scatter-legend-year {
    font-size: 17px;
    fill: #374151;
    transform-box: fill-box;
    transform-origin: 100% 50%;
    transition: font-size 220ms cubic-bezier(0.3, 0, 0.2, 1),
                font-weight 220ms cubic-bezier(0.3, 0, 0.2, 1),
                fill 220ms cubic-bezier(0.3, 0, 0.2, 1);
  }
  .scatter-legend-year.is-hot {
    font-size: 22px;
    font-weight: 600;
    fill: #1a1a1a;
  }
  .driver-row {
    /* The wrapper is what receives the hover state, the inner bar
       AND all the metadata (label, %/$ value, p-value/install-rate)
       lift together so the whole row reads as one focused unit. */
    cursor: default;
    border-radius: 6px;
    padding: 4px 6px;
    margin-left: -6px;
    margin-right: -6px;
    transition: background-color 220ms ease, transform 220ms cubic-bezier(0.3, 0, 0.2, 1);
  }
  .driver-row:hover {
    background-color: #f5efe3;   /* soft warm tint matching the page palette */
    transform: translateY(-1px);
  }
  .driver-row .driver-bar {
    transform-origin: center;
    transition: transform 220ms cubic-bezier(0.3, 0, 0.2, 1),
                filter    220ms ease,
                box-shadow 220ms ease;
    will-change: transform;
  }
  .driver-row:hover .driver-bar {
    transform: scaleY(1.55);
    filter: brightness(1.05) saturate(1.15);
    box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.18);
  }
  .driver-row .driver-label,
  .driver-row .driver-value,
  .driver-row .driver-meta {
    transition: color 220ms ease, font-weight 220ms ease,
                transform 220ms cubic-bezier(0.3, 0, 0.2, 1),
                opacity 220ms ease;
    transform-origin: right center;
  }
  /* p-value + "% of listings" meta, hidden by default, fades in only
     when the row is hovered, so the chart at rest reads as: label · bar
     · headline %/$ value (no secondary text noise). The meta becomes
     the "explanation" you see on demand. */
  .driver-row .driver-meta {
    opacity: 0;
  }
  .driver-row:hover .driver-label {
    color: var(--ink);
    transform: scale(1.04);
  }
  .driver-row:hover .driver-value {
    transform: scale(1.06);
    filter: brightness(0.9);
  }
  .driver-row:hover .driver-meta {
    opacity: 1;
    color: var(--ink-2);
    transform: scale(1.03);
  }
  /* SVG box chart (Price by Year), each row is wrapped in <g class="pby-row">.
     transform-box: fill-box makes transform-origin: center reference the
     element's OWN bounding box rather than the SVG viewport, so the
     bar grows in place. */
  .pby-row .pby-bar,
  .pby-row .pby-tick,
  .pby-row .pby-label-med {
    transform-box: fill-box;
    transform-origin: center;
    transition: transform 220ms cubic-bezier(0.3, 0, 0.2, 1),
                filter    220ms ease;
  }
  .pby-row:hover .pby-bar {
    transform: scaleY(1.35);
    filter: brightness(1.04);
  }
  .pby-row:hover .pby-tick {
    transform: scaleY(1.2);
  }
  .pby-row:hover .pby-label-med {
    transform: scale(1.08);
  }
  /* P12.5 / P87.5 dollar labels at the bar ends, hidden by default,
     fade in only on row hover. Position is anchored to the bar's left
     / right edge in the renderer. */
  .pby-row .pby-label-low,
  .pby-row .pby-label-high {
    opacity: 0;
    transition: opacity 200ms ease;
  }
  .pby-row:hover .pby-label-low,
  .pby-row:hover .pby-label-high {
    opacity: 1;
  }
  /* The n=NNN label fades down during hover so the P87.5 reveal label
     sitting just to its left becomes the visual focus. */
  .pby-row .pby-label-n {
    transition: opacity 200ms ease;
  }
  .pby-row:hover .pby-label-n {
    opacity: 0.3;
  }
  .comp-row { border-bottom: 1px solid var(--line); }
  .comp-row:last-child { border-bottom: none; }
  /* Best / Worst color signal lives ONLY on the inline Deal badge
     (rendered with bg-green-100 / bg-red-100 in the row template).
     The row itself has no persistent tint, but it does bubble on
     hover with the same soft lift/shadow as the driver bars and
     persona cards. */
  .comp-row {
    transition: background-color 220ms ease,
                transform 220ms cubic-bezier(0.3, 0, 0.2, 1),
                box-shadow 220ms ease;
  }
  /* Hover signal is JUST a soft background-color tint, no transform
     or shadow. A 1px translateY lift can push the cursor across the
     1px row border into the next row, causing the two rows to bounce
     between hover states; background-only is jitter-free. */
  .comp-row:hover {
    background-color: #faf5e9;
  }
  /* Deal cell. The hover reveal that used to fade an auction-house name over
     this cell is gone — Statomotive doesn't attribute venues (the payload no
     longer carries them at all). The badge is now a plain static cell. */
  .comp-row-deal-cell {
    position: relative;
  }
  .comp-row-deal-badge {
    display: inline-block;
  }
  /* Persona-card hover bubble, each of the Four ways gets the same
     soft lift, background tint, and shadow treatment as the comps /
     driver rows so the interaction feels consistent across the page. */
  .persona-card {
    border-radius: 6px;
    padding: 12px 14px;
    margin-left: -14px;
    margin-right: -14px;
    transition: background-color 220ms ease,
                transform 220ms cubic-bezier(0.3, 0, 0.2, 1),
                box-shadow 220ms ease;
  }
  .persona-card:hover {
    background-color: #f5efe3;
    transform: translateY(-2px);
    box-shadow: 0 4px 14px -4px rgba(0, 0, 0, 0.14);
  }
  .excerpt { border-left: 2px solid var(--line); padding-left: 1rem; color: var(--ink-2); }
  .cta-note { font-size: 0.75rem; color: var(--ink-3); }
  /* scroll padding for anchors */
  main { scroll-padding-top: 88px; }
  section[id] { scroll-margin-top: 88px; }
  .header-shadow { box-shadow: 0 1px 0 var(--line); }
  /* Brand wordmark shape */
  /* Wordmark, must stay identical to .mark in homepage.html */
  .otto-mark { font-family: 'Fraunces', serif; font-weight: 600; letter-spacing: -0.015em; font-size: 1.3rem; }
  .otto-mark::after { content: '.'; margin: 0; color: var(--accent); }
  /* Brand mark (bar-chart glyph) sits left of the wordmark, keep identical across pages */
  .mark-logo { height: 1.45em; width: auto; margin-right: 0.4em; flex: none; }
  /* Mini sparkline */
  .spark { width: 60px; height: 20px; vertical-align: middle; }
  /* Search box */
  .search-box { position: relative; }
  .search-box input {
    width: 100%;
    padding: 0.55rem 0.75rem 0.55rem 2.25rem;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: white;
    font-size: 0.9rem;
    color: var(--ink);
    outline: none;
    font-family: inherit;
    transition: border-color 120ms ease, box-shadow 120ms ease;
  }
  .search-box input:focus { border-color: #8a8a8a; box-shadow: 0 0 0 3px rgba(0,0,0,0.04); }
  .search-box svg { position: absolute; left: 0.75rem; top: 50%; transform: translateY(-50%); color: var(--ink-3); pointer-events: none; }
  .search-box .clear-btn { position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); background: none; border: 0; color: var(--ink-3); cursor: pointer; padding: 0.25rem; font-size: 1rem; line-height: 1; border-radius: 4px; }
  .search-box .clear-btn:hover { color: var(--ink); background: var(--bg); }
  .empty-state { color: var(--ink-3); padding: 3rem 1rem; text-align: center; font-size: 0.9rem; border: 1px dashed var(--line); border-radius: 8px; }
  .highlight { background: #fef3c7; color: var(--ink); border-radius: 2px; padding: 0 1px; }
  /* Brand-overview chart: smooth dim when a legend model is hovered (same
     feel as the sale-price scatter's year legend hover-dim). */
  .brand-line, .brand-dot, .brand-leg { transition: opacity 220ms cubic-bezier(0.3, 0, 0.2, 1); }
  /* Chips */
  .chip {
    display: inline-flex;
    align-items: center;
    padding: 0.4rem 0.85rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-size: 0.8rem;
    color: var(--ink-2);
    background: white;
    cursor: pointer;
    transition: all 120ms ease;
    white-space: nowrap;
    font-family: inherit;
  }
  .chip:hover { border-color: var(--ink-3); color: var(--ink); }
  .chip.active { background: var(--ink); color: white; border-color: var(--ink); }
  /* ---- Compare view ---- */
  .compare-slot { border: 1px solid var(--line); border-radius: 8px; padding: 1rem; background: white; min-height: 180px; display: flex; flex-direction: column; }
  .compare-slot.filled { border-color: #d6d3ce; }
  .compare-slot.empty { border-style: dashed; justify-content: center; align-items: center; gap: 0.5rem; color: var(--ink-3); }
  .compare-slot select { width: 100%; font-family: inherit; font-size: 0.85rem; border: 1px solid var(--line); border-radius: 4px; padding: 0.4rem 0.5rem; background: white; color: var(--ink); cursor: pointer; }
  .compare-slot .slot-title { font-family: 'Newsreader', serif; font-weight: 600; font-size: 1.05rem; line-height: 1.2; }
  .compare-slot .slot-years { font-size: 0.75rem; color: var(--ink-3); margin-top: 0.1rem; }
  .compare-slot .slot-remove { font-size: 0.7rem; color: var(--ink-3); text-decoration: underline dotted; align-self: flex-end; cursor: pointer; }
  .compare-slot .slot-remove:hover { color: var(--ink); }
  .compare-table { display: grid; grid-template-columns: minmax(130px, 180px) repeat(auto-fit, minmax(0, 1fr)); border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: white; }
  .compare-table > div { padding: 0.75rem 1rem; border-bottom: 1px solid var(--line); font-size: 0.85rem; }
  .compare-table > div.label-cell { background: #f8f7f3; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.7rem; font-weight: 500; display: flex; align-items: center; }
  .compare-table > div.winner { background: #f5f7f0; }
  .compare-table > div.winner::before { content: '●'; color: #65a30d; margin-right: 0.45rem; font-size: 0.7rem; }
  .compare-table .row-last > div { border-bottom: none; }
  .compare-bar-row { display: grid; grid-template-columns: 180px 1fr 100px; align-items: center; gap: 1rem; }
  .compare-bar { height: 14px; background: #111; border-radius: 2px; min-width: 2px; }
  .compare-bar.dim { background: #9ca3af; }
  /* ---- Chat view ---- */
  .chat-bubble { max-width: 85%; padding: 1rem 1.1rem; border-radius: 10px; line-height: 1.55; font-size: 0.92rem; }
  .chat-user { background: #111; color: #fafaf8; border-bottom-right-radius: 3px; }
  .chat-bot { background: white; border: 1px solid var(--line); color: var(--ink); border-bottom-left-radius: 3px; }
  .chat-bot p, .chat-bot ul { font-size: 0.92rem; }
  .chat-input-wrap { display: flex; gap: 0.6rem; align-items: flex-end; padding: 0.7rem; background: white; border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 4px 14px rgba(0,0,0,0.04); }
  .chat-input-wrap textarea { flex: 1; border: none; outline: none; resize: none; font-family: inherit; font-size: 0.92rem; padding: 0.4rem 0.5rem; background: transparent; color: var(--ink); max-height: 140px; }
  .chat-input-wrap textarea::placeholder { color: var(--ink-3); }
  .chat-send { background: var(--ink); color: white; border: none; border-radius: 6px; padding: 0.55rem 1rem; font-family: inherit; font-size: 0.85rem; cursor: pointer; font-weight: 500; }
  .chat-send:disabled { background: #d1d5db; cursor: not-allowed; }

  /* ===== Value a car (configurator) ===== */
  .val-grid { display: grid; grid-template-columns: 1fr 340px; gap: 32px; align-items: start; }
  @media (max-width: 820px) { .val-grid { grid-template-columns: 1fr; } }
  .val-config { min-width: 0; }
  .val-field { margin-bottom: 22px; }
  .val-field > .val-label { display: block; font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3); font-weight: 600; margin-bottom: 9px; }
  .val-field > .val-label .val-sub { text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--ink-3); }
  /* pill selectors (single-select: year, trim, transmission, body) */
  .val-pills { display: flex; flex-wrap: wrap; gap: 7px; }
  .val-pill { font: inherit; font-size: 0.85rem; padding: 7px 14px; border: 1px solid var(--line); background: var(--paper); color: var(--ink-2); border-radius: 999px; cursor: pointer; transition: all .12s ease; white-space: nowrap; }
  .val-pill:hover { border-color: var(--ink-3); color: var(--ink); }
  .val-pill.on { background: var(--ink); border-color: var(--ink); color: #fff; }
  /* selects (colors) */
  .val-select { width: 100%; max-width: 420px; font: inherit; font-size: 0.92rem; padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--paper); color: var(--ink); cursor: pointer; }
  .val-select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft, rgba(180,83,9,.12)); }
  /* mileage: number + range */
  .val-mile { display: flex; align-items: center; gap: 14px; max-width: 420px; }
  .val-mile input[type=number] { width: 130px; font: inherit; font-size: 0.95rem; padding: 9px 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--paper); color: var(--ink); }
  .val-mile input[type=range] { flex: 1; accent-color: var(--accent); }
  /* options checklist */
  .val-opts { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 8px 16px; }
  .val-opt { display: flex; align-items: flex-start; gap: 8px; font-size: 0.9rem; color: var(--ink); cursor: pointer; padding: 3px 0; }
  .val-opt input { margin-top: 2px; accent-color: var(--accent); width: 15px; height: 15px; flex: none; cursor: pointer; }
  .val-opt.pkg .val-opt-name::after { content: 'Package'; margin-left: 6px; font-size: 0.62rem; letter-spacing: .06em; text-transform: uppercase; color: var(--accent); border: 1px solid var(--accent-soft, #f0d9c4); border-radius: 999px; padding: 1px 6px; }
  .val-opt .val-opt-name { flex: 1; }
  .val-opt-usd { flex: none; font-size: 0.8rem; font-variant-numeric: tabular-nums; color: #17795e; white-space: nowrap; }
  .val-opt-usd.neg { color: #b23b3b; }
  .val-opt-usd.est { opacity: 0.8; }
  /* result card (sticky) */
  .val-result { position: sticky; top: 84px; }
  .val-card { background: var(--paper); border: 1px solid var(--line); border-radius: 16px; padding: 24px; box-shadow: 0 1px 2px rgba(0,0,0,.03); }
  .val-card-cap { font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); font-weight: 600; }
  .val-carname { font-family: 'Newsreader', serif; font-weight: 600; font-size: 1.15rem; color: var(--ink); margin: 3px 0 16px; line-height: 1.2; }
  .val-estimate { font-family: 'Newsreader', serif; font-weight: 600; font-size: 2.8rem; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; transition: opacity .12s; }
  .val-estimate.loading { opacity: 0.4; }
  .val-rbar { position: relative; height: 8px; background: var(--line); border-radius: 999px; margin: 18px 0 9px; }
  .val-rbar-band { position: absolute; top: 0; bottom: 0; background: linear-gradient(90deg, #d97706, #b45309); border-radius: 999px; transition: all .25s ease; }
  .val-rbar-mark { position: absolute; top: -4px; bottom: -4px; width: 3px; background: var(--ink); border-radius: 2px; transform: translateX(-50%); transition: left .25s ease; }
  .val-range { font-size: 0.86rem; color: var(--ink-2); }
  .val-range b { color: var(--ink); font-variant-numeric: tabular-nums; }
  .today-tag { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600; font-style: normal;
    color: var(--ink-2, #374151); border: 1px solid var(--line, #d1d5db); border-radius: 999px; padding: 1px 6px; margin-left: 4px; white-space: nowrap; cursor: help; }
  .val-note { font-size: 0.78rem; color: var(--ink-3); margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); line-height: 1.5; }
  .val-empty { color: var(--ink-3); font-size: 0.92rem; padding: 40px 0; text-align: center; }
