/* ============================================================================
   GRAVELY x KOENIG DESIGN SYSTEM — single entry point.
   Drop this whole file into a HubSpot stylesheet, or link it. Order matters:
   fonts and tokens first, base second, components last.

   ⚠️  typography.css is imported AFTER colors.css on purpose. The two files no
   longer share any token name (--gv-text-... for sizes, --gv-color-... and --gv-ink... for colour), so the
   order is safe — but the PDM system shipped exactly this import order with a
   name collision and rendered body text black-on-black. Do not add a colour
   token named --gv-text-anything.
   ========================================================================= */
@import url("https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;600;700&display=swap");

/* ============================================================================
   GRAVELY x KOENIG — FONT LOADING

   ⚠️  WHAT IS AND IS NOT SHIPPABLE — MEASURED 2026-09-03 (fontTools, OS/2.fsType),
       not read off a licence page:

     United Sans Reg  House Industries  fsType 0  Installable Embedding  -> SHIPPED (woff2)
     Hyperspace Race  Swell Type        fsType 8  Editable Embedding     -> SHIPPED (woff2)
     DIN Next LT Pro  Linotype          fsType 4  PREVIEW & PRINT ONLY   -> NOT SHIPPED

   AriensCo's own Brandfolder distributes these to dealers in DESKTOP FORMATS ONLY
   (measured: 88 font attachments, 0 woff/woff2). gravely.com serves DIN Next from a
   Monotype web kit (30AD8D_*.woff2) that AriensCo licensed for itself — Koenig does not
   inherit it. So the body face here is Barlow until Adam decides otherwise; see
   docs/DECISIONS.md #1. Swapping back is one line: change --gv-font-body.

   Barlow was chosen by measurement, not taste: against DIN Next it drifts -2.3% on set
   width (the smallest of five candidates) and its x-height/cap-height ratio is 0.723 vs
   DIN's 0.704 (the closest). Proof sheet: exports/font-substitute-proof.png.
   ========================================================================= */

/* NOTE: these paths are relative to THIS FILE (tokens/), not to the page. That is how
   url() works inside a stylesheet, and getting it wrong 404s silently — the page still
   renders, just in the fallback face. Measured 2026-09-03: every United Sans face was
   404ing as tokens/assets/fonts/... and nothing said so. If this CSS is flattened into a
   HubSpot stylesheet, re-point these to the File Manager URLs. */
@font-face {
  font-family: "United Sans Reg";
  src: url("https://45668251.fs1.hubspotusercontent-na1.net/hubfs/45668251/gravely/fonts/UnitedSansReg-Medium.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "United Sans Reg";
  src: url("https://45668251.fs1.hubspotusercontent-na1.net/hubfs/45668251/gravely/fonts/UnitedSansReg-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "United Sans Reg";
  src: url("https://45668251.fs1.hubspotusercontent-na1.net/hubfs/45668251/gravely/fonts/UnitedSansReg-Heavy.woff2") format("woff2");
  font-weight: 800; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "United Sans Reg";
  src: url("https://45668251.fs1.hubspotusercontent-na1.net/hubfs/45668251/gravely/fonts/UnitedSansReg-Black.woff2") format("woff2");
  font-weight: 900; font-style: normal; font-display: swap;
}
/* Campaign face. Reserved for secondary campaign lock-ups ("Mow Like A Pro"), never
   for headings, never for body. Brand guide p.16: campaign marks are accents. */
@font-face {
  font-family: "Hyperspace Race";
  src: url("https://45668251.fs1.hubspotusercontent-na1.net/hubfs/45668251/gravely/fonts/HyperspaceRace-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

:root {
  /* Display — the brand signature. Always uppercase, always heavy. */
  --gv-font-display: "United Sans Reg", "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  /* Body. Swap this ONE value to return to DIN Next if a web licence is bought. */
  --gv-font-body:    "Barlow", "DIN Next LT Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
  /* Campaign only. */
  --gv-font-campaign:"Hyperspace Race", "United Sans Reg", sans-serif;
  /* Figures in spec tables — tabular so columns line up. */
  --gv-font-numeric: "Barlow", "Helvetica Neue", Helvetica, Arial, sans-serif;
}

/* ============================================================================
   GRAVELY x KOENIG — COLOUR
   Source of truth: GRAVELY 1.0 Brand Standards Guide (AriensCo, © 2026), pp. 13 & 17,
   plus the live Brandfolder "Colors" section (collection ktq32jp387p8sshwrbvw8n).

   ⚠️  NAMING RULE (paid for on the PDM system, where --text-body was defined BOTH as a
   colour and as a type size; the size won and body copy rendered black-on-black):
   colour tokens are --gv-color-*, type sizes are --gv-text-*. They never share a name.

   ⚠️  EVERY RATIO BELOW WAS COMPUTED, NOT TRANSCRIBED — `python3 scripts/contrast.py`.
   Re-run it after any change. Do not edit a ratio by hand.
   ========================================================================= */

:root {
  /* ── PRIMARY (brand guide p.17) ─────────────────────────────────────── */
  --gv-red:              #DA291C;  /* PMS 485C  · CMYK 0/95/100/0  */
  --gv-black:            #212721;  /* PMS Black 3C               */
  --gv-white:            #FEFEFF;  /* never pure #FFF — the guide specifies FEFEFF */

  /* ── SECONDARY — accents only. "do not rely on them too heavily" (p.17) ─ */
  --gv-gray-dark:        #848689;
  --gv-gray-mid:         #B8BABC;

  /* ── ELECTRIC (EV sub-brand) — Brandfolder only, absent from the 1.0 guide ─
     Use ONLY on electric-product surfaces, and only on the Electric Dark ground. */
  --gv-electric-blue:    #00B2E3;
  --gv-electric-dark:    #101820;

  /* ── DERIVED NEUTRALS ───────────────────────────────────────────────────
     The brand grays fail as body text (measured). These are the same blue-gray
     hue pushed to a legible value, and they exist for exactly that reason. */
  --gv-gray-body-light:  #636568;  /* muted body ON WHITE  — 5.80:1 PASS AA   */
  --gv-gray-body-dark:   #B8BABC;  /* muted body ON BLACK  — 7.84:1 PASS AAA  */
  --gv-hairline-light:   #D2D4D5;  /* 1px rule on white — decorative, not meaning-bearing */
  --gv-hairline-dark:    #454B45;  /* 1px rule on black — decorative                      */
  --gv-border-light:     #848689;  /* meaning-bearing border on white — 3.62:1 PASS 1.4.11 */
  --gv-border-dark:      #848689;  /* meaning-bearing border on black — 4.18:1 PASS 1.4.11 */

  /* ── GROUNDS ────────────────────────────────────────────────────────────
     Two, and only two. A third ground is how a system starts to drift. */
  --gv-ground-dark:      #212721;
  --gv-ground-light:     #FEFEFF;
  --gv-ground-inset:     #F2F2F3;  /* a well on the light ground: cards, spec tables */
  --gv-ground-inset-dark:#2C332C;  /* a well on the dark ground                       */

  /* ── SEMANTIC INK ───────────────────────────────────────────────────────
     Set by the ground class, not chosen per element. See base.css. */
  --gv-ink:              var(--gv-black);
  --gv-ink-muted:        var(--gv-gray-body-light);
  --gv-ink-inverse:      var(--gv-white);
  --gv-rule:             var(--gv-hairline-light);
  --gv-edge:             var(--gv-border-light);

  /* ── ACTION ─────────────────────────────────────────────────────────────
     ONE action colour. Rank comes from the button VARIANT, never from a new hue.
     (Measured on gravely.com 2026-09-03: its own primary CTA is a red surface with a
     white uppercase United Sans label and border-radius 0. This matches.) */
  /* ⚠️  RED AS TEXT IS DISPLAY-ONLY. Measured: 4.83:1 on --gv-ground-light, but only
     4.35:1 on --gv-ground-inset, and 3.13:1 on black. It clears AA for normal text on
     exactly ONE of the three grounds, so the system does not allow it as body copy
     anywhere. Red text = >=24px, or >=19px at weight 700. Otherwise red is a SURFACE
     and the ink on it is white. */
  --gv-action:           var(--gv-red);
  --gv-action-ink:       var(--gv-white);   /* 4.83:1 on red — PASS AA */
  --gv-action-hover:     #B31F14;           /* red darkened; white ink 6.67:1 — PASS AA */
}

/* The dark ground re-points the semantic ink. Nothing else changes. */
.gv-dark {
  --gv-ink:        var(--gv-white);
  --gv-ink-muted:  var(--gv-gray-body-dark);
  --gv-ink-inverse:var(--gv-black);
  --gv-rule:       var(--gv-hairline-dark);
  --gv-edge:       var(--gv-border-dark);
  background: var(--gv-ground-dark);
  color: var(--gv-ink);
}
.gv-light {
  --gv-ink:        var(--gv-black);
  --gv-ink-muted:  var(--gv-gray-body-light);
  --gv-ink-inverse:var(--gv-white);
  --gv-rule:       var(--gv-hairline-light);
  --gv-edge:       var(--gv-border-light);
  background: var(--gv-ground-light);
  color: var(--gv-ink);
}
/* EV surfaces only. Electric Blue is 2.46:1 on white — it may NEVER appear on a light ground. */
.gv-electric {
  --gv-ink:        var(--gv-white);
  --gv-ink-muted:  var(--gv-gray-body-dark);
  --gv-action:     var(--gv-electric-blue);
  --gv-action-ink: var(--gv-electric-dark);   /* 7.22:1 — PASS AA */
  --gv-rule:       #263039;
  --gv-edge:       #6A7783;   /* 3.90:1 on Electric Dark — PASS 1.4.11.
                                 #55636E was tried first and MEASURED 2.89:1 — a fail. */
  background: var(--gv-electric-dark);
  color: var(--gv-ink);
}

/* ============================================================================
   GRAVELY x KOENIG — TYPE SCALE
   Brand guide p.18: United Sans = headline · DIN Next = body · Hyperspace Race =
   secondary (campaign) headline.

   ⚠️  These are SIZE tokens (--gv-text-*). Colour tokens are --gv-color-* / --gv-ink*.
       The two namespaces never overlap. See the warning at the top of colors.css.

   The scale is fluid between 390px and 1440px. Every step is clamp()ed so a phone
   never gets a 96px headline and a desktop never gets a timid one.
   ========================================================================= */

:root {
  /* ── DISPLAY (United Sans, uppercase) ─────────────────────────────────── */
  --gv-text-hero:    clamp(2.75rem, 1.30rem + 5.95vw, 6.50rem);  /*  44 -> 104 px */
  --gv-text-display: clamp(2.25rem, 1.38rem + 3.57vw, 4.50rem);  /*  36 ->  72 px */
  --gv-text-h1:      clamp(2.00rem, 1.42rem + 2.38vw, 3.50rem);  /*  32 ->  56 px */
  --gv-text-h2:      clamp(1.50rem, 1.16rem + 1.39vw, 2.38rem);  /*  24 ->  38 px */
  --gv-text-h3:      clamp(1.25rem, 1.11rem + 0.60vw, 1.63rem);  /*  20 ->  26 px */

  /* ── BODY (Barlow / DIN Next) ─────────────────────────────────────────── */
  --gv-text-lead:    clamp(1.13rem, 1.03rem + 0.40vw, 1.38rem);  /*  18 ->  22 px */
  --gv-text-body:    1.0625rem;                                  /*      17 px    */
  --gv-text-small:   0.9375rem;                                  /*      15 px    */
  --gv-text-fine:    0.8125rem;                                  /*      13 px    */

  /* ── UTILITY ──────────────────────────────────────────────────────────── */
  --gv-text-eyebrow: 0.75rem;                                    /*      12 px    */
  --gv-text-label:   0.8125rem;                                  /*      13 px    */
  /* The signature readout — a model number, a deck size, a year. */
  --gv-text-readout: clamp(2.25rem, 1.52rem + 3.00vw, 4.13rem);  /*  36 ->  66 px */

  /* ── WEIGHT ───────────────────────────────────────────────────────────── */
  --gv-weight-display: 800;   /* United Sans Heavy — the default for headings   */
  --gv-weight-black:   900;   /* United Sans Black — hero + readout only        */
  --gv-weight-bold:    700;
  --gv-weight-medium:  500;
  --gv-weight-regular: 400;

  /* ── LINE HEIGHT ──────────────────────────────────────────────────────── */
  --gv-leading-hero:  0.92;   /* heavy caps set TIGHT; that tension is the brand */
  --gv-leading-tight: 1.02;
  --gv-leading-head:  1.12;
  --gv-leading-body:  1.55;
  --gv-leading-loose: 1.68;   /* long-form article copy                          */

  /* ── TRACKING ─────────────────────────────────────────────────────────── */
  --gv-tracking-hero:    -0.005em;  /* United Sans caps need almost none        */
  --gv-tracking-display:  0.005em;
  --gv-tracking-eyebrow:  0.16em;   /* micro-labels open right up               */
  --gv-tracking-label:    0.08em;
  --gv-tracking-body:     0;

  /* ── MEASURE ──────────────────────────────────────────────────────────── */
  --gv-measure:      64ch;    /* body copy never runs wider                      */
  --gv-measure-lead: 46ch;    /* a lead paragraph is shorter on purpose          */
}

/* ============================================================================
   GRAVELY x KOENIG — SPACE & GRID
   An 8px base. Every gap in the system is one of these; nothing is hand-nudged.
   ========================================================================= */
:root {
  --gv-space-1:  0.25rem;   /*  4 */
  --gv-space-2:  0.5rem;    /*  8 */
  --gv-space-3:  0.75rem;   /* 12 */
  --gv-space-4:  1rem;      /* 16 */
  --gv-space-5:  1.5rem;    /* 24 */
  --gv-space-6:  2rem;      /* 32 */
  --gv-space-7:  3rem;      /* 48 */
  --gv-space-8:  4rem;      /* 64 */
  --gv-space-9:  6rem;      /* 96 */
  --gv-space-10: 8rem;      /*128 */

  /* Band padding — the vertical rhythm of a page, fluid. */
  --gv-band-y:       clamp(3rem, 1.60rem + 5.71vw, 6.50rem);
  --gv-band-y-tight: clamp(2rem, 1.40rem + 2.46vw, 3.50rem);

  /* Page shell */
  --gv-gutter:    clamp(1.25rem, 0.55rem + 2.86vw, 3rem);
  --gv-max:       1280px;   /* standard content width  */
  --gv-max-wide:  1520px;   /* galleries, full grids   */
  --gv-max-text:  46rem;    /* article column          */

  /* Grid */
  --gv-grid-gap:  clamp(1rem, 0.60rem + 1.63vw, 2rem);

  /* Hit targets — 44px minimum, no exceptions. */
  --gv-hit: 44px;
}

/* ============================================================================
   GRAVELY x KOENIG — EDGES
   The system separates things with RULES and EDGES. No shadows, no gradients,
   no glassmorphism, no glow. The brand guide forbids effects on the logo; the
   same restraint runs through the chrome, and it is what keeps photography loud.
   ========================================================================= */
:root {
  --gv-rule-hair:  1px;   /* decorative divider          */
  --gv-rule-mid:   2px;   /* section rule, table header  */
  --gv-rule-heavy: 4px;   /* the RED RULE — see below    */
  --gv-rule-slab:  8px;   /* a slab edge, hero underline */

  /* ── RADIUS ────────────────────────────────────────────────────────────
     ZERO. Everywhere. Measured on gravely.com 2026-09-03: its own primary CTAs
     render at border-radius 0px. The ONLY curve in the system is the "G" badge
     itself, and that curve is the mark's, not the chrome's. */
  --gv-radius: 0;

  /* ── SHADOW ────────────────────────────────────────────────────────────
     One, and only for a floating overlay (drawer, modal) where depth is the
     only way to say "above". Never on a card, never on a button. */
  --gv-shadow-overlay: 0 24px 64px rgba(0,0,0,.45);
}

/* ============================================================================
   GRAVELY x KOENIG — MOTION
   Short, linear-ish, mechanical. Nothing bounces. Nothing floats. Machines move
   with intent; so does this.
   ========================================================================= */
:root {
  --gv-dur-fast:  120ms;
  --gv-dur-base:  180ms;
  --gv-dur-slow:  280ms;
  --gv-ease:      cubic-bezier(.2,.6,.3,1);
  --gv-ease-out:  cubic-bezier(0,0,.25,1);
}
@media (prefers-reduced-motion: reduce) {
  :root { --gv-dur-fast: 1ms; --gv-dur-base: 1ms; --gv-dur-slow: 1ms; }
  *, *::before, *::after {
    animation-duration: 1ms !important; animation-iteration-count: 1 !important;
    transition-duration: 1ms !important; scroll-behavior: auto !important;
  }
}

/* ============================================================================
   GRAVELY x KOENIG — BASE
   Element defaults. Everything above the components; nothing decorative here.
   ========================================================================= */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--gv-ground-light);
  color: var(--gv-ink);
  font-family: var(--gv-font-body);
  font-size: var(--gv-text-body);
  line-height: var(--gv-leading-body);
  font-weight: var(--gv-weight-regular);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ── HEADINGS ────────────────────────────────────────────────────────────
   United Sans, uppercase, heavy, tight. This is the loudest thing the brand
   does with type and it is not negotiable per element. */
h1, h2, h3, h4, .gv-display {
  font-family: var(--gv-font-display);
  font-weight: var(--gv-weight-display);
  text-transform: uppercase;
  letter-spacing: var(--gv-tracking-display);
  line-height: var(--gv-leading-head);
  color: var(--gv-ink);
  margin: 0 0 var(--gv-space-4);
  text-wrap: balance;
}
h1 { font-size: var(--gv-text-h1); line-height: var(--gv-leading-tight); }
h2 { font-size: var(--gv-text-h2); }
h3 { font-size: var(--gv-text-h3); }
h4 { font-size: var(--gv-text-body); letter-spacing: var(--gv-tracking-label); }

p { margin: 0 0 var(--gv-space-4); max-width: var(--gv-measure); }
p:last-child { margin-bottom: 0; }

a { color: inherit; text-underline-offset: 0.18em; text-decoration-thickness: 1px; }

img, svg, video { max-width: 100%; height: auto; display: block; }

hr {
  border: 0; height: var(--gv-rule-hair);
  background: var(--gv-rule);
  margin: var(--gv-space-7) 0;
}

/* ── FOCUS — visible, square, red, never removed ─────────────────────── */
:focus-visible {
  outline: 3px solid var(--gv-red);
  outline-offset: 2px;
}
.gv-dark :focus-visible, .gv-electric :focus-visible {
  outline-color: var(--gv-white);
}

::selection { background: var(--gv-red); color: var(--gv-white); }

/* ── LAYOUT PRIMITIVES ───────────────────────────────────────────────── */
.gv-shell   { max-width: var(--gv-max);      margin-inline: auto; padding-inline: var(--gv-gutter); }
.gv-shell--wide { max-width: var(--gv-max-wide); }
.gv-shell--text { max-width: var(--gv-max-text); }

.gv-band    { padding-block: var(--gv-band-y); }
.gv-band--tight { padding-block: var(--gv-band-y-tight); }
/* A well on the light ground. Use this class rather than an inline background — the accent
   eyebrow has to know it is on the inset, because red at 12px measures 4.35:1 here against
   4.83:1 on pure white. */
.gv-inset { background: var(--gv-ground-inset); }

.gv-grid    { display: grid; gap: var(--gv-grid-gap); }
/* Grid and flex children default to min-width:auto, so one wide child (a spec table, a
   long readout) makes the whole track wider than the viewport. Measured: 253px of
   horizontal scroll at 390px before this line. */
.gv-grid > *, .gv-split > *, .gv-stats > * { min-width: 0; }
.gv-grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.gv-grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }
@media (min-width: 64rem) { .gv-grid--3 { grid-template-columns: repeat(3, 1fr); } }
.gv-grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); }

.gv-stack > * + * { margin-top: var(--gv-space-4); }

/* ── TYPE ROLES ──────────────────────────────────────────────────────── */
.gv-eyebrow {
  font-family: var(--gv-font-display);
  font-size: var(--gv-text-eyebrow);
  font-weight: var(--gv-weight-bold);
  letter-spacing: var(--gv-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--gv-ink-muted);
  margin: 0 0 var(--gv-space-3);
  display: block;
}
/* An eyebrow may be red ONLY at this size when it is BOLD — see the display-only
   rule in colors.css. On the dark ground red drops to 3.13:1, so it turns white. */
.gv-eyebrow--accent { color: var(--gv-red); font-weight: 700; }
.gv-dark .gv-eyebrow--accent, .gv-electric .gv-eyebrow--accent { color: var(--gv-white); }
/* 4.35:1 on the inset ground — below AA for 12px text. Falls back to ink. */
.gv-inset .gv-eyebrow--accent { color: var(--gv-ink); }

.gv-lead {
  font-size: var(--gv-text-lead);
  line-height: 1.45;
  max-width: var(--gv-measure-lead);
  color: var(--gv-ink);
}
.gv-muted { color: var(--gv-ink-muted); }
.gv-fine  { font-size: var(--gv-text-fine); color: var(--gv-ink-muted); }

/* ── THE RED RULE — the system's one recurring ornament ──────────────────
   A 4px red bar that opens a section. It is the only decoration allowed, and it
   is never used twice in the same band. */
.gv-rule-red {
  width: 4rem; height: var(--gv-rule-heavy);
  background: var(--gv-red);
  margin: 0 0 var(--gv-space-5);
  border: 0;
}

/* ── THE READOUT — a number treated as an object ─────────────────────────
   1916. 60". 26 HP. The signature device: United Sans Black, tight, with a
   small tracked label beneath it. */
.gv-readout { display: flex; flex-direction: column; gap: var(--gv-space-1); }
.gv-readout__value {
  font-family: var(--gv-font-display);
  font-size: var(--gv-text-readout);
  font-weight: var(--gv-weight-black);
  line-height: 0.86;
  letter-spacing: var(--gv-tracking-hero);
  color: var(--gv-ink);
  font-variant-numeric: tabular-nums;
}
.gv-readout__label {
  font-family: var(--gv-font-display);
  font-size: var(--gv-text-eyebrow);
  font-weight: var(--gv-weight-bold);
  letter-spacing: var(--gv-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--gv-ink-muted);
}

/* ── THE SLAB — a red or black plate carrying a short, loud line ───────── */
.gv-slab {
  display: inline-block;
  background: var(--gv-red);
  color: var(--gv-white);
  font-family: var(--gv-font-display);
  font-weight: var(--gv-weight-display);
  text-transform: uppercase;
  letter-spacing: var(--gv-tracking-label);
  font-size: var(--gv-text-label);
  padding: var(--gv-space-2) var(--gv-space-4);
}
.gv-slab--black { background: var(--gv-black); color: var(--gv-white); }

/* ── SCREEN-READER ONLY ─────────────────────────────────────────────── */
.gv-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Skip link — visible on focus, never a hidden dead end. */
.gv-skip {
  position: absolute; left: var(--gv-space-4); top: -100px; z-index: 999;
  background: var(--gv-red); color: var(--gv-white);
  padding: var(--gv-space-3) var(--gv-space-4);
  font-family: var(--gv-font-display); text-transform: uppercase;
  letter-spacing: var(--gv-tracking-label); text-decoration: none;
  transition: top var(--gv-dur-fast) var(--gv-ease);
}
.gv-skip:focus { top: var(--gv-space-4); }

/* ============================================================================
   GRAVELY x KOENIG — COMPONENTS
   Plain semantic HTML + CSS, because the implementation target is HubSpot CMS.
   Every class is namespaced .gv- so it can never collide with HubSpot's own CSS
   or with the Koenig main-site chrome.
   ========================================================================= */

/* ══ ACTIONS ═══════════════════════════════════════════════════════════════
   ONE action colour (red). Rank comes from the VARIANT, never from a new hue.
     primary   red surface, white label      — the one thing you want done
     secondary outlined, ink label           — the real alternative
     ghost     underline only                — tertiary, in-flow
   At most ONE primary per band. Two primaries side by side is not a choice,
   it is a shrug.
   ═══════════════════════════════════════════════════════════════════════ */
.gv-btn {
  --_bg: var(--gv-action);
  --_ink: var(--gv-action-ink);
  --_edge: transparent;
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--gv-space-2);
  min-height: var(--gv-hit);
  padding: var(--gv-space-3) var(--gv-space-6);
  background: var(--_bg); color: var(--_ink);
  border: 2px solid var(--_edge);
  border-radius: var(--gv-radius);
  font-family: var(--gv-font-display);
  font-size: var(--gv-text-label);
  font-weight: var(--gv-weight-display);
  text-transform: uppercase;
  letter-spacing: var(--gv-tracking-label);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--gv-dur-fast) var(--gv-ease),
              color var(--gv-dur-fast) var(--gv-ease),
              border-color var(--gv-dur-fast) var(--gv-ease);
}
.gv-btn:hover  { --_bg: var(--gv-action-hover); }
.gv-btn:active { transform: translateY(1px); }

.gv-btn--secondary {
  --_bg: transparent; --_ink: var(--gv-ink); --_edge: var(--gv-ink);
}
.gv-btn--secondary:hover { --_bg: var(--gv-ink); --_ink: var(--gv-ink-inverse); }

.gv-btn--ghost {
  --_bg: transparent; --_ink: var(--gv-ink); --_edge: transparent;
  padding-inline: 0; text-decoration: underline;
  text-decoration-thickness: 2px; text-underline-offset: 0.3em;
  text-decoration-color: var(--gv-red);
}
.gv-btn--ghost:hover { --_bg: transparent; text-decoration-color: currentColor; }

.gv-btn--block { width: 100%; }

/* A phone number is an action, and on a mobile-first dealer site it is often THE
   action. It gets its own treatment so it is never mistaken for body copy. */
.gv-phone {
  display: inline-flex; align-items: center; gap: var(--gv-space-2);
  min-height: var(--gv-hit);
  font-family: var(--gv-font-display);
  font-weight: var(--gv-weight-display);
  font-size: var(--gv-text-h3);
  letter-spacing: var(--gv-tracking-display);
  color: var(--gv-ink); text-decoration: none;
}
.gv-phone:hover { text-decoration: underline; text-decoration-color: var(--gv-red);
                  text-decoration-thickness: 3px; text-underline-offset: 0.25em; }

/* An inline text link inside body copy. Red underline, ink text — because red
   TEXT below 24px fails AA on two of the three grounds (measured). */
.gv-link {
  color: inherit; text-decoration: underline;
  text-decoration-color: var(--gv-red); text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
}
.gv-link:hover { text-decoration-color: currentColor; }

/* ══ NAVIGATION ════════════════════════════════════════════════════════ */
.gv-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--gv-black); color: var(--gv-white);
  border-bottom: var(--gv-rule-heavy) solid var(--gv-red);
}
/* Back to the design's single row: lock-up LEFT, nav and CTA right. A centred two-row header
   was tried on 2026-09-05 and rejected — the ask was only ever a bigger mark in the place the
   design puts it. The size is bounded by the ROW, not by the mark: this nav is four items, which
   leaves real room, and the probe measures the overflow rather than trusting the arithmetic. */
.gv-header__bar {
  display: flex; align-items: center; gap: var(--gv-space-4);
  min-height: 76px;
  max-width: var(--gv-max-wide); margin-inline: auto;
  padding-inline: var(--gv-gutter);
  /* The wordmark is 270.8 x 30.7 in its viewBox — at 30px tall it is 265px wide, which alone is
     68% of a 390px viewport. Measured: logo + co-brand + CTA + toggle overflowed by 253px before
     this row was made shrinkable and the desktop CTA moved to the drawer. */
  min-width: 0; flex-wrap: nowrap;
}
.gv-header__logo {
  display: flex; align-items: center; gap: var(--gv-space-4);
  /* The WRAPPER must not shrink either. Fixing flex on the <svg> alone is not enough: a
     shrinking parent squeezes the child anyway, which is how the mark still came out 264px wide
     where 283px was correct at 992px. */
  flex: 0 0 auto; min-height: var(--gv-hit); text-decoration: none; }
/* The approved Koenig x Gravely lock-up is 1130.95 x 152.1 — aspect 7.44, so every extra pixel
   of HEIGHT costs 7.44 of WIDTH. Filling more of a 76px bar means ~44px, which is 327px wide;
   that fits beside a four-item nav and the CTA from 62rem up. Below that the nav is in the
   drawer, so the mark only has to clear the Menu button. Verified by scripts/verify_header.py,
   which fails on any horizontal overflow rather than trusting this arithmetic. */
.gv-header__logo svg, .gv-header__logo img {
  /* flex: 0 0 auto — NOT 0 1 auto. A shrinkable logo keeps its fixed height and loses width,
     which either distorts the mark or letterboxes it inside a too-narrow box. Measured at 992px:
     327px of artwork squeezed into a 291.8px box. The mark never shrinks; the band below narrows
     it honestly instead. */
  height: clamp(24px, 6.2vw, 44px); width: auto; flex: 0 0 auto;
}
/* 62–75rem is the tight band: the four-item nav and the CTA have just arrived on the row but the
   viewport has not caught up. 38px is what actually fits at 992px, measured. */
@media (min-width: 62rem) and (max-width: 74.99rem) {
  .gv-header__logo svg, .gv-header__logo img { height: 34px; }
  .gv-header__nav { gap: var(--gv-space-4); }
}
.gv-header__nav { display: none; gap: var(--gv-space-5); margin-left: auto; }
.gv-header__cobrand {
  display: none;
  font-family: var(--gv-font-display); font-size: var(--gv-text-eyebrow);
  font-weight: var(--gv-weight-bold); text-transform: uppercase;
  letter-spacing: var(--gv-tracking-eyebrow); color: var(--gv-gray-mid);
  padding-left: var(--gv-space-4); border-left: 1px solid var(--gv-hairline-dark);
  line-height: 1.3;
}
.gv-header__nav a {
  font-family: var(--gv-font-display); font-size: var(--gv-text-label);
  font-weight: var(--gv-weight-display); text-transform: uppercase;
  letter-spacing: var(--gv-tracking-label); color: var(--gv-white);
  text-decoration: none; padding-block: var(--gv-space-3);
  border-bottom: 3px solid transparent;
}
.gv-header__nav a:hover, .gv-header__nav a[aria-current="page"] { border-bottom-color: var(--gv-red); }
.gv-header__actions { display: none; margin-left: auto; align-items: center; gap: var(--gv-space-3); }
.gv-header__toggle {
  margin-left: auto; min-width: var(--gv-hit); min-height: var(--gv-hit);
  background: none; border: 2px solid var(--gv-gray-dark); color: var(--gv-white);
  font-family: var(--gv-font-display); text-transform: uppercase;
  letter-spacing: var(--gv-tracking-label); font-size: var(--gv-text-fine);
  cursor: pointer; border-radius: var(--gv-radius);
}
/* The co-brand line needs room to be legible; below 30rem it is carried by the
   drawer and the footer instead of being crushed next to the mark. */
@media (min-width: 30rem) { .gv-header__cobrand { display: block; } }
@media (min-width: 48rem) { .gv-header__actions { display: flex; margin-left: auto; } }
@media (min-width: 62rem) {
  .gv-header__bar { gap: var(--gv-space-5); }
  .gv-header__nav { display: flex; }
  .gv-header__toggle { display: none; }
}
/* Mobile drawer */
.gv-drawer { display: none; background: var(--gv-black); border-top: 1px solid var(--gv-hairline-dark); }
.gv-drawer[data-open="true"] { display: block; }
.gv-drawer .gv-btn { margin: var(--gv-space-4) var(--gv-gutter); width: calc(100% - 2 * var(--gv-gutter)); }
.gv-drawer a:not(.gv-btn) {
  display: block; padding: var(--gv-space-4) var(--gv-gutter);
  min-height: var(--gv-hit);
  font-family: var(--gv-font-display); font-weight: var(--gv-weight-display);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  color: var(--gv-white); text-decoration: none;
  border-bottom: 1px solid var(--gv-hairline-dark);
}
@media (min-width: 62rem) { .gv-drawer { display: none !important; } }

.gv-footer { background: var(--gv-black); color: var(--gv-white); padding-block: var(--gv-space-8) var(--gv-space-6); }
.gv-footer__grid { display: grid; gap: var(--gv-space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); }
.gv-footer h4 { color: var(--gv-gray-mid); font-size: var(--gv-text-eyebrow);
  letter-spacing: var(--gv-tracking-eyebrow); margin-bottom: var(--gv-space-3); }
.gv-footer a { color: var(--gv-white); text-decoration: none; display: flex; align-items: center;
  min-height: var(--gv-hit); padding-block: var(--gv-space-2); font-size: var(--gv-text-small); }
.gv-footer a:hover { text-decoration: underline; text-decoration-color: var(--gv-red); }
.gv-footer__legal {
  margin-top: var(--gv-space-7); padding-top: var(--gv-space-5);
  border-top: 1px solid var(--gv-hairline-dark);
  font-size: var(--gv-text-fine); color: var(--gv-gray-mid);
  display: flex; flex-wrap: wrap; gap: var(--gv-space-4); justify-content: space-between;
}

/* ══ BANDS ═════════════════════════════════════════════════════════════ */

/* HERO — full-bleed photograph, black scrim, heavy caps bottom-left.
   The photograph does the emotional work. The chrome stays out of its way. */
.gv-hero {
  /* A band that paints its own dark ground MUST re-point the semantic ink, exactly as
     .gv-dark does. Measured 2026-09-03: without these four lines the hero <h1> inherited
     --gv-ink (black) over a black scrim and rendered at 1.00:1 — invisible. Any new band
     that sets its own background has to do the same. */
  --gv-ink: var(--gv-white);
  --gv-ink-muted: var(--gv-gray-body-dark);
  --gv-ink-inverse: var(--gv-black);
  --gv-rule: var(--gv-hairline-dark);
  --gv-edge: var(--gv-border-dark);
  position: relative; background: var(--gv-black); color: var(--gv-ink); isolation: isolate;
}
.gv-hero__media { position: absolute; inset: 0; z-index: -2; }
.gv-hero__media img { width: 100%; height: 100%; object-fit: cover; }
.gv-hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(33,39,33,.35) 0%, rgba(33,39,33,.55) 45%, rgba(33,39,33,.88) 100%);
}
.gv-hero__inner {
  max-width: var(--gv-max); margin-inline: auto; padding-inline: var(--gv-gutter);
  min-height: clamp(28rem, 60vh, 42rem);
  display: flex; flex-direction: column; justify-content: flex-end;
  padding-block: var(--gv-space-9) var(--gv-space-8);
}
.gv-hero__title {
  font-size: var(--gv-text-hero); font-weight: var(--gv-weight-black);
  line-height: var(--gv-leading-hero); letter-spacing: var(--gv-tracking-hero);
  margin: 0 0 var(--gv-space-5); max-width: 18ch; text-wrap: balance;
}
.gv-hero__lead { max-width: var(--gv-measure-lead); font-size: var(--gv-text-lead);
  color: var(--gv-ink-muted); margin-bottom: var(--gv-space-6); }
.gv-hero__actions { display: flex; flex-wrap: wrap; gap: var(--gv-space-3); }
.gv-hero--compact .gv-hero__inner { min-height: 22rem; }

/* SECTION BAND — the workhorse. A ground, a red rule, a heading, content. */
.gv-section__head { margin-bottom: var(--gv-space-7); max-width: var(--gv-measure); }

/* STAT BAND — three or four readouts on the black ground. Heritage numbers,
   spec numbers, coverage numbers. Every number must be sourceable. */
.gv-stats { display: grid; gap: var(--gv-space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); }

/* CLOSER — exactly one per page, the last thing before the footer. */
.gv-closer {
  --gv-ink: var(--gv-white); --gv-ink-muted: var(--gv-white);
  --gv-ink-inverse: var(--gv-red); --gv-rule: rgba(255,255,255,.35); --gv-edge: var(--gv-white);
  background: var(--gv-red); color: var(--gv-ink);
}
.gv-closer h2 { color: var(--gv-white); max-width: 20ch; }
.gv-closer .gv-btn { --_bg: var(--gv-white); --_ink: var(--gv-red); }
.gv-closer .gv-btn:hover { --_bg: var(--gv-black); --_ink: var(--gv-white); }
.gv-closer .gv-btn--secondary { --_bg: transparent; --_ink: var(--gv-white); --_edge: var(--gv-white); }
.gv-closer .gv-btn--secondary:hover { --_bg: var(--gv-white); --_ink: var(--gv-red); }

/* SPLIT — media one side, copy the other. Alternates by modifier, never by
   hand-written order. Three in a row on the same side reads as a template. */
.gv-split { display: grid; gap: var(--gv-space-7); align-items: center; }
.gv-split__media img { width: 100%; }
@media (min-width: 54rem) {
  .gv-split { grid-template-columns: 1fr 1fr; gap: var(--gv-space-8); }
  .gv-split--flip .gv-split__media { order: 2; }
}

/* ══ CONTENT ═══════════════════════════════════════════════════════════ */

/* MODEL CARD — the unit of a lineup grid. Photo on the inset ground, series
   name, one line of positioning, deck range, then a single ghost action. */
.gv-model {
  display: flex; flex-direction: column;
  background: var(--gv-ground-light); color: var(--gv-black);
  border: 1px solid var(--gv-hairline-light);
  text-decoration: none;
  transition: border-color var(--gv-dur-base) var(--gv-ease);
}
.gv-model:hover { border-color: var(--gv-red); }
.gv-model__media { background: var(--gv-ground-inset); aspect-ratio: 4 / 3;
  display: grid; place-items: center; padding: var(--gv-space-5); }
.gv-model__media img { max-height: 100%; width: auto; object-fit: contain; }
.gv-model__body { padding: var(--gv-space-5); display: flex; flex-direction: column;
  gap: var(--gv-space-2); flex: 1; }
.gv-model__tier {
  font-family: var(--gv-font-display); font-size: var(--gv-text-eyebrow);
  font-weight: var(--gv-weight-bold); text-transform: uppercase;
  letter-spacing: var(--gv-tracking-eyebrow); color: var(--gv-gray-body-light);
}
.gv-model__name {
  font-family: var(--gv-font-display); font-size: var(--gv-text-h3);
  font-weight: var(--gv-weight-display); text-transform: uppercase;
  letter-spacing: var(--gv-tracking-display); margin: 0;
}
.gv-model__note { font-size: var(--gv-text-small); color: var(--gv-gray-body-light); margin: 0; }
.gv-model__meta {
  margin-top: auto; padding-top: var(--gv-space-4);
  border-top: 1px solid var(--gv-hairline-light);
  font-family: var(--gv-font-display); font-size: var(--gv-text-fine);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  display: flex; justify-content: space-between; gap: var(--gv-space-3);
}
.gv-model__meta span:last-child { color: var(--gv-red); }

/* SPEC TABLE — figures are tabular, the header is a black slab, and on a phone
   the table scrolls inside itself rather than breaking the page. */
.gv-spec { width: 100%; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.gv-spec table { width: 100%; border-collapse: collapse; min-width: 32rem;
  font-variant-numeric: tabular-nums; }
.gv-spec th, .gv-spec td {
  text-align: left; padding: var(--gv-space-3) var(--gv-space-4);
  border-bottom: 1px solid var(--gv-hairline-light); font-size: var(--gv-text-small);
}
.gv-spec thead th {
  background: var(--gv-black); color: var(--gv-white);
  font-family: var(--gv-font-display); font-size: var(--gv-text-fine);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  border-bottom: 0;
}
.gv-spec tbody th { font-weight: var(--gv-weight-medium); color: var(--gv-gray-body-light);
  font-family: var(--gv-font-body); text-transform: none; letter-spacing: 0; width: 40%; }
.gv-spec tbody tr:nth-child(even) { background: var(--gv-ground-inset); }
/* A link that IS a table cell's content is a standalone target, not an inline link in prose,
   so it has to clear the 44px floor. Measured: 46 cell links were landing at 18px. */
.gv-spec td a, .gv-spec th a {
  display: inline-flex; align-items: center; min-height: var(--gv-hit);
}

/* FEATURE LIST — a red rule per item, no bullet glyphs. */
.gv-features { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gv-space-5); }
.gv-features li { padding-left: var(--gv-space-5); border-left: var(--gv-rule-mid) solid var(--gv-red); }
.gv-features h3 { font-size: var(--gv-text-body); letter-spacing: var(--gv-tracking-label);
  margin-bottom: var(--gv-space-2); }
.gv-features p { font-size: var(--gv-text-small); color: var(--gv-ink-muted); margin: 0; }

/* STORE CARD — a Koenig location that carries the line. */
.gv-store { border: 1px solid var(--gv-hairline-light); padding: var(--gv-space-5);
  display: flex; flex-direction: column; gap: var(--gv-space-2); }
.gv-store h3 { margin: 0; font-size: var(--gv-text-h3); }
/* A link that IS a card heading is a standalone target, not an inline link in prose —
   measured at 26px on the store pages before this. */
.gv-store h3 a { display: inline-flex; align-items: center; min-height: var(--gv-hit); }
.gv-store address { font-style: normal; font-size: var(--gv-text-small); color: var(--gv-ink-muted); }

/* FAQ — <details>, visible copy, no JS. The answer is on the page for a person
   AND for an answer engine; it is not a rich-snippet play. */
.gv-faq { border-top: var(--gv-rule-mid) solid var(--gv-ink); }
.gv-faq details { border-bottom: 1px solid var(--gv-rule); }
.gv-faq summary {
  cursor: pointer; list-style: none; padding: var(--gv-space-4) 0;
  min-height: var(--gv-hit); display: flex; align-items: center; justify-content: space-between;
  gap: var(--gv-space-4);
  font-family: var(--gv-font-display); font-weight: var(--gv-weight-display);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  font-size: var(--gv-text-small);
}
.gv-faq summary::-webkit-details-marker { display: none; }
.gv-faq summary::after { content: "+"; color: var(--gv-red); font-size: 1.5em; line-height: 1; }
.gv-faq details[open] summary::after { content: "\2013"; }
.gv-faq details > *:not(summary) { padding-bottom: var(--gv-space-5); max-width: var(--gv-measure); }

/* ══ FORMS ═════════════════════════════════════════════════════════════ */
.gv-field { display: flex; flex-direction: column; gap: var(--gv-space-2); margin-bottom: var(--gv-space-5); }
.gv-field label {
  font-family: var(--gv-font-display); font-size: var(--gv-text-fine);
  font-weight: var(--gv-weight-bold); text-transform: uppercase;
  letter-spacing: var(--gv-tracking-label); color: var(--gv-ink);
}
.gv-field input, .gv-field select, .gv-field textarea {
  min-height: var(--gv-hit);
  padding: var(--gv-space-3) var(--gv-space-4);
  background: var(--gv-ground-light); color: var(--gv-black);
  border: 2px solid var(--gv-edge); border-radius: var(--gv-radius);
  font-family: var(--gv-font-body); font-size: var(--gv-text-body);
  width: 100%;
}
.gv-field input:focus, .gv-field select:focus, .gv-field textarea:focus { border-color: var(--gv-red); }
.gv-field__hint { font-size: var(--gv-text-fine); color: var(--gv-ink-muted); }
.gv-field--error input { border-color: var(--gv-red); }
.gv-field__error { font-size: var(--gv-text-fine); color: var(--gv-red); font-weight: 600; }


/* ══ DECK RULER ════════════════════════════════════════════════════════════
   Introduced by the Claude Design homepage, added here rather than left as page CSS —
   when a design needs something the library cannot express, the library grows.

   Every series on one 30–72" scale, so deck width (the decision that actually matters)
   is comparable at a glance instead of buried in a table.

   ⚠️ THE NUMBERS ARE THE DRAWING. Bar start and end are computed from the real deck range,
   so a wrong deck width is not a typo — it is a bar of the wrong length in the wrong place.
   Always render this from gravely_models, never from hand-typed ranges. The synced design
   had 6 of 7 bars drawn from wrong numbers for exactly this reason.
   ═════════════════════════════════════════════════════════════════════════ */
.gv-ruler-scale, .gv-ruler {
  display: grid; grid-template-columns: minmax(8rem, 12rem) 1fr;
  gap: var(--gv-space-4); align-items: center;
}
.gv-ruler-scale {
  align-items: end; border-bottom: var(--gv-rule-mid) solid var(--gv-ink);
  padding-bottom: var(--gv-space-2);
}
.gv-ruler-scale__ticks { display: grid; }
.gv-ruler-scale__ticks span {
  text-align: right; font-family: var(--gv-font-display); font-weight: var(--gv-weight-display);
  font-size: var(--gv-text-small); letter-spacing: var(--gv-tracking-label);
  font-variant-numeric: tabular-nums;
}
.gv-ruler {
  min-height: 56px; text-decoration: none;
  border-bottom: 1px solid var(--gv-rule);
}
.gv-ruler__label { display: grid; gap: 2px; }
.gv-ruler__name {
  font-family: var(--gv-font-display); font-weight: var(--gv-weight-display);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  font-size: var(--gv-text-small); color: var(--gv-ink);
  transition: color var(--gv-dur-fast) var(--gv-ease);
}
.gv-ruler__range {
  font-family: var(--gv-font-display); font-size: var(--gv-text-fine);
  letter-spacing: var(--gv-tracking-label); color: var(--gv-ink-muted);
  font-variant-numeric: tabular-nums;
}
.gv-ruler__track {
  display: grid; align-items: center; min-height: 56px;
  background-image: repeating-linear-gradient(90deg, transparent 0 calc(100% / 7 - 1px),
    var(--gv-rule) calc(100% / 7 - 1px) calc(100% / 7));
}
.gv-ruler__bar {
  height: 16px; background: var(--gv-ink);
  transition: background var(--gv-dur-fast) var(--gv-ease);
}
.gv-ruler:hover .gv-ruler__bar { background: var(--gv-red); }
.gv-ruler:hover .gv-ruler__name { color: var(--gv-red); }
/* On a phone the label column would squeeze the track to nothing. */
@media (max-width: 40rem) {
  .gv-ruler-scale, .gv-ruler { grid-template-columns: 1fr; gap: var(--gv-space-2); }
  .gv-ruler { padding-block: var(--gv-space-3); min-height: 0; }
  .gv-ruler__track { min-height: 28px; }
}

/* A "text this store" action — the design pairs every phone number with an SMS link. */
.gv-sms {
  min-height: var(--gv-hit); display: inline-flex; align-items: center;
  font-family: var(--gv-font-display); font-size: var(--gv-text-fine);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  text-decoration: underline; text-decoration-color: var(--gv-red);
  text-decoration-thickness: 2px; text-underline-offset: .25em; color: var(--gv-ink);
}


/* ══ MODEL ROW ═════════════════════════════════════════════════════════════
   From the Claude Design series template: a big deck number, the model beside it, and one
   action — instead of a spec table, which is what most dealer sites reach for and what makes
   a lineup feel like a spreadsheet.
   The row has three cells; below 40rem the middle one is left with ~70px and the engine line
   wraps to five lines, so the action drops to its own row on a phone. */
.gv-modelrow {
  display: grid; grid-template-columns: 4.5rem minmax(0, 1fr);
  gap: var(--gv-space-4); align-items: center;
  padding: var(--gv-space-5) var(--gv-space-3);
  border-bottom: 1px solid var(--gv-rule); text-decoration: none;
  transition: background var(--gv-dur-fast) var(--gv-ease);
}
.gv-modelrow:hover { background: var(--gv-ground-inset); }
.gv-modelrow__deck {
  font-family: var(--gv-font-display); font-weight: var(--gv-weight-black);
  font-size: 2rem; line-height: .9; letter-spacing: -.01em;
  font-variant-numeric: tabular-nums; color: var(--gv-ink);
}
.gv-modelrow__name {
  font-family: var(--gv-font-display); font-weight: var(--gv-weight-display);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  font-size: var(--gv-text-small); color: var(--gv-ink);
}
.gv-modelrow__cta {
  grid-column: 1 / -1; display: flex; align-items: baseline; gap: var(--gv-space-4);
  border-top: 1px solid var(--gv-rule); padding-top: var(--gv-space-3);
}
.gv-modelrow__cta span {
  font-family: var(--gv-font-display); font-size: var(--gv-text-fine);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label); color: var(--gv-ink);
  text-decoration: underline; text-decoration-color: var(--gv-red);
  text-decoration-thickness: 2px; text-underline-offset: .25em;
}
@media (min-width: 40rem) {
  .gv-modelrow { grid-template-columns: 7.5rem minmax(0, 1fr) auto; gap: var(--gv-space-5); }
  .gv-modelrow__deck { font-size: 2.5rem; }
  .gv-modelrow__cta { grid-column: auto; display: grid; justify-items: end;
                      text-align: right; border-top: 0; padding-top: 0; }
}


/* ══ FINDER ════════════════════════════════════════════════════════════════
   From the Claude Design Finder: a progress bar, and option buttons that carry a plain-English
   note under the label — the note is what makes the choice answerable without a spec sheet. */
.gv-progress { height: 6px; background: var(--gv-ground-inset); margin-bottom: var(--gv-space-7); }
.gv-progress__bar { height: 6px; background: var(--gv-red); transition: width var(--gv-dur-slow) var(--gv-ease); }
.gv-opts {
  display: grid; gap: var(--gv-space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}
.gv-opt {
  text-align: left; cursor: pointer; font-family: var(--gv-font-body);
  background: var(--gv-ground-inset); border: 2px solid var(--gv-hairline-light);
  border-radius: var(--gv-radius); padding: var(--gv-space-5);
  min-height: 88px; display: grid; gap: var(--gv-space-2); align-content: start;
  transition: background var(--gv-dur-fast) var(--gv-ease), border-color var(--gv-dur-fast) var(--gv-ease);
}
.gv-opt:hover { border-color: var(--gv-red); background: var(--gv-white); }
.gv-opt__label {
  font-family: var(--gv-font-display); font-weight: var(--gv-weight-display);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  font-size: var(--gv-text-small); color: var(--gv-black);
}
.gv-opt__note { font-size: var(--gv-text-small); color: var(--gv-gray-body-light); line-height: 1.4; }


/* ══ STORE PAGE ════════════════════════════════════════════════════════════
   From the Claude Design store template. */
.gv-storefront { height: clamp(20rem, 42vh, 30rem); background: var(--gv-ground-inset); }
.gv-storefront img { width: 100%; height: 100%; object-fit: cover; }

.gv-dl,
.gv-hours { margin: 0; border-top: 1px solid var(--gv-rule); }
.gv-hours { max-width: 32rem; }
.gv-dl__row,
.gv-hours__row {
  display: flex; justify-content: space-between; gap: var(--gv-space-4);
  padding: var(--gv-space-3) 0; border-bottom: 1px solid var(--gv-rule);
  min-height: var(--gv-hit); align-items: center;
}
.gv-dl dt,
.gv-hours dt {
  font-family: var(--gv-font-display); font-size: var(--gv-text-small);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
}
.gv-dl dd,
.gv-hours dd {
  margin: 0; font-family: var(--gv-font-display); font-size: var(--gv-text-small);
  letter-spacing: var(--gv-tracking-label); font-variant-numeric: tabular-nums;
}

/* A bordered card on an inset band — the store page's three service blocks. */
.gv-panel {
  background: var(--gv-ground-light); border: 1px solid var(--gv-hairline-light);
  padding: var(--gv-space-6); display: flex; flex-direction: column; gap: var(--gv-space-3);
}
.gv-panel h3 { font-size: var(--gv-text-h2); margin: 0; }
.gv-panel p { font-size: var(--gv-text-small); color: var(--gv-ink-muted); margin: 0; }


/* ══ SOLUTIONS ═════════════════════════════════════════════════════════════
   From the Claude Design solutions template. Two devices:

   .gv-duty  — the residential/commercial pair. The dark half re-points its ink
               tokens via .gv-dark; a band that paints its own ground and does not
               is how two earlier Koenig systems shipped black-on-black headings.
   .gv-lane  — a whole card that is a link, ending in a red-underlined chip that
               names the series behind it. Built on .gv-panel so the lane grid and
               the store page's service blocks cannot drift apart. */
.gv-duty {
  display: flex; flex-direction: column; gap: var(--gv-space-4);
  padding: var(--gv-space-6); border: 1px solid var(--gv-border-light);
}
.gv-duty.gv-dark { border-color: transparent; }
.gv-duty h3 { font-size: var(--gv-text-h2); margin: 0; }
.gv-duty > .gv-slab { align-self: flex-start; }
.gv-duty > .gv-btn { margin-top: auto; }

.gv-lane { text-decoration: none; transition: border-color var(--gv-dur-fast) var(--gv-ease); }
.gv-lane:hover, .gv-lane:focus-visible { border-color: var(--gv-red); }
.gv-lane .gv-eyebrow { margin: 0; }
.gv-lane__series {
  margin-top: auto; padding-top: var(--gv-space-4);
  border-top: 1px solid var(--gv-hairline-light);
  font-family: var(--gv-font-display); font-size: var(--gv-text-fine);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  text-decoration: underline; text-decoration-color: var(--gv-red);
  text-decoration-thickness: 2px; text-underline-offset: .25em;
}
/* .gv-electric re-points the ink tokens AND sets the dark electric ground — but it lives in
   tokens/base.css, which loads before this file, so .gv-panel's light background won it and the
   lane rendered white ink on a light ground (measured 1.00:1 on the h3). Restate the ground here
   rather than reorder the cascade; the divider and chip re-point with it. */
.gv-lane.gv-electric {
  background: var(--gv-electric-dark); border: 1px solid var(--gv-edge);
}
.gv-lane.gv-electric .gv-lane__series {
  border-top-color: var(--gv-rule); color: var(--gv-electric-blue); text-decoration: none;
}

/* ══ CHOICE CHIP ═══════════════════════════════════════════════════════════
   From the Claude Design quote template: a large tappable button that reads as a
   selected/unselected state rather than as a radio. The selected state is carried by
   [aria-pressed="true"] so a screen reader hears it too — the design expressed it with
   inline colours only, which is invisible to anything that is not looking. */
.gv-choice {
  cursor: pointer; text-align: left; border-radius: 0;
  border: 2px solid var(--gv-border-light); background: var(--gv-ground-light);
  color: var(--gv-ink); padding: var(--gv-space-4); min-height: var(--gv-hit);
  display: grid; gap: 4px; font-family: var(--gv-font-body);
  transition: border-color var(--gv-dur-fast) var(--gv-ease),
              background var(--gv-dur-fast) var(--gv-ease);
}
.gv-choice:hover { border-color: var(--gv-red); }
.gv-choice[aria-pressed="true"] {
  border-color: var(--gv-red); background: var(--gv-red); color: var(--gv-white);
}
.gv-choice__label {
  font-family: var(--gv-font-display); font-weight: var(--gv-weight-display);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  font-size: var(--gv-text-small);
}
.gv-choice__note { font-size: var(--gv-text-fine); opacity: .8; }
.gv-choices {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  gap: var(--gv-space-3);
}

/* The attachments page's fitment matrix: a yes is meant to be scannable down a column. */
.gv-spec td[data-fit="yes"] { font-family: var(--gv-font-display); font-weight: var(--gv-weight-display); }
.gv-spec td[data-fit="no"] { color: var(--gv-gray-body-light); }
.gv-att:hover { border-color: var(--gv-red); }

/* A series link that IS a card's heading is a tap target, not an inline body link, so the
   44px minimum applies to it — the WCAG 2.5.8 inline exception does not cover this one.
   Measured at 38px on the comparison page's head-to-head cards. */
.gv-h2h h3 a { display: inline-flex; align-items: center; min-height: var(--gv-hit); }

/* Form result message. Only ever rendered from a real response — see render_quote(). */
.gv-formmsg {
  margin-top: var(--gv-space-5); padding: var(--gv-space-4) var(--gv-space-5);
  border-left: var(--gv-rule-heavy) solid var(--gv-red);
  background: var(--gv-ground-inset); font-size: var(--gv-text-small);
}
.gv-formmsg--ok { border-left-color: var(--gv-black); }

/* ══ PRICE ═════════════════════════════════════════════════════════════════
   A price on this site may ONLY be rendered from the pricing dataset — never typed. The element
   carries data-source and verify_brand_policy keys on it: a $ figure outside .gv-price still
   fails the build, which is what catches a hand-written price like the design's
   "Starting at $17,299". */
.gv-price {
  display: inline-block; margin-top: var(--gv-space-4);
  font-family: var(--gv-font-display); font-size: var(--gv-text-small);
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
  color: var(--gv-ink-muted);
}
.gv-price strong { color: var(--gv-ink); font-size: var(--gv-text-body); }


/* ══ REQUEST SERVICE — chip, alert, sheet, store action tiles ══════════════
   From Claude Design "Request service.dc.html" + "Service request sheet.dc.html" (project
   bbf0aae2, synced 2026-10-20). The design noted both the chip and the message line were missing
   from the system and should land here BEFORE the build; they have.
   · .gv-chip — a single-choice chip (role=radio). Square, 44px, black fill when checked.
   · .gv-alert — the form message line as a RED SURFACE with white type (4.8:1). .gv-field__error
     sets 13px red TEXT, which breaks this system's own red-is-a-surface rule; .gv-alert does not.
   · .gv-sheet — bottom sheet on a phone (92% of the viewport, grabber), a 560px centered dialog
     from 768px up. Header and footer are fixed; only the middle scrolls, so Send never leaves
     the screen. Above the keyboard, --gv-kb (set from visualViewport) lifts the whole sheet.
   · .gv-tiles — the phone-only 2x2 action grid on a store page. Square tiles (radius 0), Service
     is the one red tile. */
.gv-chip {
  min-height: 44px; padding: var(--gv-space-2) var(--gv-space-3);
  border: 1px solid var(--gv-border-light); border-radius: 0;
  background: var(--gv-ground-light); color: var(--gv-ink);
  font-family: var(--gv-font-body); font-size: var(--gv-text-small); text-align: left; cursor: pointer;
}
.gv-chip:hover { border-color: var(--gv-black); }
.gv-chip[aria-checked="true"] {
  border: 2px solid var(--gv-black); background: var(--gv-black); color: var(--gv-white); font-weight: 600;
}
.gv-chips { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gv-space-2); }
.gv-chips--error .gv-chip:not([aria-checked="true"]) { border-color: var(--gv-red); }
.gv-chip:disabled { cursor: default; opacity: .6; }

.gv-alert {
  background: var(--gv-red); color: var(--gv-white);
  padding: var(--gv-space-3) var(--gv-space-4); display: grid; gap: var(--gv-space-1);
}
.gv-alert strong { font-family: var(--gv-font-body); font-weight: 700; font-size: var(--gv-text-body); }
.gv-alert span { font-size: var(--gv-text-small); }
.gv-alert a {
  min-height: 44px; display: flex; align-items: center; color: var(--gv-white);
  font-family: var(--gv-font-display); font-weight: 800; font-size: var(--gv-text-body);
  letter-spacing: var(--gv-tracking-label); text-decoration: underline;
  text-decoration-thickness: 2px; text-underline-offset: .2em;
}

.gv-sheet-scrim {
  position: fixed; inset: 0; z-index: 2147483647; background: rgba(33, 39, 33, .62);
  display: flex; align-items: flex-end; justify-content: center;
}
.gv-sheet-scrim[hidden] { display: none; }
.gv-sheet {
  position: relative; width: 100%; height: calc(92dvh - var(--gv-kb, 0px));
  margin-bottom: var(--gv-kb, 0px); box-shadow: var(--gv-shadow-overlay);
  display: flex; flex-direction: column; min-height: 0;
  background: var(--gv-ground-light); color: var(--gv-ink); font-family: var(--gv-font-body);
}
.gv-sheet__head { flex: none; padding: var(--gv-space-3) var(--gv-space-5) var(--gv-space-4); border-bottom: 1px solid var(--gv-hairline-light); }
.gv-sheet__grab { width: 40px; height: 4px; background: var(--gv-hairline-light); margin: 0 auto var(--gv-space-3); }
.gv-sheet__title { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--gv-space-4); }
.gv-sheet__title > div { display: grid; gap: var(--gv-space-2); min-width: 0; }
.gv-sheet__title h2 { margin: 0; font-size: var(--gv-text-h3); line-height: 1.05; text-wrap: balance; }
.gv-sheet__close {
  flex: none; min-width: 44px; min-height: 44px; margin: calc(var(--gv-space-2) * -1) calc(var(--gv-space-3) * -1) 0 0;
  border: 0; background: transparent; color: var(--gv-ink); cursor: pointer;
  font-family: var(--gv-font-display); font-size: var(--gv-text-fine); font-weight: 800;
  text-transform: uppercase; letter-spacing: var(--gv-tracking-label);
}
.gv-sheet form, .gv-sheet__done { flex: 1; min-height: 0; display: flex; flex-direction: column; margin: 0; }
.gv-sheet form[hidden], .gv-sheet__done[hidden] { display: none; }
.gv-sheet__body { flex: 1; min-height: 0; overflow: auto; padding: var(--gv-space-5) var(--gv-space-5) var(--gv-space-2); }
.gv-sheet__body fieldset { border: 0; padding: 0; margin: 0 0 var(--gv-space-5); min-width: 0; }
.gv-sheet__body fieldset:last-of-type { margin-bottom: 0; }
.gv-sheet__body legend {
  padding: 0; margin-bottom: var(--gv-space-2); font-family: var(--gv-font-display);
  font-size: var(--gv-text-fine); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--gv-tracking-label); color: var(--gv-ink);
}
.gv-sheet__opt { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--gv-ink-muted); font-family: var(--gv-font-body); }
.gv-sheet__pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gv-space-3); }
.gv-sheet__foot {
  flex: none; padding: var(--gv-space-4) var(--gv-space-5); border-top: 1px solid var(--gv-hairline-light);
  background: var(--gv-ground-light); display: grid; gap: var(--gv-space-3);
}
.gv-sheet__foot .gv-link, .gv-sheet__foot p {
  justify-self: center; min-height: 44px; margin: 0; display: flex; align-items: center; font-size: var(--gv-text-small);
}
.gv-sheet__foot p { color: var(--gv-ink-muted); }
.gv-sheet__sent { flex: 1; min-height: 0; overflow: auto; padding: var(--gv-space-6) var(--gv-space-5); }
.gv-sheet__thanks {
  margin: 0 0 var(--gv-space-3); font-family: var(--gv-font-display); font-weight: 900; text-transform: uppercase;
  font-size: var(--gv-text-h2); line-height: 1; letter-spacing: var(--gv-tracking-display);
}
.gv-sheet button[aria-busy="true"] { opacity: .6; cursor: progress; }
body.gv-sheet-open { overflow: hidden; }
/* The sheet toggles parts with the hidden attribute; component display rules (.gv-alert grid,
   .gv-link flex) would otherwise override it and show a hidden element. */
.gv-sheet [hidden] { display: none !important; }
@media (min-width: 768px) {
  .gv-sheet-scrim { align-items: center; padding: var(--gv-space-6); }
  .gv-sheet { width: 560px; height: auto; max-height: min(740px, calc(100dvh - 2 * var(--gv-space-6))); margin-bottom: 0; }
  .gv-sheet__grab { display: none; }
}

.gv-tiles {
  background: var(--gv-ground-inset); padding: var(--gv-space-5);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gv-space-3);
}
.gv-tiles > * {
  min-height: 112px; background: var(--gv-ground-light); border: 1px solid var(--gv-hairline-light); border-radius: 0;
  display: grid; align-content: center; justify-items: center; gap: 6px; padding: var(--gv-space-3);
  text-decoration: none; color: var(--gv-ink); font-family: var(--gv-font-body); cursor: pointer;
}
.gv-tiles b {
  font-family: var(--gv-font-display); font-weight: 900; text-transform: uppercase;
  font-size: 20px; letter-spacing: var(--gv-tracking-label);
}
.gv-tiles small { font-size: 14px; color: var(--gv-ink-muted); }
.gv-tiles .gv-tile--service { background: var(--gv-red); color: var(--gv-white); border: 0; }
.gv-tiles .gv-tile--service small { color: var(--gv-white); }
@media (min-width: 768px) { .gv-tiles { display: none; } }
@media (max-width: 767px) { .gv-mast-actions { display: none !important; } }
@media (min-width: 768px) { .gv-tiles-hours { display: none !important; } }

