    /* Brand tokens. brand/tokens.json is the source of truth and
       brand/build.mjs re-audits every text pairing here against WCAG AA on
       each run, exiting non-zero on a regression. */
    :root {
      /* Core */
      --ink: #12233a;          /* primary text, dark surfaces, the wordmark */
      --ink-deep: #0c1a2c;     /* footer, dark tile, social card */
      --blue: #2f6bed;         /* fills, the mark, borders, active state */
      --blue-text: #1659eb;    /* blue at body size, or on any tint */
      --blue-deep: #1b47b8;    /* hover and pressed */
      --slate: #45587a;        /* secondary text */
      --mist: #657795;         /* tertiary text, captions, disabled */
      --line: #d4e0f1;
      --line-soft: rgba(37, 78, 138, .13);
      --wash: #eaf1fb;         /* tinted panel, table stripe */
      --foam: #f4f8fd;         /* page background */
      --white: #ffffff;
      --panel: #ffffff;
      --panel-2: #eef3fb;

      /* Data states. These carry meaning in the fit model — never decorative. */
      --fit: #1a7e4d;        --fit-tint: #e7f6ec;
      --amber: #c68f22;      --amber-tint: #fdf3dc;   --amber-text: #916819;
      --out: #b83f2b;        --coral-tint: #ffe7e1;
      --blue-tint: #d7e5fd;
      --coral: #ec6a54;      /* illustration and fills only, never text */

      --shadow: 0 24px 60px rgba(23, 54, 110, .14);
      --card-ratio: 880 / 200;
      --radius: 18px;
      --radius-sm: 10px;

      /* Type. Archivo carries wght 100–900 and wdth 62–125, so one family
         gives both the tracked-caps register voice and normal-width craft
         names; Inter takes everything read as running text or table data. */
      --display: "Archivo", "Inter", system-ui, sans-serif;
      --body: "Inter", system-ui, sans-serif;
      --track-label: .14em;
      --track-micro: .1em;
    }

    * { box-sizing: border-box; }
    html { scroll-behavior: smooth; }
    /* Height of the fixed beta strip. Everything that has to clear it reads this. */
    :root { --beta-h: 26px; }
    body {
      margin: 0;
      min-width: 320px;
      min-height: 100vh;
      color: var(--ink);
      background:
        radial-gradient(circle at 88% 2%, rgba(47, 107, 237, .10), transparent 30rem),
        linear-gradient(150deg, #eef4fc 0%, #e7eefb 48%, #f4f8fd 100%);
      font-family: var(--body);
      font-size: 15px;
      line-height: 1.55;
      /* The whole page is a table of numbers — weights, beams, prices, bands.
         Proportional digits make every one of those columns jitter. */
      font-variant-numeric: tabular-nums;
      /* Room at the foot of every page for the fixed beta strip. */
      padding-bottom: calc(var(--beta-h) + env(safe-area-inset-bottom, 0px));
    }
    body.modal-open { overflow: hidden; }
    button, input, select { font: inherit; }
    button, a { -webkit-tap-highlight-color: transparent; }
    button { color: inherit; }
    a { color: var(--blue-text); }
    a:hover { color: var(--blue-deep); }
    :focus-visible { outline: 3px solid rgba(47, 107, 237, .55); outline-offset: 3px; }
    ::selection { background: var(--blue); color: #fff; }

    .app-header {
      position: sticky;
      z-index: 60;
      top: 0;
      display: grid;
      grid-template-columns: auto minmax(0, 1fr) auto;
      align-items: center;
      gap: 24px;
      padding: 13px clamp(20px, 3vw, 44px);
      border-bottom: 1px solid var(--line-soft);
      background: rgba(244, 248, 253, .82);
      backdrop-filter: blur(18px);
    }
    .skip-link {
      position: absolute; left: 12px; top: -60px; z-index: 200;
      padding: 10px 16px; border-radius: 10px;
      background: var(--blue); color: #fff; font-weight: 600; text-decoration: none;
      transition: top .16s ease;
    }
    .skip-link:focus { top: 12px; }
    .sr-only {
      position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
      overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
    }
    /* The logo is the menu control. Below 1050px the nav collapses behind the
       mark and the mark swaps to its open state — two hulls crossing. A
       separate hamburger never appears beside the logo. Both states are the
       built assets, so they cannot drift from brand/build.mjs. */
    .brand-lockup {
      display: flex; align-items: center; gap: 12px; min-width: max-content;
      padding: 4px; margin: -4px;
      color: inherit; text-decoration: none; border-radius: 14px;
    }
    .brand-mark {
      display: block; width: 38px; height: 38px; flex: none;
      border-radius: 11px;
      background: var(--blue) url("/brand/mark-white.svg") center / 100% no-repeat;
      transition: background-color .18s ease;
    }
    .app-header.menu-open .brand-mark { background-image: url("/brand/mark-close-white.svg"); }
    .brand-lockup:hover .brand-mark { background-color: var(--blue-deep); }
    .brand-words { display: block; text-align: left; }
    .brand-name {
      display: block;
      font-family: var(--display); font-size: 15px; font-weight: 600;
      text-transform: uppercase; letter-spacing: .08em;
    }
    .brand-name i { font-style: normal; color: var(--blue-text); }
    .brand-sub {
      display: block; margin-top: 1px; color: var(--mist);
      font-family: var(--display); font-size: 9.5px; font-weight: 600; font-stretch: 92%;
      text-transform: uppercase; letter-spacing: var(--track-micro);
    }
    .header-tools { display: flex; align-items: center; gap: 10px; min-width: max-content; }
    .price .approx { margin-right: 3px; color: var(--mist); font-style: normal; font-weight: 500; }
    .currency-picker select {
      padding: 7px 10px;
      border: 1px solid var(--line);
      border-radius: 10px;
      color: var(--ink);
      background: var(--panel);
      font-size: 13px;
      font-weight: 600;
      cursor: pointer;
    }
    .currency-picker select:hover { border-color: var(--blue); }

    .site-footer { border-top: 1px solid var(--line-soft); background: rgba(255,255,255,.6); }
    .footer-inner {
      display: grid; gap: 30px;
      grid-template-columns: minmax(0, 1.1fr) minmax(0, 2fr);
      width: min(1460px, 100%); margin: 0 auto;
      padding: 40px clamp(20px, 4vw, 58px) 52px;
    }
    .footer-brand p { max-width: 34ch; margin: 8px 0 0; }
    .footer-cols { display: grid; gap: 26px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
    .footer-col h4 { margin: 0 0 8px; font-size: 12px; text-transform: uppercase; letter-spacing: .12em; color: var(--mist); }
    .footer-col p, .footer-col li { margin: 0 0 5px; color: var(--slate); font-size: 13px; }
    .footer-col ul { margin: 0; padding: 0; list-style: none; }
    /* One line, every page, pinned to the foot of the window rather than to the
       end of the document: the caveat has to be readable while someone is looking
       at a number, not only after they scroll past everything. Kept to a single
       26px row of small type — permanent, not prominent. */
    .footer-beta {
      position: fixed;
      z-index: 70;
      right: 0;
      bottom: 0;
      left: 0;
      margin: 0;
      padding: 4px clamp(12px, 3vw, 44px) calc(4px + env(safe-area-inset-bottom, 0px));
      border-top: 1px solid var(--line-soft);
      background: rgba(244, 248, 253, .88);
      backdrop-filter: blur(18px);
      color: var(--mist);
      font-size: 11px;
      line-height: 1.45;
      text-align: center;
    }
    .footer-beta a { color: var(--blue-text); }
    /* The tag's box is what sets the strip's height, so its line box is capped
       rather than left to grow on its own padding. */
    .beta-tag { display: inline-block; margin-right: 6px; padding: 1px 6px; border: 1px solid rgba(198,143,34,.42); border-radius: 999px; background: var(--amber-tint); color: var(--amber-text); font-family: var(--display); font-size: 9px; font-weight: 700; line-height: 1.35; text-transform: uppercase; letter-spacing: .09em; vertical-align: 1px; }
    /* Narrow screens take the short wording and drop the price date, so the
       strip stays one line instead of wrapping to two. */
    .beta-short { display: none; }
    @media (max-width: 760px) {
      .beta-long, .beta-prices { display: none; }
      .beta-short { display: inline; }
    }
    /* At the 320px floor the short line still runs 10px wider than the window
       at 11px type, so the smallest phones drop a size rather than wrap. */
    @media (max-width: 400px) {
      .footer-beta { font-size: 10px; }
    }
    @media (max-width: 760px) { .footer-inner { grid-template-columns: 1fr; } }
    .top-nav { display: flex; justify-content: center; gap: 4px; overflow-x: auto; scrollbar-width: none; }
    .top-nav::-webkit-scrollbar { display: none; }
    /* A register's nav, not an app's: labelled in the display face, marked
       active with a rule rather than a filled pill. */
    .nav-btn {
      position: relative;
      min-width: max-content;
      padding: 10px 11px 9px;
      border: 0;
      color: var(--slate);
      background: transparent;
      font-family: var(--display); font-size: 11.5px; font-weight: 600; font-stretch: 92%;
      text-transform: uppercase; letter-spacing: var(--track-micro);
      cursor: pointer;
      transition: color .18s ease;
    }
    .nav-btn::after {
      content: "";
      position: absolute; right: 11px; bottom: 1px; left: 11px;
      height: 2px; border-radius: 2px;
      background: var(--blue);
      transform: scaleX(0); transform-origin: left;
      transition: transform .2s ease;
    }
    .nav-btn:hover, .nav-btn.active { color: var(--ink); }
    .nav-btn.active::after { transform: scaleX(1); }
    .header-count {
      display: flex;
      align-items: baseline;
      gap: 7px;
      min-width: max-content;
      color: var(--mist);
      font-size: 10px;
      letter-spacing: var(--track-micro);
    }
    .header-count {
      font-family: var(--display); font-weight: 600; font-stretch: 92%;
      text-transform: uppercase; letter-spacing: var(--track-micro);
    }
    .header-count b {
      color: var(--ink); font-size: 15px; font-weight: 700;
      letter-spacing: -.02em; text-transform: none;
    }

    main { width: min(1460px, 100%); margin: 0 auto; padding: clamp(26px, 4vw, 58px) clamp(20px, 4vw, 58px) 150px; }
    .view { display: none; animation: viewIn .25s ease both; }
    .view.active { display: block; }
    @keyframes viewIn { from { opacity: 0; transform: translateY(5px); } }
    h1, h2, h3, h4 { margin-top: 0; font-family: var(--display); font-weight: 700; line-height: 1.06; letter-spacing: -.03em; }
    h1 { max-width: 780px; margin-bottom: 17px; font-size: clamp(38px, 6.2vw, 72px); }
    h2 { font-size: clamp(28px, 3.8vw, 46px); letter-spacing: -.028em; }
    h3 { font-size: 21px; font-weight: 600; letter-spacing: -.02em; }
    h4 { font-weight: 600; letter-spacing: -.015em; }

    /* Every uppercase micro-label on the site — eyebrows, table headers, spec
       captions, tags, provenance chips. One rule so they stay one voice: the
       display face, held slightly narrow so the tracking does not eat column
       width, which is what the Archivo width axis is here for. */
    .eyebrow, .card-kicker, .pick-label, .question-meta, .tray-label, .price span,
    .tag, .src-tag, .fit-chip, .weight-axis, .spec-cell span, .footer-col h4,
    .filter-field label, .dealer-table th, .compare-table th:first-child,
    .compare-table td:first-child, .brand-sub, .nav-btn, .header-count {
      font-family: var(--display);
      font-weight: 600;
      font-stretch: 92%;
      font-variant-numeric: tabular-nums;
    }
    .eyebrow {
      margin: 0 0 10px; color: var(--blue-text); font-size: 11px;
      text-transform: uppercase; letter-spacing: var(--track-label);
    }
    p { margin-top: 0; }
    .lede { max-width: 680px; color: var(--slate); font-size: clamp(16px, 1.7vw, 19px); }
    .section-head { display: flex; align-items: end; justify-content: space-between; gap: 20px; margin-bottom: 24px; }
    .section-head h2 { margin-bottom: 6px; }
    .section-head p { margin: 0; color: var(--slate); }
    .section-head.compact h2 { font-size: 30px; }
    .muted { color: var(--mist); }
    .small { font-size: 12px; }

    .btn {
      text-decoration: none;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      min-height: 42px;
      padding: 10px 16px;
      border: 1px solid var(--line);
      border-radius: 12px;
      color: var(--ink);
      background: var(--panel-2);
      font-family: var(--display);
      font-size: 13.5px;
      font-weight: 600;
      letter-spacing: -.005em;
      cursor: pointer;
      transition: transform .18s ease, border-color .18s ease, background .18s ease;
    }
    .btn:hover { transform: translateY(-1px); border-color: #b7c8e6; }
    .btn.primary { border-color: var(--blue); color: #fff; background: var(--blue); }
    .btn.primary:hover { border-color: var(--blue-deep); background: var(--blue-deep); }
    .btn.coral { border-color: var(--coral); color: #21100f; background: var(--coral); }
    /* Ghost is a quieter button, not an invisible one. With background:transparent
       a row of dealer links on a translucent panel read as hairline outlines with
       nothing behind them; it needs a surface to sit on. */
    .btn.ghost { background: var(--white); }
    .btn.ghost:hover { background: var(--panel-2); }
    /* Outbound: maker sites and dealers. Marked as leaving the site. */
    .btn.out {
      border-color: rgba(47, 107, 237, .3);
      color: var(--blue-text);
      background: var(--blue-tint);
      text-decoration: none;
    }
    .btn.out:hover { border-color: var(--blue); color: #fff; background: var(--blue); }
    .btn[disabled] { opacity: .45; cursor: default; transform: none; }
    .btn[disabled]:hover { transform: none; border-color: var(--line); }
    .btn.small-btn { min-height: 34px; padding: 7px 11px; border-radius: 9px; font-size: 12px; }
    .icon-btn {
      display: inline-grid;
      width: 40px;
      height: 40px;
      place-items: center;
      border: 1px solid var(--line);
      border-radius: 50%;
      background: var(--panel);
      cursor: pointer;
    }
    .icon-btn:hover { border-color: var(--blue); }

    /* Wizard */
    .wizard-hero { position: relative; min-height: 540px; }
    /* The fleet device: the mark opened out into a column of hulls. This
       replaces the concentric load-line rings, which were drawn for a mark
       that no longer exists. */
    /* The fleet device: the mark opened out into a fleet on one heading. It
       replaces the concentric load-line rings, which were drawn for a mark
       that no longer exists. Landscape cut, so it lives in the strip above
       the fit card instead of sitting behind it. */
    .waterline {
      position: absolute;
      z-index: -1;
      top: -14px;
      right: -2vw;
      width: min(34vw, 430px);
      aspect-ratio: 360 / 108;
      background: url("/brand/device-fleet-wide.svg") right top / contain no-repeat;
      opacity: .42;
    }
    .wizard-shell { max-width: 970px; }
    .hero-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, 372px); gap: clamp(24px, 4vw, 56px); align-items: center; }
    .hero-grid .wizard-shell { max-width: 640px; }
    .hero-grid h1 { font-size: clamp(38px, 4.6vw, 62px); }
    .hero-fit {
      /* Filled by fillHeroFit() once the app knows the stored weight, so it holds
         its own height from first paint. Without this the card arrived late and
         pushed the footer down — the single 0.30 layout shift on the home page.
         470px is the rendered height at every breakpoint (measured 390/768/1280). */
      min-height: 470px;
      padding: 26px 24px 22px;
      border: 1px solid var(--line);
      border-radius: var(--radius);
      background: rgba(255,255,255,.86);
      box-shadow: var(--shadow);
      backdrop-filter: blur(6px);
    }
    .hero-fit h3 { margin-bottom: 6px; font-size: 21px; }
    .hero-fit .muted.small { margin-bottom: 18px; }
    .hero-fit .weight-set { margin-bottom: 18px; }
    .hero-fit-result { margin: 0 0 2px; color: var(--slate); font-size: 13px; line-height: 1.5; }
    .hero-fit-result b { color: var(--ink); font-weight: 600; }
    .hero-fit-cta { width: 100%; margin-top: 14px; }
    .hero-fit-bar { display: flex; gap: 3px; height: 7px; margin: 14px 0 9px; border-radius: 4px; background: var(--panel-2); overflow: hidden; }
    .hero-fit-bar i { background: var(--blue); transition: width .2s ease; }
    .hero-fit-bar u { background: rgba(47,107,237,.32); transition: width .2s ease; }
    .hero-fit-legend { display: flex; align-items: center; gap: 6px; margin: 0; color: var(--mist); font-size: 11.5px; }
    .hero-fit-legend .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); }
    .hero-fit-legend .dot.workable { margin-left: 8px; background: rgba(47,107,237,.32); }
    @media (max-width: 940px) { .hero-grid { grid-template-columns: 1fr; gap: 34px; } .hero-fit { max-width: 460px; } }
    .wizard-intro { padding-top: clamp(24px, 5vw, 74px); }
    .hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 30px; }
    .hero-proof { display: flex; flex-wrap: wrap; gap: 22px; margin-top: 48px; padding-top: 17px; border-top: 1px solid var(--line-soft); }
    .hero-proof span { color: var(--mist); font-size: 12px; }
    .hero-proof b { display: block; margin-bottom: 2px; color: var(--ink); font-family: var(--display); font-size: 22px; }
    .wizard-progress { display: grid; grid-template-columns: repeat(6, 1fr); gap: 7px; margin: 8px 0 44px; }
    .progress-segment { height: 4px; overflow: hidden; border-radius: 2px; background: var(--line); }
    .progress-segment span { display: block; width: 100%; height: 100%; background: var(--blue); transform: scaleX(0); transform-origin: left; transition: transform .25s ease; }
    .progress-segment.done span { transform: scaleX(1); }
    .question-meta { color: var(--mist); font-size: 12px; text-transform: uppercase; letter-spacing: .12em; }
    .question-title { max-width: 720px; margin: 7px 0 10px; font-size: clamp(32px, 4.4vw, 55px); }
    .question-copy { max-width: 650px; color: var(--slate); }
    .option-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 32px; }
    .option-card {
      position: relative;
      min-height: 116px;
      padding: 21px 22px;
      border: 1px solid var(--line);
      border-radius: var(--radius);
      text-align: left;
      background: rgba(255, 255, 255, .72);
      cursor: pointer;
      transition: transform .18s ease, border-color .18s ease, background .18s ease;
    }
    .option-card:hover, .option-card.selected { transform: translateY(-2px); border-color: var(--blue); background: #eaf1fd; }
    .option-card strong { display: block; margin-bottom: 6px; font-family: var(--display); font-size: 20px; }
    .option-card span { color: var(--slate); font-size: 13px; }
    .wizard-actions { display: flex; justify-content: space-between; gap: 12px; margin-top: 28px; }
    .budget-card { max-width: 760px; margin-top: 32px; padding: 28px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); }
    .budget-value { margin-bottom: 16px; font-family: var(--display); font-size: 42px; font-weight: 700; }
    input[type="range"] { width: 100%; accent-color: var(--blue); }
    .range-labels { display: flex; justify-content: space-between; margin-top: 7px; color: var(--mist); font-size: 11px; }
    .toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 25px; padding-top: 20px; border-top: 1px solid var(--line-soft); }
    .switch { position: relative; display: inline-block; width: 48px; height: 27px; flex: 0 0 auto; }
    .switch input { width: 1px; height: 1px; opacity: 0; }
    .switch span { position: absolute; inset: 0; border-radius: 30px; background: #b9c9e2; cursor: pointer; transition: .2s; }
    .switch span::after { content: ""; position: absolute; top: 4px; left: 4px; width: 19px; height: 19px; border-radius: 50%; background: var(--ink); transition: .2s; }
    .switch input:checked + span { background: var(--blue); }
    .switch input:checked + span::after { transform: translateX(21px); background: var(--white); }
    .result-note { margin: 12px 0 25px; padding: 12px 15px; border-left: 3px solid var(--amber); background: var(--amber-tint); color: var(--amber-text); font-size: 13px; }
    .special-picks { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-top: 42px; }
    .pick-block { padding: 22px; border: 1px solid var(--line); border-radius: var(--radius); background: rgba(255, 255, 255, .62); }
    .pick-block.stretch { border-color: rgba(236, 106, 84, .5); }
    .pick-block.safe { border-color: rgba(47, 107, 237, .42); }
    /* Coral is illustration only — it fails as text at 3.11:1, so the label
       on a coral-bordered block is set in Out. */
    .pick-label { margin-bottom: 13px; color: var(--out); font-size: 11px; text-transform: uppercase; letter-spacing: var(--track-label); }
    .safe .pick-label { color: var(--blue-text); }

    /* Home: what to do next. The hero used to run straight into the footer. */
    .home-next { margin-top: clamp(40px, 6vw, 78px); padding-top: 34px; border-top: 1px solid var(--line-soft); }
    .guide-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr)); }
    .guide-card {
      display: flex; flex-direction: column; gap: 4px;
      padding: 18px 19px 16px;
      border: 1px solid var(--line-soft);
      border-radius: 14px;
      color: inherit;
      background: var(--panel);
      text-decoration: none;
      transition: border-color .18s ease, transform .18s ease;
    }
    .guide-card:hover { border-color: rgba(47,107,237,.42); color: inherit; transform: translateY(-2px); }
    .guide-card .eyebrow { margin: 0; }
    .guide-card strong { font-family: var(--display); font-size: 18px; letter-spacing: -.02em; }
    .guide-count { margin-top: auto; padding-top: 12px; color: var(--blue-text); font-family: var(--display); font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-micro); }
    .home-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }

    /* Browse and cards */
    /* The deck holds the search box and the weight, because those two decide what
       the page is showing; the seven refinements fold behind the Filters button at
       every width, and whatever is switched on is listed as a chip underneath. The
       bar used to be open on desktop with all nine controls in one grey grid, and
       the weight — which re-scores every craft on the page — sat sixth in the row
       looking like the least of them. */
    .filter-deck {
      display: grid;
      grid-template-columns: minmax(240px, 1fr) auto auto;
      align-items: center;
      gap: 14px 18px;
      margin-bottom: 12px;
      padding: 15px 16px;
      border: 1px solid var(--line-soft);
      border-radius: var(--radius);
      background: rgba(255, 255, 255, .78);
    }
    .filter-deck .search-field { min-width: 0; }
    .filter-deck .search-input input { min-height: 46px; font-size: 15px; }
    .deck-weight {
      width: 274px;
      padding: 11px 14px 12px;
      border: 1px solid var(--blue-tint);
      border-radius: 14px;
      background: var(--wash);
    }
    .deck-weight > label {
      display: block;
      margin: 0 0 7px 1px;
      color: var(--blue-text);
      font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
    }
    .deck-weight .weight-set-row { gap: 7px; }
    .deck-weight .weight-input { background: var(--white); }
    .deck-weight .weight-input input { font-size: 26px; }
    .deck-weight .weight-step { width: 36px; background: var(--white); }
    .deck-weight .weight-range { margin-top: 9px; }
    .deck-weight .range-labels { display: none; }
    .deck-note { margin: 8px 0 0; color: var(--slate); font-size: 11.5px; line-height: 1.42; }
    .deck-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
    /* Chips are the promise that a shut panel is not hiding anything: every filter
       that is on says so here, and clicking one takes it off. */
    .filter-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 14px; }
    .filter-chips:empty { display: none; }
    .filter-chip {
      display: inline-flex;
      align-items: center;
      gap: 7px;
      padding: 5px 10px 5px 12px;
      border: 1px solid var(--blue-tint);
      border-radius: 999px;
      color: var(--blue-text);
      background: var(--wash);
      font-family: var(--display); font-size: 12px; font-weight: 600;
      cursor: pointer;
    }
    .filter-chip:hover { border-color: var(--blue); background: var(--blue-tint); }
    .filter-chip .chip-x { color: var(--blue); font-size: 15px; line-height: 1; }
    .filter-toggle {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      min-height: 40px;
      padding: 8px 16px;
      border: 1px solid var(--line);
      border-radius: 12px;
      background: var(--white);
      font-family: var(--display); font-size: 13px; font-weight: 600;
      cursor: pointer;
    }
    .filter-toggle:hover { border-color: var(--blue); color: var(--blue-deep); }
    .filter-toggle span { display: inline-block; transition: transform .18s ease; }
    .filter-toggle[aria-expanded="true"] span { transform: rotate(180deg); }
    .filter-toggle b { padding: 1px 7px; border-radius: 999px; color: #fff; background: var(--blue); font-size: 11px; }
    .browse-filters { display: none; }
    .browse-filters.open { display: block; }
    .filter-bar {
      position: relative;
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
      gap: 10px;
      margin-bottom: 16px;
      padding: 14px;
      border: 1px solid var(--line-soft);
      border-radius: var(--radius);
      background: rgba(255, 255, 255, .7);
    }
    .filter-field { min-width: 0; }
    .filter-field label { display: block; margin: 0 0 6px 2px; color: var(--mist); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em; }
    select, .filter-button {
      width: 100%;
      min-height: 40px;
      padding: 8px 11px;
      border: 1px solid var(--line);
      border-radius: 10px;
      color: var(--ink);
      background: var(--panel);
    }
    .filter-button { text-align: left; cursor: pointer; }
    /* Search takes the whole first row of the bar. It is the one control a reader
       arrives already knowing how to use, and at 168px it would sit in the line of
       dropdowns looking like one more of them. */
    .search-input { position: relative; display: block; }
    .search-input input {
      width: 100%;
      min-height: 40px;
      padding: 8px 38px 8px 36px;
      border: 1px solid var(--line);
      border-radius: 10px;
      color: var(--ink);
      background: var(--panel);
      appearance: none;
      -webkit-appearance: none;
    }
    /* The browser's own clear button would sit beside ours. */
    .search-input input::-webkit-search-cancel-button, .search-input input::-webkit-search-decoration { display: none; }
    .search-icon {
      position: absolute;
      top: 50%;
      left: 12px;
      width: 16px;
      height: 16px;
      transform: translateY(-50%);
      fill: none;
      stroke: var(--mist);
      stroke-width: 2;
      stroke-linecap: round;
      pointer-events: none;
    }
    .search-clear {
      position: absolute;
      top: 50%;
      right: 6px;
      visibility: hidden;
      width: 28px;
      height: 28px;
      transform: translateY(-50%);
      border: 0;
      border-radius: 8px;
      color: var(--slate);
      background: transparent;
      font-size: 18px;
      line-height: 1;
      cursor: pointer;
    }
    .search-clear.on { visibility: visible; }
    .search-clear:hover { color: var(--ink); background: var(--line-soft); }
    .brand-popover {
      position: absolute;
      z-index: 20;
      top: 75px;
      left: 14px;
      display: none;
      width: min(660px, calc(100% - 28px));
      padding: 15px;
      border: 1px solid var(--line);
      border-radius: 14px;
      background: #ffffff;
      box-shadow: var(--shadow);
    }
    .brand-popover.open { display: block; }
    .popover-foot { display: flex; justify-content: space-between; gap: 10px; margin-top: 12px; }
    .chip-wrap { display: flex; flex-wrap: wrap; gap: 7px; }
    .chip {
      padding: 6px 12px;
      border: 1px solid var(--line);
      border-radius: 999px;
      color: var(--slate);
      background: var(--panel);
      font-family: var(--display);
      font-size: 11.5px;
      font-weight: 600;
      cursor: pointer;
      transition: border-color .16s ease, color .16s ease, background .16s ease;
    }
    .chip:hover { border-color: #b7c8e6; color: var(--ink); }
    .chip.active { border-color: var(--blue); color: #fff; background: var(--blue); }
    .filter-secondary { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 28px; }
    .category-chips { display: flex; flex-wrap: wrap; gap: 7px; }
    .inline-check { display: inline-flex; align-items: center; gap: 8px; color: var(--slate); font-size: 12px; }
    .inline-check input { accent-color: var(--blue); }

    /* Browse as a rail. Below 1080px nothing changes: the deck stays a slab, the
       refinements stay behind the Filters button and the brand list stays a
       popover, because a phone has no room to spend on a permanent column. Above
       it, the filters stop hiding — they become a sticky left rail beside the
       grid, the toggle button goes away (there is nothing left to toggle) and the
       brand popover flattens into the rail instead of floating over a 240px
       column it cannot fit inside. All of this is layout: every handler,
       including the chips and the popover open/close, is the same code. */
    @media (min-width: 1080px) {
      #view-browse.view.active {
        display: grid;
        grid-template-columns: 274px minmax(0, 1fr);
        column-gap: 24px;
        align-items: start;
      }
      #view-browse > .section-head { grid-column: 1 / -1; }
      #browseControls {
        position: sticky;
        top: 84px;
        max-height: calc(100vh - 104px - var(--beta-h));
        padding: 14px;
        overflow-y: auto;
        overscroll-behavior: contain;
        border: 1px solid var(--line-soft);
        border-radius: var(--radius);
        background: rgba(255, 255, 255, .72);
      }
      #browseResults { min-width: 0; }
      /* One column, and the deck loses its slab treatment — the rail is already
         a panel, so a second border around the search box just draws a box in a box. */
      #browseControls .filter-deck {
        display: block;
        margin-bottom: 10px;
        padding: 0;
        border: 0;
        background: none;
      }
      #browseControls .deck-weight { width: auto; margin-top: 10px; }
      #browseControls .deck-actions { margin-top: 10px; }
      #browseControls .filter-toggle { display: none; }
      #browseControls .clear-btn { width: 100%; justify-content: center; }
      #browseControls .browse-filters { display: block; }
      /* Flex, not the 1-column grid, so the flattened brand list can be ordered
         to sit directly under the Brand button instead of at the foot of the
         rail: it is the last child of .filter-bar because it used to float. */
      #browseControls .filter-bar {
        display: flex;
        flex-direction: column;
        padding: 0;
        border: 0;
        background: none;
      }
      #browseControls .filter-bar .filter-field { order: 3; }
      #browseControls .filter-bar .filter-field:first-child { order: 1; }
      #browseControls .brand-popover { order: 2; }
      #browseControls .brand-popover {
        position: static;
        display: none;
        width: auto;
        margin-top: 8px;
        padding: 12px;
        border-color: var(--line-soft);
        background: var(--wash);
        box-shadow: none;
      }
      #browseControls .brand-popover.open { display: block; }
      #browseControls .filter-secondary { flex-direction: column; align-items: stretch; gap: 10px; margin-bottom: 0; }
      #browseControls .filter-secondary .chip-wrap { flex-direction: column; align-items: flex-start; gap: 8px; }
    }
    .results-meta { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 14px; color: var(--mist); font-size: 13px; }
    .results-meta strong { color: var(--ink); }
    /* Auto-fill rather than a fixed 3, so the grid reflows on its own instead of
   snapping between three hardcoded breakpoints. */
    .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(322px, 1fr)); gap: 16px; }
    /* A column, so every card in a row ends level and the price/compare foot lines
       up across the grid instead of floating at whatever height the copy stopped. */
    .model-card {
      position: relative;
      display: flex;
      flex-direction: column;
      overflow: hidden;
      border: 1px solid var(--line-soft);
      border-radius: var(--radius);
      background: rgba(255, 255, 255, .85);
      cursor: pointer;
      transition: transform .2s ease, border-color .2s ease, box-shadow .2s ease;
    }
    .model-card:hover { transform: translateY(-4px); border-color: #aec0e0; box-shadow: 0 20px 45px rgba(23, 54, 110, .12); }
    /* One canonical media shape for every card. tools/images.mjs already trimmed,
       matted or cropped each image onto exactly this ratio, so `cover` crops
       nothing — it just guarantees the grid stays even if a source slips through
       at the wrong size. The old fixed 182px `contain` box with 14px padding is
       what letterboxed two thirds of the catalogue into a hairline. */
    .card-media { position: relative; aspect-ratio: var(--card-ratio); overflow: hidden; background: #fff; border-bottom: 1px solid var(--line-soft); }
    .card-media img, .compare-image img { display: block; width: 100%; height: 100%; object-fit: cover; }
    .model-card.is-drawn .card-media { background: #fff; }
    .card-link { display: flex; flex: 1; flex-direction: column; color: inherit; text-decoration: none; }
    .card-link:focus-visible { outline: 3px solid rgba(47, 107, 237, .55); outline-offset: -3px; }
    .image-fallback {
      display: grid;
      width: 100%;
      height: 100%;
      place-items: center;
      color: rgba(255, 255, 255, .92);
      background: linear-gradient(145deg, #3f74e6, #2f6bed 58%, #5a86f0);
      font-family: var(--display);
      font-size: clamp(25px, 4vw, 45px);
      font-weight: 700;
      letter-spacing: -.04em;
    }
    /* Both of these used to be pinned over the card image. A 4.4:1 crop of a
       6-metre hull has no free corner, so they live in the body now. */
    .match-score { flex: none; padding: 5px 9px; border-radius: 999px; color: var(--white); background: var(--blue); font-size: 11px; font-weight: 800; }
    .card-body { display: flex; flex: 1; flex-direction: column; padding: 18px 18px 0; }
    .card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
    .card-kicker { margin: 0 0 3px; color: var(--blue-text); font-size: 10px; text-transform: uppercase; letter-spacing: var(--track-micro); }
    .card-title { margin: 0 0 8px; font-size: 24px; }
    .card-fit { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin: 0 0 11px; }
    .card-fit .fit-band { color: var(--mist); font-size: 11px; }
    .card-reason { min-height: 40px; color: var(--slate); font-size: 13px; }
    .spec-line { display: flex; flex-wrap: wrap; gap: 6px 13px; margin: 12px 0; color: var(--mist); font-size: 11px; }
    .spec-line span { position: relative; }
    .spec-line span + span::before { content: "/"; position: absolute; left: -9px; color: #9fb0cb; }
    /* The hull's dimensions are one fact, not two, so they carry the line. */
    .spec-line .spec-dims { color: var(--slate); font-weight: 700; }
    .tag-list { display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0; }
    .tag { display: inline-flex; padding: 4px 7px; border: 1px solid var(--line); border-radius: 7px; color: var(--slate); font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; }
    .tag.coral { border-color: rgba(236,106,84,.45); color: var(--out); }
    .rating-row { display: grid; grid-template-columns: 64px 1fr 22px; align-items: center; gap: 8px; margin: 8px 0; color: var(--mist); font-size: 10px; }
    .rating-row b { color: var(--ink); text-align: right; }
    .bar { height: 6px; overflow: hidden; border-radius: 10px; background: #dbe4f2; }
    .bar > span { display: block; width: calc(var(--rating, 0) * 10%); height: 100%; border-radius: inherit; background: var(--blue); transform-origin: left; animation: barIn .65s cubic-bezier(.22,.8,.25,1) both; }
    .rating-row.speed .bar > span { background: var(--coral); }
    /* The at-your-weight stability readout. Sits under the editorial bar in the
       same grid so the number lines up with the bar's own value, and only ever
       appears when the score actually moves. */
    .rating-adjust { display: grid; grid-template-columns: 64px 1fr 22px; gap: 8px; margin: -4px 0 8px; font-size: 10px; color: var(--mist); }
    .rating-adjust span { grid-column: 1 / 3; }
    .rating-adjust b { grid-column: 3; text-align: right; color: var(--blue); }
    .rating-adjust.severe b { color: var(--coral); }
    .big-ratings .rating-adjust { grid-template-columns: 78px 1fr 28px; margin: -8px 0 12px; font-size: 11px; }
    /* Card scores. The number is the element; the bar survives as a hairline that
       gives the number a shape without competing with it. Two tinted tiles side by
       side read as one instrument — stacked full-width bars read as two verdicts,
       and were the loudest thing on a card whose actual headline is the fit. */
    /* `margin-top: auto` pins the scores to the bottom of the body, so every card
       in a row shows its two numbers on the same line however the spec line above
       them wrapped. Comparing across a row is the whole job of a grid. */
    .metric-row { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin: auto 0 2px; }
    .metric { padding: 9px 11px 10px; border: 1px solid var(--line-soft); border-radius: 10px; background: var(--blue-tint); }
    .metric.speed { background: var(--coral-tint); }
    .metric-k { display: block; color: var(--mist); font-family: var(--display); font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; }
    .metric-v { display: block; margin-top: 2px; color: var(--blue-text); font-family: var(--display); font-size: 23px; font-weight: 750; line-height: 1; letter-spacing: -.03em; }
    .metric.speed .metric-v { color: var(--out); }
    .metric-v em { color: var(--mist); font-size: 11px; font-style: normal; font-weight: 600; letter-spacing: 0; }
    .metric .bar { display: block; height: 3px; margin-top: 7px; background: rgba(23, 54, 110, .13); }
    .metric.speed .bar > span { background: var(--coral); }
    .metric-adjust { display: block; margin-top: 6px; color: var(--mist); font-size: 10px; }
    .metric-adjust b { color: var(--blue); }
    .metric-adjust.severe b { color: var(--out); }
    @keyframes barIn { from { transform: scaleX(0); } }
    .card-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 15px 18px 18px; padding-top: 13px; border-top: 1px solid var(--line-soft); }
    .price { color: var(--ink); font-family: var(--display); font-size: 14px; font-weight: 700; letter-spacing: -.02em; }
    .price span { display: block; color: var(--mist); font-size: 9px; font-weight: 500; text-transform: uppercase; letter-spacing: .08em; }
    .empty-state { padding: 48px 20px; border: 1px dashed var(--line); border-radius: var(--radius); text-align: center; color: var(--slate); }
    .empty-state p { margin-bottom: 18px; }
    .empty-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }

    /* Paddler weight fit */
    .fit-chip { display: inline-flex; align-items: center; gap: 5px; padding: 4px 8px; border: 1px solid var(--line); border-radius: 999px; font-size: 10px; font-weight: 700; letter-spacing: .02em; white-space: nowrap; }
    .fit-chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
    .fit-ideal { border-color: rgba(47,107,237,.45); color: var(--blue-text); background: var(--blue-tint); }
    .fit-workable { border-color: rgba(198,143,34,.42); color: var(--amber-text); background: var(--amber-tint); }
    .fit-light, .fit-heavy { border-color: rgba(236,106,84,.42); color: var(--out); background: var(--coral-tint); }
    .fit-unknown { border-color: var(--line); color: var(--mist); background: var(--panel-2); }
    .weight-scale { position: relative; height: 34px; margin: 28px 0 6px; border-radius: 8px; background: linear-gradient(90deg, var(--coral-tint) 0%, var(--coral-tint) var(--min), var(--amber-tint) var(--min), var(--amber-tint) var(--ilo), var(--blue-tint) var(--ilo), var(--blue-tint) var(--ihi), var(--amber-tint) var(--ihi), var(--amber-tint) var(--max), var(--coral-tint) var(--max), var(--coral-tint) 100%); }
    .weight-scale .marker { position: absolute; top: -5px; bottom: -5px; width: 3px; border-radius: 3px; background: var(--ink); transform: translateX(-1.5px); }
    .weight-scale .marker span { position: absolute; top: -20px; left: 50%; transform: translateX(-50%); padding: 1px 6px; border-radius: 6px; background: var(--ink); color: #fff; font-size: 10px; font-weight: 800; white-space: nowrap; }
    .weight-axis { display: flex; justify-content: space-between; color: var(--mist); font-size: 10px; font-weight: 700; }
    .fit-readout { display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: baseline; margin-top: 12px; }
    .fit-readout b { font-family: var(--display); font-size: 20px; font-weight: 700; letter-spacing: -.03em; }
    .method-note { margin-top: 14px; padding: 12px 14px; border-left: 3px solid var(--line); border-radius: 0 10px 10px 0; background: var(--panel-2); color: var(--slate); font-size: 12px; }
    .method-note p { margin: 0 0 8px; }
    .method-note p:last-child { margin-bottom: 0; }
    code {
      font-family: var(--display);
      font-stretch: 88%;
      font-weight: 500;
      font-variant-numeric: tabular-nums;
      letter-spacing: .01em;
    }
    .method-note code { font-size: 12px; }

    /* Chart */
    .chart-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
    .chart-shell { position: relative; padding: 18px; border: 1px solid var(--line-soft); border-radius: var(--radius); background: rgba(255, 255, 255, .78); }
    #scatter { display: block; width: 100%; height: auto; min-height: 480px; }
    .chart-pan-hint { display: none; margin: 0 0 8px; color: var(--mist); font-size: 11.5px; }
    /* On a phone the plot is 960 units wide inside a 320-unit box, so a 480px
       min-height letterboxed it into a strip of dots with a screen of white above
       and below. Below the breakpoint it keeps a size a thumb can hit and the
       shell pans instead. The base rule above has to come first, or it wins on
       source order and the hint never appears. */
    @media (max-width: 760px) {
      .chart-shell { overflow-x: auto; padding: 12px; -webkit-overflow-scrolling: touch; }
      #scatter { min-width: 660px; min-height: 400px; }
      .chart-pan-hint { display: block; }
    }
    .grid-line { stroke: rgba(40,80,140,.12); stroke-width: 1; }
    .axis-label { fill: var(--mist); font-family: var(--body); font-size: 11px; }
    .quadrant-label { fill: rgba(60,90,140,.30); font-family: var(--display); font-size: 15px; font-weight: 650; }
    .plot-dot { cursor: pointer; stroke: rgba(255,255,255,.92); stroke-width: 1.5; transition: opacity .18s, stroke-width .18s; }
    .plot-dot:hover, .plot-dot:focus { stroke-width: 3; }
    .chart-tooltip { position: absolute; z-index: 10; display: none; max-width: 230px; padding: 11px 13px; border: 1px solid var(--line); border-radius: 11px; background: #ffffff; box-shadow: var(--shadow); pointer-events: none; font-size: 11px; }
    .chart-tooltip.visible { display: block; }
    .chart-tooltip strong { display: block; color: var(--ink); font-family: var(--display); font-size: 16px; }
    .cluster-count { font-family: var(--display); font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; text-anchor: middle; dominant-baseline: central; pointer-events: none; }
    .chart-list-panel { position: absolute; z-index: 12; display: none; width: 280px; max-height: 350px; overflow: auto; background: #ffffff; border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow); }
    .chart-list-panel.visible { display: block; }
    .clp-head { position: sticky; top: 0; display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--line-soft); background: #ffffff; font-size: 11px; color: var(--slate); }
    .clp-head strong { color: var(--ink); font-family: var(--display); font-size: 13px; }
    .clp-close { margin-left: auto; border: 0; background: none; font-size: 18px; color: var(--mist); cursor: pointer; line-height: 1; }
    .clp-row { display: flex; width: 100%; align-items: center; gap: 8px; padding: 8px 12px; border: 0; border-bottom: 1px solid var(--line-soft); background: none; text-align: left; font-size: 12px; color: var(--ink); cursor: pointer; }
    .clp-row:hover { background: #f2f6fd; }
    .clp-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
    .clp-price { margin-left: auto; color: var(--mist); font-size: 10.5px; white-space: nowrap; }

    /* Content provenance tags */
    .src-tag { display: inline-block; vertical-align: middle; margin-left: 8px; padding: 2px 8px; border-radius: 999px; font-family: var(--body); font-size: 9px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; white-space: nowrap; }
    .src-manufacturer { background: #fdeee2; color: #a35317; }
    .src-research { background: #e5efff; color: #2f5fb8; }
    .src-community { background: var(--fit-tint); color: var(--fit); }
    .src-auto { background: #eef0f4; color: #5b6b85; }
    .card-reason .src-tag, .src-tag.block { display: inline-block; margin: 0 0 4px; }
    /* What the dot colour means. Sits directly under the plot, because the
       amber step is below the 3:1 contrast bar and needs the relief. */
    .fit-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; margin: 0 0 12px; color: var(--slate); font-size: 11.5px; }
    .fit-key { display: inline-flex; align-items: center; gap: 6px; }
    .fit-key:first-child { color: var(--mist); }
    .fit-key b { color: var(--ink); font-family: var(--display); font-weight: 700; }
    .fit-key i { width: 10px; height: 10px; border-radius: 50%; box-shadow: 0 0 0 1.5px rgba(255,255,255,.92); }
    .fit-key i.cluster { width: 14px; height: 14px; background: var(--slate); opacity: .9; }

    .brand-legend { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line-soft); }
    .legend-btn { display: inline-flex; align-items: center; gap: 6px; padding: 5px 8px; border: 1px solid transparent; border-radius: 8px; color: var(--slate); background: transparent; font-size: 10px; cursor: pointer; }
    .legend-btn.off { opacity: .35; }
    /* Maker dots are a binary on/off state, not an identity hue — see the
       comment on FIT_COLOR for why maker is no longer colour-coded. */
    .legend-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); }
    .legend-btn.off .legend-dot { background: var(--line); }

    /* Market */
    .market-grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(300px, .85fr); gap: 22px; align-items: start; }
    .panel { padding: 22px; border: 1px solid var(--line-soft); border-radius: var(--radius); background: rgba(255, 255, 255, .72); }
    .panel h3 { margin-bottom: 8px; }
    .panel-intro { color: var(--slate); font-size: 13px; }
    .dealer-table, .compare-table { width: 100%; border-collapse: collapse; }
    .dealer-table th, .dealer-table td { padding: 12px 9px; border-bottom: 1px solid var(--line-soft); text-align: left; vertical-align: top; }
    .dealer-table th { color: var(--mist); font-size: 9px; text-transform: uppercase; letter-spacing: .1em; }
    .dealer-table td { font-size: 12px; }
    .dealer-table td:first-child { color: var(--ink); font-weight: 650; }
    #buyRoot > .panel, #buyRoot > section.panel { margin-bottom: 22px; }
    .brand-card-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); margin-top: 18px; }
    .brand-card { padding: 15px 16px 14px; border: 1px solid var(--line-soft); border-radius: 13px; background: var(--panel); transition: border-color .18s ease, transform .18s ease; }
    .brand-card:hover { border-color: rgba(47,107,237,.4); transform: translateY(-2px); }
    .brand-card-head { display: flex; align-items: center; gap: 9px; }
    .brand-card-head h4 { margin: 0; font-size: 15.5px; letter-spacing: -.02em; }
    .brand-card p { margin: 3px 0 11px; }
    .brand-card .chip-wrap { gap: 6px; }
    .market-panel .market-head { display: flex; align-items: start; justify-content: space-between; gap: 18px; flex-wrap: wrap; }
    .market-panel .market-head h3 { margin-bottom: 5px; }
    .market-panel .panel-intro { max-width: 62ch; margin: 0; }
    .market-badge { padding: 5px 11px; border: 1px solid rgba(47,107,237,.28); border-radius: 999px; background: var(--blue-tint); color: var(--blue-text); font-family: var(--display); font-size: 11px; font-weight: 600; letter-spacing: .01em; white-space: nowrap; }
    .next-markets { border-style: dashed; background: rgba(255,255,255,.5); }
    .next-markets .panel-intro { max-width: 88ch; }
    .method-grid { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(300px, .95fr); gap: 22px; align-items: start; }
    .method-side { display: grid; gap: 22px; }
    .method-side .panel-intro { max-width: 58ch; }
    .link-btn { padding: 2px 0; border: 0; background: none; color: var(--blue-text); font-size: 13px; font-weight: 600; cursor: pointer; }
    .link-btn:hover { text-decoration: underline; }
    .tip-list { display: grid; gap: 9px; margin: 17px 0 0; padding: 0; list-style: none; counter-reset: tips; }
    .tip-list li { position: relative; padding: 11px 10px 11px 39px; border-top: 1px solid var(--line-soft); color: var(--slate); font-size: 12px; counter-increment: tips; }
    .tip-list li::before { content: counter(tips); position: absolute; left: 5px; top: 10px; color: var(--blue-text); font-family: var(--display); font-weight: 700; }

    /* Detail */
    .modal {
      position: fixed;
      z-index: 100;
      inset: 0;
      display: none;
      align-items: stretch;
      justify-content: flex-end;
      background: rgba(23, 44, 82, .42);
      backdrop-filter: blur(8px);
    }
    .modal.open { display: flex; }
    .detail-panel { position: relative; width: min(1040px, 94vw); height: 100%; overflow-y: auto; padding: 28px clamp(22px, 4vw, 52px) 70px; border-left: 1px solid var(--line); background: var(--panel); box-shadow: var(--shadow); animation: panelIn .28s ease both; }
    @keyframes panelIn { from { transform: translateX(35px); opacity: .5; } }
    .modal-close { position: sticky; z-index: 5; top: 0; float: right; background: #eef3fb; }
    .detail-head { margin: 42px 0 24px; }
    .detail-head h2 { margin-bottom: 9px; font-size: clamp(38px, 5vw, 64px); }
    .detail-subline { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; color: var(--slate); }
    .gallery-layout { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(280px, .75fr); gap: 20px; }
    .gallery-main { position: relative; margin: 0; overflow: hidden; border-radius: var(--radius); background: #fff; border: 1px solid var(--line-soft); }
    .gallery-main img { display: block; width: 100%; height: auto; max-height: 60vh; object-fit: contain; }
    .gallery-main .image-fallback { color: #eaf1ff; }
    .gallery-arrow { position: absolute; top: 50%; z-index: 2; transform: translateY(-50%); background: rgba(18,40,80,.72); }
    .gallery-arrow.prev { left: 12px; }
    .gallery-arrow.next { right: 12px; }
    /* The caption sits under the image, not on it. It used to be a floating panel
       over the bottom of the frame, which reads fine on a one-line credit and
       covers the hull on a three-line one — and the long captions are exactly the
       ones worth reading, because they are the images with a caveat attached.
       Same bar the static craft pages use, so both surfaces caption alike. */
    .gallery-frame { position: relative; }
    .gallery-caption .credit { opacity: .82; }
    .gallery-caption { padding: 9px 13px; border-top: 1px solid var(--line-soft); color: var(--mist); font-size: 12px; }
    .thumbs { display: flex; gap: 8px; margin-top: 9px; overflow-x: auto; }
    .thumb { width: 72px; height: 55px; flex: 0 0 auto; overflow: hidden; padding: 0; border: 1px solid var(--line); border-radius: 9px; background: #e7edf7; cursor: pointer; }
    .thumb.active { border-color: var(--blue); }
    .thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
    .thumb .image-fallback { font-size: 13px; }
    .detail-summary { padding: 24px; border: 1px solid var(--line-soft); border-radius: var(--radius); background: var(--panel); }
    .big-ratings { margin: 22px 0; }
    .big-ratings .rating-row { grid-template-columns: 78px 1fr 28px; margin: 12px 0; font-size: 12px; }
    .big-ratings .bar { height: 9px; }
    .rating-basis { color: var(--mist); font-size: 11px; }
    .price-block { margin-top: 20px; padding: 17px; border: 1px solid rgba(47,107,237,.28); border-radius: 13px; background: rgba(47,107,237,.07); }
    .price-main { font-family: var(--display); font-size: 29px; font-weight: 750; }
    .price-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 10px 0; color: var(--slate); font-size: 11px; }
    .detail-section { margin-top: 36px; padding-top: 25px; border-top: 1px solid var(--line-soft); }
    .detail-section h3 { margin-bottom: 17px; }
    .spec-table { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; overflow: hidden; border: 1px solid var(--line-soft); border-radius: 14px; background: var(--line-soft); }
    .spec-cell { min-height: 90px; padding: 16px; background: #f3f7fd; }
    .spec-cell span { display: block; margin-bottom: 8px; color: var(--mist); font-size: 9px; text-transform: uppercase; letter-spacing: .1em; }
    .spec-cell strong { font-family: var(--display); font-size: 18px; }
    .layup-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 7px; margin-top: 10px; }
    .layup-item { display: flex; justify-content: space-between; gap: 10px; padding: 9px 11px; border: 1px solid var(--line-soft); border-radius: 9px; color: var(--slate); font-size: 11px; }
    .timeline { display: grid; gap: 10px; }
    .timeline-item { position: relative; padding: 0 0 18px 22px; border-left: 1px solid #cdd9ec; }
    .timeline-item::before { content: ""; position: absolute; top: 2px; left: -5px; width: 9px; height: 9px; border-radius: 50%; background: var(--blue); }
    .timeline-item strong { display: block; margin-bottom: 4px; }
    .timeline-item p { margin: 0; color: var(--slate); font-size: 12px; }
    .pros-cons { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
    .list-card { padding: 19px; border: 1px solid var(--line-soft); border-radius: 14px; background: var(--panel); }
    .list-card.pros { border-top-color: var(--blue); }
    .list-card.cons { border-top-color: var(--coral); }
    .list-card ul { margin: 0; padding-left: 18px; color: var(--slate); font-size: 12px; }
    .list-card li + li { margin-top: 8px; }
    .quote-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 11px; }
    blockquote { margin: 0; padding: 18px; border: 1px solid var(--line-soft); border-radius: 13px; background: #f3f7fd; color: var(--slate); font-size: 12px; }
    blockquote::before { content: "“"; display: block; height: 21px; color: var(--blue); font-family: var(--display); font-weight: 700; font-size: 40px; line-height: 1; }
    blockquote cite { display: block; margin-top: 10px; color: var(--mist); font-style: normal; font-size: 10px; }
    /* minmax(0, 1fr), not the implicit 1fr: a grid item defaults to min-width:auto,
       so a long unbreakable source URL widened the track past the viewport and took
       the whole craft page sideways on a phone. The ellipsis below only works once
       the track is allowed to be narrower than its content. */
    .source-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 7px; margin: 0; padding: 0; list-style: none; }
    .source-list li { min-width: 0; }
    .source-list a { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* Compare */
    .compare-tray {
      position: fixed;
      z-index: 80;
      right: 22px;
      bottom: calc(20px + var(--beta-h));
      left: 22px;
      display: none;
      align-items: center;
      gap: 12px;
      width: min(980px, calc(100% - 44px));
      margin: auto;
      padding: 10px 11px 10px 16px;
      border: 1px solid var(--line);
      border-radius: 16px;
      background: rgba(255, 255, 255, .94);
      box-shadow: 0 20px 70px rgba(23,54,110,.22);
      backdrop-filter: blur(18px);
    }
    .compare-tray.visible { display: flex; }
    .tray-label { min-width: max-content; color: var(--mist); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em; }
    .tray-items { display: flex; flex: 1; gap: 7px; min-width: 0; overflow-x: auto; }
    .tray-chip { display: flex; align-items: center; gap: 7px; min-width: max-content; padding: 7px 8px 7px 11px; border: 1px solid var(--line); border-radius: 10px; font-size: 11px; }
    .tray-chip button { border: 0; color: var(--mist); background: transparent; cursor: pointer; }
    .compare-modal { align-items: center; justify-content: center; padding: 24px; }
    .compare-panel { width: min(1180px, 96vw); max-height: 92vh; overflow: auto; padding: 26px; border: 1px solid var(--line); border-radius: 20px; background: var(--panel); box-shadow: var(--shadow); }
    .compare-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 15px; margin-bottom: 20px; }
    .compare-panel-head h2 { margin: 0; font-size: 34px; }
    .compare-table { table-layout: fixed; }
    .compare-table th, .compare-table td { padding: 13px; border: 1px solid var(--line-soft); vertical-align: top; }
    .compare-table th:first-child, .compare-table td:first-child { width: 120px; color: var(--mist); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .07em; }
    .compare-table th:not(:first-child) { color: var(--ink); font-family: var(--display); font-size: 20px; text-align: left; }
    .compare-table .best { background: rgba(47,107,237,.09); box-shadow: inset 0 2px var(--blue); }
    .compare-image { height: 125px; background: #eaeff7; }
    .compare-image .image-fallback { font-size: 26px; }
    .compare-mini-list { margin: 0; padding-left: 15px; color: var(--slate); font-size: 10px; }
    .toast { position: fixed; z-index: 200; right: 22px; top: 84px; max-width: 340px; padding: 12px 15px; border: 1px solid var(--line); border-radius: 11px; background: #ffffff; box-shadow: var(--shadow); transform: translateY(-10px); opacity: 0; pointer-events: none; transition: .2s; }
    .toast.show { transform: translateY(0); opacity: 1; }

    @media (max-width: 1050px) {
      /* Nav folds into the mark. The lockup stops being a home link and
         becomes the menu control; its open state is two hulls crossing. */
      .app-header { grid-template-columns: auto 1fr; gap: 12px; }
      .header-tools { grid-column: 2; grid-row: 1; justify-self: end; }
      .top-nav {
        position: absolute;
        z-index: 70;
        top: calc(100% + 6px);
        left: clamp(18px, 3vw, 44px);
        display: none;
        flex-direction: column;
        gap: 2px;
        min-width: 236px;
        padding: 8px;
        overflow: visible;
        border: 1px solid var(--line);
        border-radius: 16px;
        background: var(--panel);
        box-shadow: var(--shadow);
      }
      .app-header.menu-open .top-nav { display: flex; }
      .nav-btn { padding: 12px 13px; border-radius: var(--radius-sm); text-align: left; }
      .nav-btn:hover { background: var(--panel-2); }
      .nav-btn::after {
        top: 50%; right: auto; bottom: auto; left: 4px;
        width: 3px; height: 17px; margin-top: -8.5px;
        transform: scaleY(0); transform-origin: center;
      }
      .nav-btn.active::after { transform: scaleY(1); }
      .gallery-layout { grid-template-columns: 1fr; }
      .market-grid, .method-grid { grid-template-columns: 1fr; }
    }
    /* Below this the filter bar is worth more than a screen of vertical space, so
       it folds behind the Filters button and the results start at the top. */
    @media (max-width: 900px) {
      .filter-deck { grid-template-columns: 1fr; }
      .deck-weight { width: 100%; }
      .deck-actions .filter-toggle { flex: 1 1 auto; justify-content: center; }
    }
    @media (max-width: 760px) {
      .brand-popover { top: 8px; left: 8px; width: calc(100% - 16px); }
    }
    @media (max-width: 720px) {
      main { padding-right: 18px; padding-left: 18px; }
      .app-header { padding-right: 18px; padding-left: 18px; }
      .brand-sub, .header-count { display: none; }
      .waterline { top: -4px; width: 62vw; opacity: .2; }
      .option-grid, .special-picks, .pros-cons, .quote-grid { grid-template-columns: 1fr; }
      .spec-table { grid-template-columns: repeat(2, 1fr); }
      .card-grid { grid-template-columns: 1fr; }
      .results-meta { flex-wrap: wrap; }
      .home-links .btn { flex: 1 1 auto; }
      .compare-tray { right: 10px; bottom: 10px; left: 10px; width: calc(100% - 20px); }
      .tray-label { display: none; }
      .compare-panel { padding: 17px; }
      .compare-table th:first-child, .compare-table td:first-child { width: 88px; }
      .compare-table th, .compare-table td { padding: 8px; }
      .dealer-table th:nth-child(2), .dealer-table td:nth-child(2) { display: none; }
    }
    @media (max-width: 560px) {
      .filter-bar { grid-template-columns: 1fr; }
      .spec-table, .layup-list { grid-template-columns: 1fr; }
      .detail-panel { width: 100vw; padding-bottom: 96px; }
      .compare-panel { max-height: 96vh; }
      .gallery-main img { max-height: 44vh; }
    }
    /* Touch: the chart's maker legend and inline text buttons were 20-28px tall,
       which is under the comfortable tap target on a phone. */
    @media (pointer: coarse) {
      .legend-btn { padding: 9px 10px; }
      .link-btn { padding: 8px 0; }
      .chip { padding: 9px 14px; }
      .thumb { width: 84px; height: 62px; }
    }
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; transition-duration: .01ms !important; }
    }

    /* ── Static pages ──────────────────────────────────────────────────────
       The prerendered craft, listing and brand-hub pages. They share the app's
       header, footer and card styles; everything below is what only they use. */
    .static-page .top-nav .nav-btn { text-decoration: none; }
    .breadcrumb { width: min(1460px, 100%); margin: 0 auto; padding: 14px clamp(20px, 4vw, 58px) 0; }
    .breadcrumb ol { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; color: var(--mist); font-size: 12px; }
    .breadcrumb li + li::before { content: "›"; margin-right: 6px; color: #9fb0cb; }
    .breadcrumb a { color: var(--mist); text-decoration: none; }
    .breadcrumb a:hover { color: var(--blue); text-decoration: underline; }
    .breadcrumb [aria-current] { color: var(--ink); }

    .craft { max-width: 900px; }

    /* The craft page used to be one 900px column left-aligned in whatever width
       the screen had, so a third of a desktop viewport held nothing while the
       numbers a reader came for — fit, price, the two scores — sat below the
       fold under a paragraph. The rail takes that empty third. Mobile keeps the
       same DOM but reorders it: heading, rail, then the article. */
    .craft-railed { display: flex; flex-direction: column; max-width: 1180px; }
    .craft-railed > .craft-head { order: 1; }
    .craft-railed > .craft-rail { order: 2; margin: 0 0 28px; }
    .craft-railed > .craft-body { order: 3; min-width: 0; }
    @media (min-width: 1120px) {
      .craft-railed { display: grid; grid-template-columns: minmax(0, 1fr) 296px; column-gap: 34px; align-items: start; }
      .craft-railed > .craft-head { grid-column: 1; grid-row: 1; }
      .craft-railed > .craft-body { grid-column: 1; grid-row: 2; }
      /* Sticky under the sticky header, not under the top of the window. */
      .craft-railed > .craft-rail { position: sticky; top: 84px; grid-column: 2; grid-row: 1 / span 2; margin: 0; }
    }
    .craft-rail {
      padding: 16px;
      border: 1px solid var(--line-soft);
      border-radius: 14px;
      background: rgba(255, 255, 255, .92);
      box-shadow: 0 10px 30px rgba(23, 54, 110, .08);
    }
    .rail-eyebrow { margin: 0 0 9px; color: var(--blue-text); font-family: var(--display); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: var(--track-micro); }
    .rail-band { margin: 9px 0 0; color: var(--slate); font-size: 12.5px; line-height: 1.45; }
    .rail-kv { display: grid; margin: 13px 0 0; padding-top: 12px; border-top: 1px solid var(--line-soft); }
    .rail-kv > div { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 4px 0; }
    .rail-kv dt { color: var(--mist); font-size: 12.5px; }
    .rail-kv dd { margin: 0; font-family: var(--display); font-size: 15px; font-weight: 700; }
    .rail-kv dd span { color: var(--mist); font-size: 11px; font-weight: 600; }
    .rail-price { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin: 12px 0 0; padding-top: 12px; border-top: 1px solid var(--line-soft); color: var(--mist); font-size: 12.5px; }
    .rail-price strong { color: var(--ink); font-family: var(--display); font-size: 18px; }
    .rail-actions { display: grid; gap: 8px; margin-top: 14px; }
    .rail-actions .btn { justify-content: center; text-align: center; }
    .rail-correct { color: var(--mist); font-size: 12px; text-align: center; }
    /* Four numbers above the fold. The full spec table below keeps category,
       skill level and price; this is only what a reader compares. */
    dl.quick-specs { display: grid; grid-template-columns: repeat(auto-fit, minmax(108px, 1fr)); gap: 1px; margin: 18px 0 0; overflow: hidden; border-radius: 12px; background: var(--line-soft); }
    dl.quick-specs > div { padding: 10px 13px; background: var(--panel); }
    dl.quick-specs dt { color: var(--mist); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .09em; }
    dl.quick-specs dd { margin: 3px 0 0; font-family: var(--display); font-size: 16px; font-weight: 700; }
    .craft-head { margin-bottom: 26px; }
    .craft-head h1 { margin: 6px 0 14px; font-size: clamp(34px, 5vw, 58px); }
    .craft-head .eyebrow a { color: inherit; }
    .craft-lead { margin: 0 0 30px; overflow: hidden; border: 1px solid var(--line-soft); border-radius: var(--radius); background: #fff; }
    .craft-lead img { display: block; width: 100%; max-height: 62vh; height: auto; object-fit: contain; background: #fff; }
    .craft-lead figcaption,
    .static-gallery figcaption { padding: 9px 14px; border-top: 1px solid var(--line-soft); color: var(--mist); font-size: 12px; }
    .credit { opacity: .82; }

    /* Method-page diagrams. Drawn at build time from one craft's real record, so
       the type sizes are fixed in user units and the whole drawing scales with the
       column rather than reflowing. */
    .method-fig { margin: 26px 0 30px; padding: 20px 18px 16px; border: 1px solid var(--line-soft); border-radius: 14px; background: rgba(255,255,255,.72); }
    .method-fig svg { display: block; width: 100%; height: auto; overflow: visible; }
    .method-fig figcaption { margin-top: 14px; padding-top: 13px; border-top: 1px solid var(--line-soft); color: var(--mist); font-size: 12.5px; line-height: 1.55; }
    .method-fig figcaption strong { color: var(--slate); }
    .method-fig figcaption code { font-size: 12px; }
    .method-fig text { font-family: var(--body); font-size: 13px; fill: var(--slate); }
    .mf-step { font-family: var(--display); font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: var(--track-micro); fill: var(--mist); }
    .mf-v { font-family: var(--display); font-weight: 700; fill: var(--ink); }
    .mf-note { font-size: 11.5px; fill: var(--mist); }
    .mf-box path, .mf-box rect { fill: rgba(47,107,237,.05); stroke: var(--blue); stroke-width: 1.4; stroke-dasharray: 5 4; }
    .mf-dim text { font-size: 12.5px; }
    .mf-arrow { fill: none; stroke: var(--mist); stroke-width: 1.2; }
    .mf-sum text { font-size: 13.5px; }
    .mf-total { font-family: var(--display); font-size: 17px; font-weight: 750; fill: var(--ink); }
    .mf-rule { stroke: var(--line); stroke-width: 1; }
    .mf-s-out { stop-color: var(--coral-tint); }
    .mf-s-work { stop-color: var(--amber-tint); }
    .mf-s-fit { stop-color: var(--blue-tint); }
    .mf-strip { stroke: var(--line-soft); stroke-width: 1; }
    .mf-tick line { stroke: var(--mist); stroke-width: 1; }
    .mf-tick text { font-size: 11.5px; fill: var(--mist); }
    .mf-design line { stroke: var(--ink); stroke-width: 2.2; }
    .mf-design text { font-family: var(--display); font-size: 12.5px; font-weight: 700; fill: var(--ink); }
    /* Section drawings */
    .mf-hull { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: round; }
    .mf-fill.good { fill: rgba(47,107,237,.16); }
    .mf-fill.bad { fill: rgba(236,106,84,.18); }
    .mf-water { stroke: var(--blue); stroke-width: 1.6; }
    .mf-wl { font-size: 10.5px; fill: var(--mist); }
    .mf-wlv { font-family: var(--display); font-size: 11.5px; font-weight: 700; fill: var(--blue-text); }
    .mf-lead { fill: none; stroke: var(--line); stroke-width: 1; }
    .mf-axis { stroke: var(--line); stroke-width: 1; stroke-dasharray: 3 3; }
    .mf-pt circle { fill: var(--ink); }
    .mf-pt text { font-family: var(--display); font-size: 12px; font-weight: 700; fill: var(--ink); }
    .mf-pt.m circle, .mf-pt.m text { fill: var(--blue); }
    .mf-pt.g circle, .mf-pt.g text { fill: var(--mist); }
    .mf-ring { fill: none; stroke: var(--mist); stroke-width: 1.2; }
    .mf-gm line { stroke: var(--blue); stroke-width: 1.4; }
    .mf-gm text { font-family: var(--display); font-size: 11.5px; font-weight: 700; fill: var(--blue-text); }
    .mf-gm.neg line { stroke: var(--coral); }
    .mf-gm.neg text { fill: var(--out); }
    .mf-divide { stroke: var(--line-soft); stroke-width: 1; }
    .mf-rows text { font-size: 12.5px; fill: var(--mist); }
    .mf-score { font-family: var(--display); font-size: 13.5px; font-weight: 700; }
    .mf-score.good { fill: var(--blue-text); }
    .mf-score.bad { fill: var(--out); }
    /* Squeezing a two-panel section drawing into a phone's width shrinks its
       labels past reading. Below 700px the drawing keeps a floor and the reader
       pans instead; the caption still wraps to the column. */
    .mf-scroll { overflow-x: auto; }
    .mf-hint { display: none; }
    @media (max-width: 700px) {
      .method-fig { padding: 14px 12px 12px; }
      .method-fig text { font-size: 15px; }
      .mf-scroll svg { min-width: 560px; }
      .mf-hint { display: block; margin: 9px 0 0; color: var(--mist); font-size: 11.5px; }
    }
    .craft-section { margin: 0 0 38px; }
    .craft-section > h2 { margin: 0 0 14px; font-size: clamp(22px, 2.6vw, 30px); }
    .craft-section > h3 { margin: 22px 0 10px; font-size: 16px; }
    /* The spec table is a <dl> here rather than the app's divs, because on a page
       these really are term/description pairs and screen readers should hear that. */
    dl.spec-table { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 1px; margin: 0; overflow: hidden; border-radius: 12px; background: var(--line-soft); }
    dl.spec-table .spec-cell { margin: 0; padding: 13px 15px; background: var(--panel); }
    dl.spec-table dt { color: var(--mist); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .09em; }
    dl.spec-table dd { margin: 4px 0 0; font-size: 17px; font-weight: 600; }
    /* auto-fit leaves the container's 1px-gap background showing as a grey block
       wherever the last row runs short (6 cells into 5 columns). Stretching the
       last cell to the end line fills whatever the row has left, at every count. */
    dl.spec-table > .spec-cell:last-child { grid-column-end: -1; }

    .static-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 14px; }
    .static-gallery figure { margin: 0; overflow: hidden; border: 1px solid var(--line-soft); border-radius: 12px; background: #fff; }
    .static-gallery img { display: block; width: 100%; height: auto; }

    .brand-card { display: flex; flex-direction: column; gap: 3px; padding: 15px 17px; border: 1px solid var(--line-soft); border-radius: 13px; background: var(--panel); color: inherit; text-decoration: none; transition: border-color .18s ease, transform .18s ease; }
    .brand-card:hover { border-color: #aec0e0; transform: translateY(-2px); }
    .brand-card strong { font-family: var(--display); font-size: 17px; }

    /* The weight control. One component, used in the hero, the wizard step and the
       chart filter bar, because it is one number and it should look like one thing
       everywhere it can be changed. The field is a real number input so the value
       can be typed rather than hunted for with a slider. */
    .field-caption { margin: 0 0 8px; color: var(--mist); font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; }
    .weight-set-row { display: flex; align-items: stretch; gap: 8px; }
    .weight-input {
      flex: 1; display: flex; align-items: baseline; justify-content: center; gap: 5px;
      padding: 6px 10px; border: 1px solid var(--line); border-radius: 12px; background: var(--panel-2);
    }
    .weight-input input {
      width: 100%; max-width: 4.2ch; padding: 0; border: 0; background: none;
      color: var(--blue); font-family: var(--display); font-size: 34px; font-weight: 700;
      line-height: 1.15; letter-spacing: -.03em; text-align: right;
    }
    .weight-input input:focus { outline: 2px solid var(--blue); outline-offset: 4px; border-radius: 3px; }
    /* Chrome and Safari draw spinners that clip a 34px display face; the ± buttons
       beside the field do the same job at a size a thumb can hit. */
    .weight-input input::-webkit-outer-spin-button,
    .weight-input input::-webkit-inner-spin-button { margin: 0; -webkit-appearance: none; appearance: none; }
    .weight-input input[type=number] { -moz-appearance: textfield; appearance: textfield; }
    .weight-input b { color: var(--slate); font-size: 14px; font-weight: 600; }
    .weight-step {
      width: 42px; border: 1px solid var(--line); border-radius: 12px; background: var(--panel);
      color: var(--slate); font-size: 19px; line-height: 1; cursor: pointer;
    }
    .weight-step:hover { border-color: var(--blue); background: var(--blue-tint); color: var(--blue-deep); }
    .weight-range { width: 100%; margin-top: 12px; }

    /* Chart chrome that browse already had: a count of what is drawn, and a legend
       that says how many makers are showing instead of only implying it. */
    .chart-meta { align-items: flex-start; gap: 12px; }
    .legend-actions { display: flex; align-items: center; gap: 12px; width: 100%; margin-bottom: 4px; }
    .chart-empty {
      position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
      gap: 10px; padding: 24px; border-radius: var(--radius); background: rgba(255,255,255,.9); text-align: center;
    }
    .chart-empty h3 { margin: 0; }
    .chart-empty p { max-width: 34ch; margin: 0; }

    /* --- Contact ---------------------------------------------------------- */
    /* The form sits inside the same .craft article the other static pages use,
       so it inherits the measure and the heading rhythm and only needs controls. */
    .contact-page { max-width: 720px; }
    /* The form, the craft field and the confirmation are all flex containers, and
       a display rule outranks the hidden attribute's UA default. Without this the
       "sent" panel appears underneath a form that never went away. */
    .contact-page [hidden] { display: none !important; }
    .contact-lede { margin: 0 0 30px; color: var(--slate); font-size: 17px; line-height: 1.6; }
    .contact-form { display: flex; flex-direction: column; gap: 22px; }
    .contact-row { display: grid; gap: 22px; grid-template-columns: 1fr 1fr; }
    .contact-field { display: flex; flex-direction: column; gap: 7px; min-width: 0; padding: 0; border: 0; margin: 0; }
    .contact-field > label, .contact-field > legend {
      padding: 0; color: var(--ink); font-size: 12px; font-weight: 600;
      letter-spacing: var(--track-micro); text-transform: uppercase;
    }
    .contact-field > label i { color: var(--mist); font-style: normal; font-weight: 500; text-transform: none; letter-spacing: 0; }
    .contact-field input, .contact-field textarea {
      width: 100%; padding: 11px 13px; border: 1px solid var(--line); border-radius: var(--radius-sm);
      color: var(--ink); background: var(--panel);
      font-family: var(--body); font-size: 15px; line-height: 1.5;
    }
    .contact-field textarea { resize: vertical; min-height: 150px; }
    .contact-field input:focus, .contact-field textarea:focus {
      border-color: var(--blue); outline: 2px solid var(--blue); outline-offset: 1px;
    }
    /* Invalid is a state the reader is put into, never one they arrive in. It is
       set by the script after a submit or a blur on a field they have touched;
       there is deliberately no :invalid rule, because an empty required field
       matches that from the moment the page loads. */
    .contact-field.is-invalid input, .contact-field.is-invalid textarea { border-color: var(--out); }
    /* Firefox draws its own red glow on a required-but-empty control. That is
       the arrival-state scolding this section exists to prevent. */
    .contact-field input:invalid, .contact-field textarea:invalid { box-shadow: none; }
    .contact-error { margin: 0; color: var(--out); font-size: 13px; }
    .contact-hint { color: var(--mist); font-size: 12px; font-style: normal; }
    .contact-foot-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
    .contact-foot-row .contact-error { flex: 1 1 auto; }
    .contact-count { flex: none; color: var(--mist); font-size: 12px; }
    .contact-count.is-tight { color: var(--out); }

    /* The topic is the decision that shapes the rest of the form, so it is asked
       first and answered in one tap. A native select hides four of five options
       behind an interaction and tells the reader nothing about what this page is
       for; the radios underneath keep it a real radio group for keyboard and
       assistive tech, and keep the no-JavaScript post identical. */
    .topic-pills { display: flex; flex-wrap: wrap; gap: 8px; }
    .topic-pills input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
    .topic-pills label {
      padding: 9px 16px; border: 1px solid var(--line); border-radius: 999px;
      color: var(--slate); background: var(--panel);
      font-size: 14px; font-weight: 500; cursor: pointer;
      transition: border-color .14s ease, background .14s ease, color .14s ease;
    }
    .topic-pills label:hover { border-color: var(--blue); color: var(--blue-text); }
    .topic-pills input:checked + label { border-color: var(--blue); color: var(--white); background: var(--blue); font-weight: 600; }
    .topic-pills input:focus-visible + label { outline: 2px solid var(--blue); outline-offset: 2px; }

    /* Off-screen rather than display:none — a bot that skips hidden inputs still
       fills this one, which is the entire point of it. */
    .contact-gotcha { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
    /* Turnstile injects its iframe after first paint. Without a reserved box the
       Send button jumps down the page under the reader's cursor. */
    .contact-form .cf-turnstile { min-height: 65px; }

    .contact-actions { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
    .contact-submit {
      display: inline-flex; align-items: center; gap: 9px;
      padding: 13px 34px; border: 0; border-radius: var(--radius-sm);
      color: var(--white); background: var(--blue);
      font-family: var(--body); font-size: 15px; font-weight: 600; cursor: pointer;
    }
    .contact-submit:hover { background: var(--blue-deep); }
    .contact-submit:disabled { background: var(--mist); cursor: default; }
    .contact-submit.is-sending::after {
      content: ""; width: 14px; height: 14px; border-radius: 50%;
      border: 2px solid rgba(255, 255, 255, .45); border-top-color: var(--white);
      animation: contactSpin .7s linear infinite;
    }
    @keyframes contactSpin { to { transform: rotate(360deg); } }
    .contact-status { margin: 0; color: var(--slate); font-size: 14px; }
    .contact-status.is-error { color: var(--out); }

    .contact-done {
      padding: 32px 30px; border: 1px solid var(--fit); border-radius: var(--radius);
      background: var(--fit-tint); text-align: center;
      animation: contactDone .3s ease both;
    }
    .contact-done:focus { outline: none; }
    @keyframes contactDone { from { opacity: 0; transform: translateY(6px); } }
    .contact-done .done-mark {
      width: 46px; height: 46px; margin: 0 auto 14px; border-radius: 50%;
      color: var(--white); background: var(--fit);
      font-size: 25px; font-weight: 700; line-height: 46px;
    }
    .contact-done h2 { margin: 0 0 8px; font-size: 27px; }
    .contact-done p { margin: 0 auto; max-width: 46ch; color: var(--slate); }
    .done-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 22px; }
    .contact-foot { margin-top: 30px; }

    @media (max-width: 560px) {
      .contact-row { grid-template-columns: 1fr; }
      .contact-submit { flex: 1 1 auto; justify-content: center; }
      .done-actions .btn { flex: 1 1 auto; justify-content: center; text-align: center; }
    }

    /* --- Feedback prompts --------------------------------------------------- */
    /* Deliberately not a floating tab. The button lives in the header chrome on
       every page, and the fuller prompt appears only where a reader has just
       finished reading a number they might be in a position to contradict: the
       end of a craft page, the end of the detail modal, and under the method.
       Nothing overlays content and nothing follows the scroll. */
    .header-feedback {
      display: inline-flex; align-items: center; gap: 7px;
      padding: 7px 13px;
      border: 1px solid var(--blue-tint); border-radius: 10px;
      color: var(--blue-text); background: var(--blue-tint);
      font-size: 13px; font-weight: 600; text-decoration: none; white-space: nowrap;
    }
    .header-feedback:hover { border-color: var(--blue); color: var(--white); background: var(--blue); }
    .header-feedback svg { width: 15px; height: 15px; flex: none; }

    .feedback-note {
      display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 20px;
      margin: 36px 0 0; padding: 22px 26px;
      border: 1px solid var(--blue-tint); border-radius: var(--radius);
      background: var(--wash);
    }
    .feedback-note > div { flex: 1 1 340px; min-width: 0; }
    .feedback-note .eyebrow { margin: 0 0 5px; }
    .feedback-note p { margin: 0; color: var(--slate); }
    .feedback-note .btn { flex: none; }
    /* Inside the modal the panel is already a card, so the prompt drops the tint
       and reads as the last section rather than a second surface on top of one. */
    .detail-panel .feedback-note { background: var(--panel-2); }

    @media (max-width: 720px) {
      /* The label goes to screen readers rather than away: the accessible name
         has to survive, and an icon-only link with no name is a dead end. */
      .header-feedback .label {
        position: absolute; width: 1px; height: 1px;
        overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
      }
      .header-feedback { padding: 8px 9px; }
      .feedback-note { padding: 20px; }
      .feedback-note .btn { width: 100%; text-align: center; justify-content: center; }
    }
