/* Blackline design tokens. Single source of truth. Reasoning: docs/art-direction.md */

:root {
  /* neutral ramp */
  --_ink-990: #060708;
  --_ink-950: #0A0B0C;
  --_ink-900: #101113;
  --_ink-850: #16181A;
  --_ink-800: #1D1F22;
  --_ink-700: #26292D;
  --_ink-600: #33373C;
  --_ink-500: #4A4F55;
  /* _ink-400 was #6E747A: 3.49:1 on --surface-raised-hover, 3.77 on
     --surface-raised, 4.00 on --surface-alt, 4.17 on --surface-page — below
     AA on every ground it prints on. Re-cut 2026-09-06 to #82888E: 4.61 /
     4.97 / 5.27 / 5.50 / 5.63 (raised-hover / raised / alt / page / sunken),
     and 1.36:1 against --text-muted so the third ink step survives. */
  --_ink-400: #82888E;
  --_ink-300: #9AA0A6;
  --_ink-200: #C3C7CB;
  --_ink-100: #E8EAEC;

  /* bone ramp */
  --_bone-050: #FAF9F6;
  --_bone-100: #F2F0EC;
  --_bone-200: #E4E1D9;
  --_bone-300: #CFCBC1;
  --_bone-ink: #0E0F10;
  --_bone-ink-soft: #55585B;

  /* accent ramp — in-limit signal green */
  --_signal-400: #C2E85C;
  --_signal-500: #AEDD2F;
  --_signal-600: #86AB1D;
  --_signal-700: #4F6606;

  /* alpha */
  --_alpha-ink-45: rgba(10, 11, 12, 0.45);
  --_alpha-ink-30: rgba(10, 11, 12, 0.30);
  --_alpha-ink-22: rgba(10, 11, 12, 0.22);
  --_alpha-ink-12: rgba(10, 11, 12, 0.12);
  --_alpha-ink-08: rgba(10, 11, 12, 0.08);
  --_alpha-sunken-72: rgba(6, 7, 8, 0.72);
  --_alpha-sunken-86: rgba(6, 7, 8, 0.86);
  --_alpha-light-04: rgba(255, 255, 255, 0.04);
  --_alpha-light-06: rgba(255, 255, 255, 0.06);
  --_alpha-light-10: rgba(255, 255, 255, 0.10);
  --_alpha-light-14: rgba(255, 255, 255, 0.14);
  --_alpha-light-20: rgba(255, 255, 255, 0.20);
  --_alpha-light-28: rgba(255, 255, 255, 0.28);
  --_shadow-ink-40: rgba(0, 0, 0, 0.40);
  --_shadow-ink-55: rgba(0, 0, 0, 0.55);
  --_shadow-ink-70: rgba(0, 0, 0, 0.70);
  --_shadow-ink-80: rgba(0, 0, 0, 0.80);

  /* surfaces */
  --surface-page: var(--_ink-950);
  --surface-alt: var(--_ink-900);
  --surface-sunken: var(--_ink-990);
  --surface-raised: var(--_ink-850);
  --surface-raised-hover: var(--_ink-800);
  --surface-inset: var(--_ink-900);
  --surface-inset-hover: var(--_ink-850);
  --surface-accent: var(--_signal-500);
  --surface-accent-hover: var(--_signal-400);
  --surface-accent-wash: rgba(174, 221, 47, 0.10);
  --surface-accent-wash-strong: rgba(174, 221, 47, 0.18);
  --surface-invert: var(--_bone-100);
  --surface-invert-raised: var(--_bone-050);
  --surface-invert-ink: var(--_ink-850);
  --surface-media: var(--_ink-800);
  --surface-header: rgba(10, 11, 12, 0.88);

  /* text */
  --text-primary: var(--_ink-100);
  --text-secondary: var(--_ink-200);
  --text-muted: var(--_ink-300);
  --text-faint: var(--_ink-400);
  --text-ghost: rgba(255, 255, 255, 0.045);
  --text-accent: var(--_signal-500);
  --text-accent-strong: var(--_signal-400);
  --text-accent-quiet: var(--_signal-600);
  --text-on-accent: var(--_ink-950);
  --text-on-accent-muted: rgba(10, 11, 12, 0.66);
  --text-on-invert: var(--_bone-ink);
  --text-on-invert-muted: var(--_bone-ink-soft);
  --text-on-invert-accent: var(--_signal-700);
  --text-on-media: var(--_ink-100);

  /* borders */
  --border-hairline: var(--_alpha-light-06);
  --border-soft: var(--_alpha-light-10);
  --border-strong: var(--_ink-700);
  --border-accent: var(--_signal-500);
  --border-accent-soft: rgba(174, 221, 47, 0.35);
  --border-on-invert: var(--_alpha-ink-12);
  --border-on-invert-strong: var(--_bone-300);
  --border-on-accent: var(--_alpha-ink-22);
  --border-width-hair: 1px;
  --border-width-medium: 2px;
  --border-width-bar: 3px;
  --border-width-slab: 4px;

  /* focus */
  --focus-ring-color: var(--_signal-500);
  --focus-ring-width: 2px;
  --focus-ring-offset: 3px;
  --focus-ring-color-invert: var(--_signal-700);
  --focus-ring-inset: inset 0 0 0 var(--focus-ring-width) var(--focus-ring-color);

  /* overlays / scrims / sheen / grain */
  --overlay-media: rgba(10, 11, 12, 0.28);
  --overlay-media-hover: rgba(10, 11, 12, 0.12);
  --overlay-media-heavy: rgba(6, 7, 8, 0.55);
  --scrim-hero: linear-gradient(180deg, rgba(6, 7, 8, 0.72) 0%, rgba(6, 7, 8, 0.35) 38%, rgba(6, 7, 8, 0.78) 100%);
  --scrim-tile: linear-gradient(180deg, rgba(6, 7, 8, 0) 42%, rgba(6, 7, 8, 0.86) 100%);
  /* rail-end scrim runs the full --rail-peek box. At 60% the fade was 14px at
     390 and a bisected marque read at 3.7:1 (legible type, not a ghost). */
  --scrim-edge: linear-gradient(90deg, rgba(6, 7, 8, 0.9) 0%, rgba(6, 7, 8, 0) 100%);
  --scrim-bleed: linear-gradient(180deg, rgba(6, 7, 8, 0) 52%, rgba(6, 7, 8, 0.82) 100%);
  --sheen-color: var(--_alpha-light-14);
  --sheen-color-specular: var(--_alpha-light-28);
  --sheen-skew: -18deg;
  --sheen-width-compact: 45%;
  --sheen-width-broad: 20%;
  --sheen-width-specular: 12%;
  --grain-opacity: 0.055;
  --grain-opacity-invert: 0.09;
  --grain-size: 180px;
  --grain-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");

  /* families */
  --font-display: "Chivo", "Chivo Fallback", system-ui, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-body: var(--font-display);
  --font-mono: "Martian Mono", "Martian Mono Fallback", ui-monospace, "Cascadia Mono", Menlo, monospace;

  /* type scale — nothing exceeds --text-display at any viewport */
  --text-display: clamp(2.75rem, 1.55rem + 5vw, 6.25rem);
  --text-h2: clamp(1.875rem, 1.15rem + 3.1vw, 3.5rem);
  --text-h3: clamp(1.25rem, 1.08rem + 0.7vw, 1.625rem);
  --text-h4: clamp(1.0625rem, 1rem + 0.28vw, 1.25rem);
  --text-stat: clamp(2.25rem, 1.5rem + 3.1vw, 4rem);
  --text-index: clamp(2rem, 1.2rem + 3.4vw, 4rem);
  --text-ledger: clamp(1.75rem, 1.1rem + 2.6vw, 3rem);
  --text-price: clamp(1.25rem, 1.05rem + 0.85vw, 1.75rem);
  --text-step: clamp(1rem, 0.92rem + 0.35vw, 1.25rem);
  --text-lead: clamp(1.0625rem, 1rem + 0.28vw, 1.25rem);
  --text-spec: clamp(0.8125rem, 0.78rem + 0.16vw, 0.9375rem);
  --text-body: 1rem;
  --text-body-sm: 0.9375rem;
  --text-caption: 0.8125rem;
  --text-label: 0.75rem;
  --text-micro: 0.6875rem;

  /* weight */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --weight-display: 800;

  /* line-height */
  --leading-flat: 1;
  --leading-tight: 1.1;
  --leading-snug: 1.16;
  --leading-heading: 1.28;
  --leading-micro: 1.45;
  --leading-body: 1.6;
  --leading-relaxed: 1.75;

  /* tracking */
  --tracking-display: -0.025em;
  --tracking-tight: -0.012em;
  --tracking-normal: 0em;
  --tracking-mono-tight: -0.03em;
  --tracking-mono-label: 0.06em;
  --tracking-label: 0.16em;

  /* space */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs: 0.75rem;
  --space-sm: 1rem;
  --space-md: 1.5rem;
  --space-lg: 2rem;
  --space-xl: 3rem;
  --space-2xl: 4rem;
  --space-3xl: 6rem;
  --space-4xl: 8rem;
  --space-5xl: 10rem;
  --space-section: clamp(4rem, 2.4rem + 6.6vw, 8.5rem);
  --space-section-tight: clamp(2.5rem, 1.7rem + 3.4vw, 4.5rem);
  --space-band: clamp(2rem, 1.4rem + 2.4vw, 3.25rem);
  --space-stack: clamp(2rem, 1.5rem + 2vw, 3.5rem);
  --gutter: clamp(1.25rem, 0.8rem + 2vw, 2.5rem);

  /* grid & widths */
  --grid-columns: 12;
  --grid-gap: clamp(1rem, 0.6rem + 1.6vw, 2rem);
  --grid-gap-tight: clamp(0.75rem, 0.5rem + 1vw, 1.25rem);
  --grid-row-gap: clamp(1.5rem, 1rem + 2vw, 2.75rem);
  --width-content: 1320px;
  --width-wide: 1560px;
  --width-page: 1920px;
  --width-prose: 34rem;
  --width-measure: 62ch;
  --card-min: 300px;
  --card-min-wide: 380px;

  /* radius — five jobs, see art-direction */
  --radius-sharp: 0px;
  --radius-inset: 6px;
  --radius-media: 10px;
  --radius-card: 14px;
  --radius-slab: 22px;
  --radius-pill: 999px;
  --radius-round: 50%;

  /* chamfer — top-right on every block. Override --chamfer; clip and edge follow. */
  --chamfer-sm: 12px;
  --chamfer-md: 20px;
  --chamfer-lg: 32px;
  --chamfer: var(--chamfer-md);
  --chamfer-clip: polygon(0 0, calc(100% - var(--chamfer)) 0, 100% var(--chamfer), 100% 100%, 0 100%);
  --chamfer-edge-ink: var(--border-soft);
  --chamfer-edge-ink-accent: var(--border-accent);
  --chamfer-edge-ink-invert: var(--border-on-invert-strong);
  --chamfer-edge: linear-gradient(45deg,transparent calc(50% - 1.25px),var(--chamfer-edge-ink) calc(50% - 1.25px),var(--chamfer-edge-ink) calc(50% - .25px),transparent calc(50% - .25px)) top right/var(--chamfer) var(--chamfer) no-repeat;

  /* shadow — box-shadow for square boxes, drop-shadow for chamfered ones */
  --shadow-card: 0 1px 2px var(--_shadow-ink-40), 0 8px 24px -8px var(--_shadow-ink-55);
  --shadow-card-hover: 0 2px 4px var(--_shadow-ink-40), 0 22px 46px -14px var(--_shadow-ink-70);
  --shadow-media: 0 24px 60px -24px var(--_shadow-ink-80), 0 2px 6px var(--_shadow-ink-40);
  --shadow-header: 0 1px 0 var(--_alpha-light-06), 0 14px 32px -20px var(--_shadow-ink-80);
  --shadow-accent: 0 8px 26px -14px rgba(174, 221, 47, 0.32);
  --shadow-field: inset 0 1px 0 var(--_alpha-light-04);
  --shadow-invert-card: 0 1px 2px var(--_alpha-ink-08), 0 14px 30px -16px var(--_alpha-ink-22);
  --shadow-chamfer: drop-shadow(0 1px 1px var(--_shadow-ink-40)) drop-shadow(0 14px 22px var(--_shadow-ink-55));
  --shadow-chamfer-invert: drop-shadow(0 1px 1px var(--_alpha-ink-08)) drop-shadow(0 16px 24px var(--_alpha-ink-22));
  --shadow-none: none;

  /* tick language — the inputs. The composed rules (--tick-rule, -fine,
     -vertical) are NOT here; they sit in the per-element block at the end of
     this file so a section root can override any input below and every rule
     inside it follows. Never `filter: invert()` a tick rule. */
  --tick-pitch: 8px;
  --tick-len: 4px;
  --tick-len-major: 9px;
  --tick-pitch-fine: 6px;
  --tick-len-fine: 3px;
  --tick-ink: var(--_alpha-light-10);
  --tick-ink-major: var(--_alpha-light-20);
  --tick-ink-invert: var(--_alpha-ink-12);
  --tick-ink-invert-major: var(--_alpha-ink-30);
  --tick-ink-accent: var(--_alpha-ink-30);
  --tick-ink-accent-major: var(--_alpha-ink-45);
  --tick-band-pitch: clamp(10px, 0.85vw, 16px);
  --tick-band-len: 7px;
  --tick-band-len-major: 16px;
  --marker-dot: 10px;
  --marker-dot-lg: 18px;

  /* specimen treatment — no photograph ships plain */
  --specimen-inset: 8px;
  --specimen-inset-lg: 12px;
  --specimen-frame: var(--_alpha-light-20);
  --specimen-frame-invert: var(--_alpha-ink-22);
  --specimen-frame-width: var(--border-width-hair);
  --chip-bg: var(--_alpha-sunken-86);
  --chip-border: var(--_alpha-light-14);
  --chip-text: var(--text-secondary);
  --chip-value: var(--text-accent);
  --chip-pad-block: 6px;
  --chip-pad-inline: 10px;
  --chip-min-h: 26px;
  --chip-offset: var(--specimen-inset);

  /* annotation — heading spec line, bracketed section coordinate */
  --spec-line-gap: var(--space-xs);
  --bracket-arm: 7px;
  --bracket-gap: 10px;
  --bracket-color: var(--border-accent-soft);
  --bracket-width: var(--border-width-hair);

  /* ledger — spec-table-as-layout: display-size name, micro data, hairline rows */
  --ledger-row-min-h: clamp(7.5rem, 9vw, 10.5rem);
  --ledger-pad-block: clamp(1.25rem, 0.9rem + 1.4vw, 2rem);
  --ledger-col-gap: clamp(1rem, 0.6rem + 1.6vw, 2.5rem);
  --ledger-grid: minmax(13rem, 26fr) minmax(15rem, 42fr) minmax(6.5rem, 13fr);
  --ledger-micro-cols: 2;
  --ledger-rule: var(--border-hairline);
  --ledger-rule-active: var(--border-accent-soft);
  --ledger-hover: var(--surface-alt);

  /* full-bleed photo band — 100vw, no container, tiles butted, micro label bottom-left */
  --bleed-band-h: clamp(20rem, 42vw, 38.75rem);
  --bleed-label-inset: clamp(0.75rem, 1.4vw, 1.5rem);
  --bleed-seam: var(--border-width-hair);
  --bleed-seam-ink: var(--_ink-990);

  /* bordered cell rail — trust marques */
  --cell-min-w: clamp(9.375rem, 14vw, 14.375rem);
  --cell-h: clamp(4.5rem, 6vw, 6.5rem);

  /* easing — four curves, four jobs */
  --ease-entrance: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-state: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-exit: cubic-bezier(0.4, 0, 1, 1);
  --ease-lift: cubic-bezier(0.34, 1.2, 0.64, 1);

  /* duration */
  --dur-instant: 120ms;
  --dur-fast: 200ms;
  --dur-base: 320ms;
  --dur-accordion: 340ms;
  --dur-slow: 480ms;
  --dur-hero: 600ms;
  --dur-sheen-compact: 380ms;
  --dur-sheen-broad: 600ms;
  --dur-marquee: 38s;
  --stagger-tight: 40ms;
  --stagger-base: 60ms;
  --stagger-loose: 80ms;

  /* transform amounts */
  --lift-card: -6px;
  --lift-button: -2px;
  --lift-tile: -4px;
  --lift-ledger: -2px;
  --scale-media-hover: 1.045;
  --scale-bleed-hover: 1.03;
  --reveal-shift: 24px;

  /* controls & components */
  --control-h-sm: 40px;
  --control-h: 48px;
  --control-h-lg: 56px;
  --control-pad-x: 1.5rem;
  --control-pad-x-lg: 2rem;
  --card-pad: clamp(1rem, 0.8rem + 0.6vw, 1.5rem);
  --card-pad-lg: clamp(1.5rem, 1.2rem + 1vw, 2.25rem);
  --accordion-row-pad: clamp(1rem, 0.85rem + 0.5vw, 1.5rem);
  --icon-sm: 16px;
  --icon-md: 20px;
  --icon-lg: 24px;
  --icon-circle: 56px;
  --icon-circle-lg: 72px;
  --step-circle: clamp(56px, 4vw, 76px);
  --avatar: 44px;
  --brandmark-h: clamp(28px, 2.2vw, 40px);
  --header-h: clamp(60px, 4.4vw, 76px);
  /* floor 3.5rem: a 20px peek at 390 could not ghost an ~83px marque. 6vw governs
     from 934px up, so the jobs rail (consumer >= 1024) is unchanged. */
  --rail-peek: clamp(3.5rem, 6vw, 5rem);

  /* reserved media ratios — CLS 0 */
  --ratio-hero: 16 / 9;
  --ratio-card: 4 / 3;
  --ratio-tall: 3 / 4;
  --ratio-portrait: 2 / 3;
  --ratio-wide: 21 / 9;
  --ratio-bleed: 3 / 2;
  --ratio-square: 1 / 1;

  /* z-index */
  --z-below: -1;
  --z-media: 1;
  --z-scrim: 2;
  --z-content: 3;
  --z-raised: 10;
  --z-sticky: 20;
  --z-header: 40;
  --z-overlay: 60;
  --z-drawer: 80;
  --z-grain: 90;
  --z-skiplink: 200;

  /* breakpoints */
  --bp-md: 768px;
  --bp-lg: 1024px;
  --bp-xl: 1280px;
  --bp-2xl: 1600px;
  --bp-3xl: 1920px;
}

/* --------------------------------------------------------------------------
   COMPOSED TICK RULES — declared on every element, not on :root, on purpose.
   A custom property substitutes its var() references on the element where it
   is DECLARED. A :root-level --tick-rule therefore inherits already resolved
   to the root inks and the 8px pitch, and every local override of --tick-ink
   is inert (statband.css measured this). Declared here, each element rebuilds
   the rule from the --tick-* inputs it inherits, so setting --tick-ink on a
   section root recolours every rule inside it. Pseudo-elements are listed so
   an override placed on the pseudo itself also takes.
   Cost: three gradient strings per element, never rasterised until used.
   -------------------------------------------------------------------------- */
*, ::before, ::after {
  --tick-pitch-major: calc(var(--tick-pitch) * 5);
  --tick-rule-h: var(--tick-len-major);
  --tick-rule-w: var(--tick-len-major);
  /* horizontal: marks hang from the top edge, hairline along the top */
  --tick-rule:
    repeating-linear-gradient(90deg,var(--tick-ink-major) 0 var(--border-width-hair),transparent var(--border-width-hair) var(--tick-pitch-major)) 0 0/100% var(--tick-len-major) no-repeat,
    repeating-linear-gradient(90deg,var(--tick-ink) 0 var(--border-width-hair),transparent var(--border-width-hair) var(--tick-pitch)) 0 0/100% var(--tick-len) no-repeat,
    linear-gradient(var(--tick-ink),var(--tick-ink)) 0 0/100% var(--border-width-hair) no-repeat;
  --tick-rule-fine:
    repeating-linear-gradient(90deg,var(--tick-ink) 0 var(--border-width-hair),transparent var(--border-width-hair) var(--tick-pitch-fine)) 0 0/100% var(--tick-len-fine) no-repeat,
    linear-gradient(var(--tick-ink),var(--tick-ink)) 0 0/100% var(--border-width-hair) no-repeat;
  /* vertical: --tick-rule stood on end. Marks grow from the left edge,
     hairline along the left; size the box with --tick-rule-w or offset with
     background-position-x to centre the axis in a gutter. */
  --tick-rule-vertical:
    repeating-linear-gradient(180deg,var(--tick-ink-major) 0 var(--border-width-hair),transparent var(--border-width-hair) var(--tick-pitch-major)) 0 0/var(--tick-len-major) 100% no-repeat,
    repeating-linear-gradient(180deg,var(--tick-ink) 0 var(--border-width-hair),transparent var(--border-width-hair) var(--tick-pitch)) 0 0/var(--tick-len) 100% no-repeat,
    linear-gradient(var(--tick-ink),var(--tick-ink)) 0 0/var(--border-width-hair) 100% no-repeat;
}

/* ==========================================================================
   BASE — reset, faces, element defaults, layout primitives, shared utilities.
   Every value is a token. Reasoning: docs/art-direction.md
   ========================================================================== */

/* --------------------------------------------------------------------------
   FACES
   Both families self-hosted variable WOFF2. font-display: swap.
   The swap is metrically neutral: each fallback family named in the token
   stacks is declared from a local() source with size-adjust and metric
   overrides MEASURED in-browser, so the fallback fills the same box.
   Chivo ascent 0.940 / descent 0.250 / zero line gap.
   Martian Mono ascent 1.000 / descent 0.200 / zero line gap.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: Chivo;
  src: url("../assets/fonts/chivo-latin-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Martian Mono";
  src: url("../assets/fonts/martian-mono-latin-var.woff2") format("woff2");
  font-weight: 100 800;
  font-stretch: 75% 112.5%;
  font-style: normal;
  font-display: swap;
}

/* body copy — Chivo 400/500 vs Arial regular */
@font-face {
  font-family: "Chivo Fallback";
  src: local("Arial"), local("Liberation Sans"), local("Helvetica Neue");
  font-weight: 100 500;
  size-adjust: 106.97%;
  ascent-override: 87.88%;
  descent-override: 23.37%;
  line-gap-override: 0%;
}
/* controls — Chivo 600 vs Arial Bold. local() resolves a FACE, so a bold band
   must name the bold face: local("Arial") inside a 601+ band returns regular
   and the browser will not synthesise over it. */
@font-face {
  font-family: "Chivo Fallback";
  src: local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold");
  font-weight: 501 650;
  size-adjust: 101.17%;
  ascent-override: 92.91%;
  descent-override: 24.71%;
  line-gap-override: 0%;
}
/* headings — Chivo 700 vs Arial Bold */
@font-face {
  font-family: "Chivo Fallback";
  src: local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold");
  font-weight: 651 800;
  size-adjust: 101.69%;
  ascent-override: 92.44%;
  descent-override: 24.58%;
  line-gap-override: 0%;
}
/* wordmark — Chivo 900 vs Arial Black, which is narrower and much taller */
@font-face {
  font-family: "Chivo Fallback";
  src: local("Arial Black"), local("Arial-Black"), local("Liberation Sans Bold");
  font-weight: 801 900;
  size-adjust: 88.58%;
  ascent-override: 106.12%;
  descent-override: 28.22%;
  line-gap-override: 0%;
}
/* mono — measured against Consolas. Advance does not vary with weight. */
@font-face {
  font-family: "Martian Mono Fallback";
  src: local("Consolas"), local("DejaVu Sans Mono"), local("Liberation Mono"), local("Menlo");
  font-weight: 100 800;
  size-adjust: 127.32%;
  ascent-override: 78.54%;
  descent-override: 15.71%;
  line-gap-override: 0%;
}

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

html {
  /* THE ONE DECLARATION THAT MAKES UA CHROME AGREE ACROSS PLATFORMS.
     Everything the page does not draw itself — the scrollbar, the select
     popup, the text caret, the spellcheck underline, the autofill wash, the
     canvas behind the document before CSS arrives, the date and file pickers
     — is painted by the platform from this keyword. Without it: light
     scrollbars beside a near-black page on Windows in all three engines, a
     white select list in Firefox, a pale-blue Chrome autofill block over the
     dark booking fields, and a light native bezel on every iOS control.
     One keyword, and none of that has to be re-styled per engine. */
  color-scheme: dark;
  /* iOS inflates type on orientation change unless told not to; the
     unprefixed property is what Chrome and the newer WebKit read. Neither
     disables user zoom — that is maximum-scale, which is not set. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: calc(var(--header-h) + var(--space-md));
}

/* opt in rather than opt out: a reduced-motion reset scoped differently from
   the state it undoes is the failure recorded in docs/lessons.md */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin-block: 0;
  margin-inline: auto;
  max-width: var(--width-page);
  background: var(--surface-page);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--text-body);
  font-weight: var(--weight-regular);
  line-height: var(--leading-body);
  overflow-x: hidden;

  /* TYPE RENDERING — a deliberate position, not a default.
     macOS renders light-on-dark type with stem darkening and looks a weight
     heavier than the same page on Windows, which is the difference the client
     sees. Two properties can be set and only these two:
     - -webkit-font-smoothing: antialiased turns that off in Safari and Chrome
       on macOS and in every iOS browser. It is a no-op on Windows and Linux,
       where Chrome and Edge use DirectWrite and ignore it.
     - -moz-osx-font-smoothing: grayscale is the same switch for Firefox on
       macOS. Without it Firefox is the odd one out ON THE SAME MAC, which is
       a divergence we can actually close; the macOS-versus-Windows one is not
       reachable from CSS and is accepted.
     text-rendering stays at its initial `auto`, stated here so nobody adds
     optimizeLegibility later: it forces the full shaping path, and on the
     variable Chivo face it changes measured line breaks between engines —
     the opposite of what this pass is for. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: auto;

  /* iOS paints a translucent grey block over any element it decides was
     tapped, which lands as a rectangle over the pill radius on every accent
     button and over the chamfer on every card. Removed page-wide because
     every control here already answers a press with its own :active
     transform; nothing loses its feedback. */
  -webkit-tap-highlight-color: transparent;
}

h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}

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

a { color: inherit; text-decoration: none; }

button, input, select, textarea { font: inherit; color: inherit; margin: 0; }
button { background: none; border: 0; cursor: pointer; }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-inset);
}

::selection { background: var(--surface-accent); color: var(--text-on-accent); }

/* --------------------------------------------------------------------------
   GRAIN — one fixed layer over the whole page, no HTTP request
   -------------------------------------------------------------------------- */
.grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-grain);
  pointer-events: none;
  background-image: var(--grain-image);
  background-size: var(--grain-size) var(--grain-size);
  opacity: var(--grain-opacity);
}

/* --------------------------------------------------------------------------
   LAYOUT PRIMITIVES
   -------------------------------------------------------------------------- */
/* The gutter is also the safe-area guard. head.html ships viewport-fit=cover,
   so on a notched iPhone in LANDSCAPE the page runs under the sensor housing
   and the inline insets are ~44px; in portrait they are 0 and this resolves
   back to --gutter exactly. Written as max() rather than an addition so the
   measure is unchanged wherever there is no inset — which is every desktop.
   env() with a 0px fallback also covers a browser that knows the function but
   not the keyword. */
.wrap {
  width: 100%;
  max-width: var(--width-content);
  margin-inline: auto;
  padding-inline: var(--gutter);
  padding-inline: max(var(--gutter), env(safe-area-inset-left, 0px)) max(var(--gutter), env(safe-area-inset-right, 0px));
}
.wrap--wide { max-width: var(--width-wide); }
.wrap--prose { max-width: var(--width-measure); }

.grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr));
  gap: var(--grid-gap);
}

/* FULL BLEED. 100vw includes the classic scrollbar on Windows and Linux but
   100% does not, so `width: 100vw` on a centred box overhangs the viewport by
   half a scrollbar at each edge — and above --width-page, where <body> stops
   growing, it overhangs by half the difference. Both are clamped here rather
   than left to an ancestor's overflow to hide:
   - the reach is capped at --width-page, so the wide case is exact;
   - the scrollbar residue (<=8px a side, 0 on macOS overlay scrollbars and 0
     on iOS) cannot be removed in CSS, because no unit reports the viewport
     minus its scrollbar. Where the bleeding element is already a child of a
     full-width block — .workband, .refusal__band — the vw maths is deleted
     instead and `width: auto` is exact everywhere. Prefer that. */
.bleed {
  width: auto;
  margin-inline: calc(50% - min(50vw, var(--width-page) / 2));
}

.stack > * + * { margin-block-start: var(--space-md); }

.section { padding-block: var(--space-section); }
.section--tight { padding-block: var(--space-section-tight); }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   SHARED TYPE COMPONENTS
   -------------------------------------------------------------------------- */
/* Section coordinate: [ 04 / SERVICES ]. The brackets are DRAWN, not typed —
   typed brackets in Martian Mono are heavy, their sidebearings fix the internal
   spacing, and they land in the accessible name as punctuation. */
.eyebrow {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding-inline: calc(var(--bracket-arm) + var(--bracket-gap));
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.eyebrow::before,
.eyebrow::after {
  content: "";
  position: absolute;
  inset-block: 0;
  width: var(--bracket-arm);
  border-block: var(--bracket-width) solid var(--bracket-color);
}
.eyebrow::before {
  inset-inline-start: 0;
  border-inline-start: var(--bracket-width) solid var(--bracket-color);
}
.eyebrow::after {
  inset-inline-end: 0;
  border-inline-end: var(--bracket-width) solid var(--bracket-color);
}
.eyebrow__idx { color: var(--text-accent); }

/* All section heads are left-aligned. Only the hero is centred: a mix of the
   two across fourteen sections reads as an accident, and centred-everything is
   the shared tell with both reference models. */
.sec-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-sm);
  text-align: start;
  max-width: var(--width-measure);
  margin-block-end: var(--space-stack);
}
/* the section boundary, graduated */
.sec-head::before {
  content: "";
  align-self: stretch;
  height: var(--tick-rule-h);
  background: var(--tick-rule);
}

.sec-title {
  font-size: var(--text-h2);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-display);
}

/* ACCENT GRAMMAR, page-wide: accent INK may only touch a number, a unit, a
   tick or a marker. Accent FILL is unrestricted. So the spec line sets in
   muted ink and only its figures are accented. */
.sec-spec {
  margin-block-start: calc(var(--spec-line-gap) - var(--space-sm));
  font-family: var(--font-mono);
  font-size: var(--text-spec);
  font-weight: var(--weight-medium);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-mono-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.sec-spec b { color: var(--text-accent); font-weight: var(--weight-medium); }

.accent { color: var(--text-accent); }

.sec-lead {
  font-size: var(--text-lead);
  line-height: var(--leading-body);
  color: var(--text-muted);
}

/* µ (U+00B5) uppercases to Greek capital Mu, so text-transform turns "µm"
   into "MM" — a corrupted reading on a page about readings. Every unit inside
   an uppercased string carries this. */
.u { text-transform: none; }

.mono {
  font-family: var(--font-mono);
  letter-spacing: var(--tracking-mono-tight);
  font-feature-settings: "tnum" 1;
}

/* --------------------------------------------------------------------------
   BUTTONS — one hover mechanism, a skewed sheen, compact calibration
   -------------------------------------------------------------------------- */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-height: var(--control-h);
  padding-inline: var(--control-pad-x);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--text-body-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  overflow: hidden;
  transition:
    transform var(--dur-fast) var(--ease-lift),
    background-color var(--dur-fast) var(--ease-state),
    border-color var(--dur-fast) var(--ease-state),
    box-shadow var(--dur-fast) var(--ease-lift);
}
.btn::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(var(--sheen-width-compact) * -1);
  width: var(--sheen-width-compact);
  background: var(--sheen-color);
  transform: skewX(var(--sheen-skew)) translate3d(0, 0, 0);
  transition: transform var(--dur-sheen-compact) var(--ease-state);
  pointer-events: none;
}
.btn:hover::after { transform: skewX(var(--sheen-skew)) translate3d(400%, 0, 0); }

.btn--lg { min-height: var(--control-h-lg); padding-inline: var(--control-pad-x-lg); }

.btn--primary { background: var(--surface-accent); color: var(--text-on-accent); }
.btn--primary:hover {
  background: var(--surface-accent-hover);
  transform: translate3d(0, var(--lift-button), 0);
  box-shadow: var(--shadow-accent);
}
.btn--primary:active { transform: translate3d(0, 0, 0); box-shadow: var(--shadow-none); }

.btn--ghost {
  border: var(--border-width-hair) solid var(--border-soft);
  color: var(--text-primary);
}
.btn--ghost:hover {
  border-color: var(--border-accent);
  color: var(--text-primary);
  transform: translate3d(0, var(--lift-button), 0);
}
.btn--ghost:active { transform: translate3d(0, 0, 0); }

.btn--dark { background: var(--surface-invert-ink); color: var(--text-primary); }
.btn--dark:hover {
  background: var(--surface-raised-hover);
  transform: translate3d(0, var(--lift-button), 0);
}
.btn--dark:active { transform: translate3d(0, 0, 0); }

/* pill CTA at the foot of a card */
.pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  min-height: var(--control-h-sm);
  padding-inline: var(--space-md);
  border-radius: var(--radius-pill);
  background: var(--surface-accent-wash);
  border: var(--border-width-hair) solid var(--border-accent-soft);
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  transition:
    background-color var(--dur-fast) var(--ease-state),
    color var(--dur-fast) var(--ease-state),
    transform var(--dur-fast) var(--ease-lift);
}
.pill:hover {
  background: var(--surface-accent);
  color: var(--text-on-accent);
  transform: translate3d(0, var(--lift-button), 0);
}
.pill:active { transform: translate3d(0, 0, 0); }

/* --------------------------------------------------------------------------
   ICONS — inline sprite, stroked, painted from currentColor
   -------------------------------------------------------------------------- */
.icon {
  width: var(--icon-lg);
  height: var(--icon-lg);
  fill: none;
  stroke: currentColor;
  stroke-width: var(--border-width-medium);
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}
.icon--sm { width: var(--icon-md); height: var(--icon-md); }
.icon--xs { width: var(--icon-sm); height: var(--icon-sm); }
.icon--fill { fill: currentColor; stroke: none; }

/* THE MARK — the depth gauge, from #i-mark. Two inks, not three: the frame and
   the pale graduations take currentColor like every other icon here, and only
   the reading takes its own, so a parent can move the reading alone.
   --mark-read is set here rather than inside the symbol because a <use> shadow
   tree is closed to document selectors — an inherited custom property is the
   only way to paint into it, and the only way a hover can reach it.
   Miter joins: the 45-degree chamfer is the whole point of the frame, and the
   round join .icon sets for the stroked pictograms sands it off. */
.icon--mark {
  --mark-read: var(--text-accent);
  width: var(--icon-md);
  height: var(--icon-md);
  color: var(--text-secondary);
  stroke-linejoin: miter;
}

.icon-circle {
  display: grid;
  place-items: center;
  width: var(--icon-circle);
  height: var(--icon-circle);
  border-radius: var(--radius-round);
  background: var(--surface-accent-wash);
  border: var(--border-width-hair) solid var(--border-accent-soft);
  color: var(--text-accent);
  flex: none;
}

/* --------------------------------------------------------------------------
   MEDIA — every photo box reserves its ratio, so CLS stays 0
   -------------------------------------------------------------------------- */
.media {
  position: relative;
  overflow: hidden;
  background: var(--surface-media);
}
/* <picture> is the direct child, so the image is addressed through it and
   taken out of flow: the box is sized by its reserved ratio, never by the
   intrinsic height of the photograph. */
.media picture { display: contents; }
.media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-entrance);
}

/* SPECIMEN TREATMENT — no photograph ships plain.
   ::after carries the tint AND the inset bezel. The bezel is drawn as an
   outline with a negative offset rather than a border on a second element:
   it stays a sharp square inside the rounded, chamfered card, and it does not
   ride along when the image scales on hover. */
.media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: var(--z-scrim);
  background: var(--overlay-media);
  outline: var(--specimen-frame-width) solid var(--specimen-frame);
  outline-offset: calc(var(--specimen-inset) * -1);
  transition: background-color var(--dur-fast) var(--ease-state);
  pointer-events: none;
}
.media--lg::after { outline-offset: calc(var(--specimen-inset-lg) * -1); }

/* the specular band: narrow and bright, the way a light actually crosses
   paint. Third calibration of the one sheen mechanism. */
.media::before {
  content: "";
  position: absolute;
  z-index: var(--z-content);
  inset-block: calc(var(--space-lg) * -1);
  /* parked four band-widths out: the skew displaces the band horizontally by
     tan(18deg) x half the height, which on a 900px hero pushed a one-width
     offset back into view as a static streak. */
  inset-inline-start: calc(var(--sheen-width-specular) * -4);
  width: var(--sheen-width-specular);
  background: var(--sheen-color-specular);
  transform: skewX(var(--sheen-skew)) translate3d(0, 0, 0);
  transition: transform var(--dur-sheen-broad) var(--ease-state);
  pointer-events: none;
}
.media:hover::before { transform: skewX(var(--sheen-skew)) translate3d(1200%, 0, 0); }

/* SPEC CHIP — bottom-left, aligned to the bezel. Carries a true value: a
   reading where one exists, otherwise a real figure from the price table.
   Never an invented measurement. */
.chip {
  position: absolute;
  z-index: var(--z-content);
  inset-block-end: var(--chip-offset);
  inset-inline-start: var(--chip-offset);
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  min-height: var(--chip-min-h);
  padding: var(--chip-pad-block) var(--chip-pad-inline);
  border-radius: var(--radius-sharp);
  background: var(--chip-bg);
  border: var(--border-width-hair) solid var(--chip-border);
  color: var(--chip-text);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-mono-label);
  text-transform: uppercase;
  /* A chip is a reading, so it is never truncated. It sits inside a box that
     is both overflow-hidden and clip-path-chamfered, so anything that does not
     fit is silently cut — which is exactly how "FROM $1,850" shipped as
     "FROM $1". Wrapping, not nowrap, is the only safe default. */
  white-space: normal;
  max-width: calc(100% - var(--chip-offset) * 2);
}
.chip b { color: var(--chip-value); font-weight: var(--weight-medium); }
.chip--quiet b { color: var(--text-accent-quiet); }
.chip--lg { inset-block-end: var(--specimen-inset-lg); inset-inline-start: var(--specimen-inset-lg); }

/* --------------------------------------------------------------------------
   THE CHAMFER — the shape signature. Top-right, every block, never a control.
   clip-path removes box-shadow and clips both drop-shadow and the focus
   outline, so: no cast shadow on the dark ground (a black cast shadow on the
   near-black ground was never doing the work), and focusable chamfered
   elements ring with --focus-ring-inset.
   The cut eats the border along the diagonal; --chamfer-edge is the
   background layer that draws the missing hairline back.
   -------------------------------------------------------------------------- */
.chamfer { clip-path: var(--chamfer-clip); }
.chamfer--lg { --chamfer: var(--chamfer-lg); }
.chamfer--sm { --chamfer: var(--chamfer-sm); }

/* --------------------------------------------------------------------------
   TICK RULE — one construction, four inks. Horizontal only.
   -------------------------------------------------------------------------- */
.rule {
  height: var(--tick-rule-h);
  background: var(--tick-rule);
}

/* --------------------------------------------------------------------------
   REVEALS
   The hidden state scopes under .motion-ready — a class added only once the
   motion library has actually loaded. Never under .js: .js is set before first
   paint and would hide this content forever on a blocked CDN.
   The reduced-motion reset is scoped exactly like the state it undoes, because
   a media query adds no specificity.
   -------------------------------------------------------------------------- */
.motion-ready [data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--reveal-shift), 0);
}
@media (prefers-reduced-motion: reduce) {
  .motion-ready [data-reveal] { opacity: 1; transform: none; }
}

/* ==========================================================================
   SHELL — skip link, fixed header, mobile drawer, sticky call bar.
   Above the fold: loads render-blocking.
   ========================================================================== */

/* fixed to the viewport corner, so with viewport-fit=cover it is the page's
   job to keep it out of the sensor housing in landscape */
.skip-link {
  position: fixed;
  inset-block-start: max(var(--space-2xs), env(safe-area-inset-top, 0px));
  inset-inline-start: max(var(--space-2xs), env(safe-area-inset-left, 0px));
  z-index: var(--z-skiplink);
  padding: var(--space-2xs) var(--space-sm);
  border-radius: var(--radius-inset);
  background: var(--surface-accent);
  color: var(--text-on-accent);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  transform: translate3d(0, calc(var(--space-4xl) * -1), 0);
  transition: transform var(--dur-fast) var(--ease-entrance);
}
.skip-link:focus-visible { transform: translate3d(0, 0, 0); }

/* --------------------------------------------------------------------------
   HEADER — a hairline row over everything, no rhythm of its own
   -------------------------------------------------------------------------- */
.header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--z-header);
  max-width: var(--width-page);
  margin-inline: auto;
  background: var(--surface-header);
  border-block-end: var(--border-width-hair) solid var(--border-hairline);
  transition: box-shadow var(--dur-base) var(--ease-state);
}
.header--scrolled { box-shadow: var(--shadow-header); }

.header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  min-height: var(--header-h);
}

.header__mark {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-h4);
  letter-spacing: var(--tracking-label);
  color: var(--text-primary);
  transition: color var(--dur-fast) var(--ease-state);
}
/* the word is one flex item, so the gap lands between mark and wordmark and
   never between BLACKLINE and its full stop */
.header__mark-word { white-space: nowrap; }
/* the gauge's reading and the wordmark's full stop are the same accent doing
   the same job, so they move together or the pair reads as two objects */
.header__mark:hover .header__mark-dot { color: var(--text-accent-strong); }
.header__mark:hover .icon--mark { --mark-read: var(--text-accent-strong); }
.header__mark-dot { color: var(--text-accent); }

/* stretched so a submenu panel can hang off the header's own bottom edge
   rather than off the bottom of a 33px-tall link */
.header__nav { margin-inline-start: auto; align-self: stretch; display: flex; }

.header__list {
  display: none;
  align-items: center;
  gap: var(--space-lg);
}

.header__item {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3xs);
}

.header__link {
  position: relative;
  display: inline-block;
  padding-block: var(--space-2xs);
  font-size: var(--text-body-sm);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  color: var(--text-secondary);
  transition: color var(--dur-fast) var(--ease-state);
}
.header__link::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  width: 100%;
  height: var(--border-width-medium);
  background: var(--surface-accent);
  transform: scale3d(0, 1, 1);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease-state);
}
.header__link:hover { color: var(--text-primary); }
.header__link:hover::after { transform: scale3d(1, 1, 1); }
.header__link:active { color: var(--text-primary); }

/* WHERE AM I. The accent lands as a rule under the word, never as ink in it:
   --here is the parent of the open section, [aria-current] the page itself.
   Both use the underline the link already draws on hover, latched on. */
.header__link--here,
.header__link[aria-current="page"] { color: var(--text-primary); }
.header__link--here::after,
.header__link[aria-current="page"]::after { transform: scale3d(1, 1, 1); }

/* --------------------------------------------------------------------------
   SUBMENU — a parent stays a link; this button is the disclosure beside it.
   The marker is the site's accordion marker, the one .faq__marker uses: a
   cross that turns 45deg, not a chevron. Hidden below 1024, where the list is
   only ever seen with JS off and the submenu is simply part of it.
   -------------------------------------------------------------------------- */
.header__disc {
  display: none;
  place-items: center;
  width: var(--icon-md);
  height: var(--icon-md);
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-state), transform var(--dur-fast) var(--ease-state);
}

/* Below 1024 this is a plain nested list. It is the JS-off fallback and a
   phone has no hover, so a panel here would be a menu nobody can open. */
.header__sub {
  flex: 1 0 100%;
  display: grid;
  gap: var(--space-3xs);
  padding-block-start: var(--space-3xs);
  padding-inline-start: var(--space-sm);
  background: var(--tick-rule-vertical);
}

.header__sublink {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: var(--space-2xs);
  padding: var(--space-3xs) var(--space-2xs);
  border-radius: var(--radius-inset);
  font-size: var(--text-body-sm);
  color: var(--text-secondary);
  transition: background-color var(--dur-fast) var(--ease-state), color var(--dur-fast) var(--ease-state);
}
.header__sublink:hover { background: var(--surface-raised-hover); color: var(--text-primary); }
.header__sublink[aria-current="page"] { background: var(--surface-accent-wash); color: var(--text-primary); }

/* accent ink is allowed on a numeral, never on the label — base.css */
.header__sublink-idx {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-mono-label);
  color: var(--text-faint);
  transition: color var(--dur-fast) var(--ease-state);
}
.header__sublink:hover .header__sublink-idx,
.header__sublink[aria-current="page"] .header__sublink-idx { color: var(--text-accent); }

.header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-inline-start: auto;
}

.header__phone {
  display: none;
  align-items: center;
  gap: var(--space-3xs);
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  white-space: nowrap;
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-state);
}
.header__phone:hover { color: var(--text-accent); }

/* the row is one line at 1024 or --header-h is a lie and the fixed header
   crops the page it offsets */
.header__cta { display: none; white-space: nowrap; }

.header__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  min-height: var(--control-h-sm);
  padding-inline: var(--space-sm);
  border: var(--border-width-hair) solid var(--border-soft);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  transition: border-color var(--dur-fast) var(--ease-state), color var(--dur-fast) var(--ease-state);
}
.header__toggle:hover { border-color: var(--border-accent); color: var(--text-accent); }

/* --------------------------------------------------------------------------
   DRAWER — with JS off the toggle is hidden and the nav list shows inline
   -------------------------------------------------------------------------- */
.drawer {
  position: fixed;
  inset-block-start: var(--header-h);
  inset-inline: 0;
  z-index: var(--z-drawer);
  max-width: var(--width-page);
  margin-inline: auto;
  padding: var(--space-lg) var(--gutter) var(--space-xl);
  /* landscape on a notched iPhone puts the inline insets at ~44px, and the
     drawer's own padding is what has to clear them because it is fixed and
     therefore outside .wrap */
  padding-inline: max(var(--gutter), env(safe-area-inset-left, 0px)) max(var(--gutter), env(safe-area-inset-right, 0px));
  padding-block-end: calc(var(--space-xl) + env(safe-area-inset-bottom, 0px));
  /* both groups expanded runs 894px against an 844px phone, so the drawer
     scrolls rather than pushing its own CTAs off the screen.
     svh is unknown to Safari before 15.4, and an unknown unit drops the whole
     declaration — which here would remove the cap and let the drawer run past
     the bottom of the screen with its CTAs on it. vh first, svh second: old
     engines keep the vh line, new ones overwrite it. Same pattern in
     hero.css. */
  max-block-size: calc(100vh - var(--header-h));
  max-block-size: calc(100svh - var(--header-h));
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--surface-sunken);
  border-block-end: var(--border-width-hair) solid var(--border-strong);
  transform: translate3d(0, -100%, 0);
  transition: transform var(--dur-base) var(--ease-state), visibility var(--dur-base) var(--ease-state);
  visibility: hidden;
}
.drawer[data-open="true"] { transform: translate3d(0, 0, 0); visibility: visible; }

.drawer__list { display: grid; gap: var(--space-xs); }
.drawer__link {
  position: relative;
  display: block;
  padding-block: var(--space-xs);
  border-block-end: var(--border-width-hair) solid var(--border-hairline);
  font-family: var(--font-display);
  font-size: var(--text-h3);
  font-weight: var(--weight-bold);
  color: var(--text-primary);
  transition: color var(--dur-fast) var(--ease-state), transform var(--dur-fast) var(--ease-lift);
}
.drawer__link:hover { color: var(--text-accent); transform: translate3d(var(--space-2xs), 0, 0); }
.drawer__foot { margin-block-start: var(--space-lg); display: grid; gap: var(--space-xs); }

/* --------------------------------------------------------------------------
   DRAWER GROUP — accordion, never an overlay. A second sheet sliding over the
   first is where a phone visitor loses the way back; this one just grows.
   The row carries the hairline so the disclosure sits inside the same rule as
   the link rather than beside a border that stops short of it.
   -------------------------------------------------------------------------- */
.drawer__row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  border-block-end: var(--border-width-hair) solid var(--border-hairline);
}
.drawer__row .drawer__link { flex: 1; border-block-end: 0; }

.drawer__disc {
  display: grid;
  place-items: center;
  flex: none;
  width: var(--control-h-sm);
  height: var(--control-h-sm);
  border: var(--border-width-hair) solid var(--border-soft);
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-state), border-color var(--dur-fast) var(--ease-state), transform var(--dur-fast) var(--ease-state);
}
.drawer__disc:hover { border-color: var(--border-accent); color: var(--text-accent); }

/* Instant, both ways: an accordion's height is not a transform and this site
   does not animate height. Which also satisfies the reduced-motion rule
   without a second code path. */
.drawer__sub {
  display: none;
  gap: var(--space-3xs);
  padding-block: var(--space-2xs) var(--space-xs);
  padding-inline-start: var(--space-md);
  background: var(--tick-rule-vertical);
}
.drawer__item[data-open="true"] .drawer__sub { display: grid; }
.drawer__item[data-open="true"] .drawer__disc { color: var(--text-accent); border-color: var(--border-accent); transform: rotate(45deg); }

.drawer__sublink {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: var(--space-2xs);
  padding-block: var(--space-2xs);
  font-size: var(--text-body-sm);
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-state), transform var(--dur-fast) var(--ease-lift);
}
.drawer__sublink:hover { color: var(--text-primary); transform: translate3d(var(--space-2xs), 0, 0); }
.drawer__sublink[aria-current="page"] { color: var(--text-primary); }

.drawer__sublink-idx {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-mono-label);
  color: var(--text-faint);
}
.drawer__sublink[aria-current="page"] .drawer__sublink-idx { color: var(--text-accent); }

/* same latch as the header: a short accent rule, not accent ink */
.drawer__link--here::after,
.drawer__link[aria-current="page"]::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  width: var(--space-lg);
  height: var(--border-width-bar);
  background: var(--surface-accent);
}

/* JS off: no drawer, the list is the nav */
html:not(.js) .drawer { display: none; }
html:not(.js) .header__toggle { display: none; }
html:not(.js) .header__list { display: flex; flex-wrap: wrap; gap: var(--space-sm); }

/* ...and below 1024 that list carries both submenus inline, because a phone
   has no hover to open a panel with. Measured at 390: 637px of header. A
   fixed element that tall does not sit over the page, it replaces it — so
   with no script and no drawer the header stops being fixed and becomes what
   it actually is, the document's opening block. Fixed positioning is a
   consequence of the 60px bar, not a property of the header.
   Above 1024 nothing changes: the list is one row and the submenus are
   absolutely positioned panels, so the bar is 60px and stays fixed. */
@media (max-width: 1023px) {
  html:not(.js) .header { position: static; }
  html:not(.js) body { padding-block-start: 0; }
  /* and once it is a block it should read as one: mark, then the whole nav in
     a single column, rather than five links reflowing around a wordmark */
  html:not(.js) .header__inner { flex-wrap: wrap; row-gap: var(--space-sm); padding-block: var(--space-sm); }
  html:not(.js) .header__nav { flex: 1 0 100%; margin-inline-start: 0; }
  html:not(.js) .header__list { flex-direction: column; align-items: stretch; }
}

/* --------------------------------------------------------------------------
   STICKY CALL BAR — mobile only
   -------------------------------------------------------------------------- */
.callbar {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: var(--z-sticky);
  display: flex;
  gap: var(--space-2xs);
  padding: var(--space-2xs);
  /* THE HOME INDICATOR. head.html ships viewport-fit=cover, so the fixed bar's
     bottom edge is the physical bottom of the screen and the system gesture
     bar is drawn over the last ~34px of it. Both buttons sat partly behind it.
     env() resolves to 0px on every device without an inset, so the bar is
     unchanged on Android, on a desktop narrow window and on a pre-notch
     iPhone. Declared after the shorthand so the shorthand cannot re-flatten
     it, and the plain --space-2xs line stays as the fallback for an engine
     that does not know env() at all. */
  padding-block-end: calc(var(--space-2xs) + env(safe-area-inset-bottom, 0px));
  padding-inline: max(var(--space-2xs), env(safe-area-inset-left, 0px)) max(var(--space-2xs), env(safe-area-inset-right, 0px));
  background: var(--surface-sunken);
  border-block-start: var(--border-width-hair) solid var(--border-strong);
}
.callbar__btn { flex: 1; padding-inline: var(--space-2xs); white-space: nowrap; }

@media (min-width: 768px) {
  .header__phone { display: inline-flex; }
  .header__cta { display: inline-flex; }
}

@media (min-width: 1024px) {
  /* two disclosure buttons cost ~48px of row; the wide gap comes back at 1280 */
  .header__list { display: flex; align-items: stretch; gap: var(--space-md); }
  .header__toggle { display: none; }
  .drawer { display: none; }
  .callbar { display: none; }
  .header__actions { margin-inline-start: 0; }

  /* full height so inset-block-start: 100% is the header's own bottom edge */
  .header__item { height: 100%; flex-wrap: nowrap; }
  .header__disc { display: grid; }

  /* ------------------------------------------------------------------------
     THE PANEL. Not a translucent sheet over a photograph: a real opaque
     surface a step above the header, the site's chamfer on the top-right
     corner, and a cast shadow. clip-path deletes box-shadow, so the shadow is
     the drop-shadow pair made for chamfered objects — applied to this same
     element, which is what makes it follow the cut corner instead of a box.
     Absolutely positioned, so it can never change --header-h: CLS stays 0.
     ------------------------------------------------------------------------ */
  .header__sub {
    --chamfer: var(--chamfer-sm);
    --sub-dur: var(--dur-fast);
    --sub-ease: var(--ease-exit);
    position: absolute;
    inset-block-start: 100%;
    inset-inline-start: calc(var(--space-sm) * -1);
    z-index: var(--z-overlay);
    flex: none;
    min-width: var(--card-min);
    padding: var(--space-2xs) var(--space-2xs) var(--space-xs);
    border: var(--border-width-hair) solid var(--border-soft);
    border-radius: var(--radius-inset);
    clip-path: var(--chamfer-clip);
    background: var(--chamfer-edge), var(--surface-raised);
    filter: var(--shadow-chamfer);
    opacity: 0;
    visibility: hidden;
    transform: translate3d(0, calc(var(--space-2xs) * -1), 0);
    transition: opacity var(--sub-dur) var(--sub-ease), transform var(--sub-dur) var(--sub-ease), visibility var(--sub-dur) var(--sub-ease);
  }

  /* THE NO-SCRIPT PATH, and the one that runs before nav.js does. Entrance is
     slower than exit; the two curves ride in on --sub-dur/--sub-ease so the
     reduced-motion reset below only has to unset one `transition`. */
  .header__item:hover .header__sub,
  .header__item:focus-within .header__sub {
    --sub-dur: var(--dur-base);
    --sub-ease: var(--ease-entrance);
    opacity: 1;
    visibility: visible;
    transform: translate3d(0, 0, 0);
  }
  .header__item:hover .header__disc,
  .header__item:focus-within .header__disc { color: var(--text-accent); transform: rotate(45deg); }

  /* ONCE nav.js IS LIVE, data-open is the only truth — it is the sole way to
     close a panel whose disclosure button still holds focus. These tie with
     the pair above on specificity and win on source order, which is why they
     are last and must stay last. No attribute at all means "CSS decides", so
     a blocked script leaves the hover/focus menu above intact. */
  .header__item[data-open="false"] .header__sub {
    opacity: 0;
    visibility: hidden;
    transform: translate3d(0, calc(var(--space-2xs) * -1), 0);
  }
  .header__item[data-open="false"] .header__disc { color: var(--text-muted); transform: none; }
  .header__item[data-open="true"] .header__sub {
    --sub-dur: var(--dur-base);
    --sub-ease: var(--ease-entrance);
    opacity: 1;
    visibility: visible;
    transform: translate3d(0, 0, 0);
  }
  .header__item[data-open="true"] .header__disc { color: var(--text-accent); transform: rotate(45deg); }
}

@media (min-width: 1280px) {
  .header__list { gap: var(--space-lg); }
}

/* the panel appears and disappears; it does not travel */
@media (prefers-reduced-motion: reduce) {
  .header__sub { transition: none; }
  .header__disc { transition: none; }
}

/* The end padding reserves the call bar's own height so the footer is never
   under it. The bar now also carries the home-indicator inset, so the reserve
   has to carry it too or the last footer row hides behind the taller bar. */
body { padding-block-start: var(--header-h); padding-block-end: var(--control-h-lg); }
body { padding-block-end: calc(var(--control-h-lg) + env(safe-area-inset-bottom, 0px)); }
@media (min-width: 1024px) { body { padding-block-end: 0; } }

/* ==========================================================================
   01 HERO — full-bleed photograph, centred type, static instrument readout.
   Above the fold: loads render-blocking. 1 image (the LCP element).
   ========================================================================== */

.hero {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  /* svh, with a vh line under it. Safari before 15.4 does not know the unit
     and an unknown unit invalidates the WHOLE declaration, so a lone svh line
     ships no min-height at all here — and, forty lines down, no height cap on
     .hero__media, which is the rule that keeps the photograph an LCP
     candidate. Old engines take the vh line, new ones overwrite it; the
     difference between the two units is the collapsing mobile toolbar, which
     is precisely the case those engines predate. */
  min-height: 88vh;
  min-height: 88svh;
  margin-block-start: calc(var(--header-h) * -1);
  padding-block: calc(var(--header-h) + var(--space-3xl)) var(--space-2xl);
  overflow: hidden;
}

/* LCP. Chrome drops an <img> from LCP candidacy once its box is as large as the
   viewport — it reads it as a page background — and every hero on this site is
   content-driven taller than the fold at some supported height, so the LCP
   element was the sub-heading and not the photograph. The rule is area, not
   position: measured at 485px wide against a 900.4px-tall image, a 900px-tall
   viewport (436,500px2) emits no image entry and a 901px one (436,985px2) emits
   an entry of 436,694px2. Offsetting the image downward instead was tried and
   does not work — Lighthouse's trace carried no image candidate at all —
   because the box stays the same size.

   So the photograph has to be shorter than the fold, at EVERY width. The cap
   was scoped below 1024 when it was written because at 1440x900 it took 252px
   off the bottom of the untightened hero and put a seam across the readout;
   the desktop rhythm below now removes that, so the cap is unconditional.
   Measured 2026-09-06: the home hero was 1161px tall against a 900px viewport
   and timed h1.hero__title; every service/city hero measured 790-842px, which
   passes at 900 but fails a 1440x756 laptop. One rule fixes both.

   It caps the MEDIA BOX, not the img inside it: the specimen bezel and the
   spec chip belong to the photograph, so they travel with its bottom edge
   rather than framing the dark band underneath. min() means the cap is inert
   on any hero already shorter than the fold — at 1920x1080 nothing here
   applies at all. */
.hero__media {
  position: absolute;
  inset: 0 0 auto;
  height: min(100%, calc(100vh - var(--space-2xs)));
  height: min(100%, calc(100svh - var(--space-2xs)));
  z-index: var(--z-media);
  background: var(--surface-media);
}
/* the scrim already carries the tint; .media's own overlay would double it.
   The specimen bezel on ::after stays. */
.hero__media::after { background: none; }
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 58%;
}

/* The negative top margin exists to slide the hero under a FIXED header. With
   no script and below 1024 the header is a block in the flow instead (see
   shell.css), and the pull would drag the photograph over the navigation. */
@media (max-width: 1023px) {
  html:not(.js) .hero { margin-block-start: 0; padding-block-start: var(--space-2xl); }
}

.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: var(--z-scrim);
  background: var(--scrim-hero);
  pointer-events: none;
}

.hero__inner { position: relative; z-index: var(--z-content); }

.hero__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  text-align: center;
  max-width: var(--width-measure);
  margin-inline: auto;
}

.hero__title {
  font-size: var(--text-display);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  color: var(--text-on-media);
  text-wrap: balance;
}

.hero__sub {
  font-size: var(--text-lead);
  line-height: var(--leading-body);
  color: var(--text-secondary);
  max-width: var(--width-prose);
}

.hero__ctas {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-xs);
  margin-block-start: var(--space-2xs);
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-xs) var(--space-md);
  margin-block-start: var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.hero__meta li { display: flex; align-items: center; gap: var(--space-2xs); }
.hero__meta li::before {
  content: "";
  width: var(--space-3xs);
  height: var(--space-3xs);
  border-radius: var(--radius-round);
  background: var(--surface-accent);
}

/* --------------------------------------------------------------------------
   READOUT — the instrument corner. Static: the vocabulary is content, not
   choreography, so it reads identically with JS off.
   -------------------------------------------------------------------------- */
.hero__readout {
  margin-block-start: var(--space-xl);
  margin-inline: auto;
  max-width: var(--card-min-wide);
  padding: var(--card-pad);
  border: var(--border-width-hair) solid var(--border-soft);
  border-radius: var(--radius-card);
  clip-path: var(--chamfer-clip);
  background: var(--chamfer-edge), var(--surface-header);
  text-align: start;
}

.hero__readout-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-block-end: var(--space-2xs);
  border-block-end: var(--border-width-hair) solid var(--border-hairline);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-faint);
}
.hero__readout-live { color: var(--text-muted); }

.hero__readout-rows { display: grid; gap: var(--space-2xs); margin-block-start: var(--space-2xs); }
.hero__readout-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-sm);
  align-items: baseline;
}
.hero__readout-key {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-faint);
}
.hero__readout-val {
  font-family: var(--font-mono);
  font-size: var(--text-body-sm);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  text-align: end;
}
.hero__readout-val--depth { color: var(--text-accent); }
.hero__readout-note {
  margin-block-start: var(--space-2xs);
  font-size: var(--text-micro);
  line-height: var(--leading-snug);
  color: var(--text-faint);
}

@media (min-width: 1024px) {
  .hero { min-height: 94vh; min-height: 94svh; }
  .hero__inner { display: grid; grid-template-columns: 1fr auto; align-items: end; gap: var(--grid-gap); }
  .hero__body { grid-column: 1 / -1; }
  .hero__readout { margin-block-start: var(--space-2xl); margin-inline: 0 0; grid-column: 2; justify-self: end; }
}

/* --------------------------------------------------------------------------
   THE HOME HERO'S DESKTOP RHYTHM, TIGHTENED — the other half of the LCP fix.
   The cap above is what makes the photograph an LCP candidate; this is what
   keeps the readout ON the photograph while it happens.

   Measured at 1440x900 before: 1161px of hero against a 900px fold, so the cap
   cut the plate at 892 and the readout, which ends at 1098, straddled the cut
   with 206px of instrument floating on flat ground. Tightened: 929px of hero,
   the readout ends at 897, five pixels inside the cut, and the remaining band
   is 37px — read as the hero's own bottom margin rather than as a seam. At
   1920x1080 the hero is shorter than the cap and there is no band at all.

   Nothing here shrinks a type size, a measure or a control: the 232px comes
   entirely from padding, gaps and one 64px offset that only ever existed to
   push the readout down a viewport that no longer has the room. Scoped off
   .hero--svc — the service and city heroes are 700-842px, already inside the
   fold at 900, and their approved composition is not part of this problem.
   -------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  .hero:not(.hero--svc) { padding-block: calc(var(--header-h) + var(--space-lg)) var(--space-lg); }
  .hero:not(.hero--svc) .hero__body { gap: var(--space-sm); }
  .hero:not(.hero--svc) .hero__ctas { margin-block-start: 0; }
  .hero:not(.hero--svc) .hero__meta { margin-block-start: var(--space-2xs); }
  .hero:not(.hero--svc) .hero__inner { row-gap: var(--space-sm); }
  .hero:not(.hero--svc) .hero__readout { margin-block-start: 0; }
}

/* --------------------------------------------------------------------------
   SERVICE-PAGE HERO — same plate, left-aligned. Shorter than the home hero:
   a service page has an argument below the fold and must not spend a full
   viewport on the title. The h1 stays at --text-display; dropping to --text-h2
   to fit a longer sentence would fail the 6:1 h1:body contrast rule.
   -------------------------------------------------------------------------- */
.hero--svc { min-height: 62vh; min-height: 62svh; }
.hero--svc .hero__body { align-items: flex-start; text-align: start; margin-inline: 0; max-width: none; }
.hero--svc .hero__ctas,
.hero--svc .hero__meta { justify-content: flex-start; }
.hero--svc .hero__sub { max-width: var(--width-measure); }
.hero--svc .hero__media img { object-position: center 45%; }

/* The service heroes are brighter under the title band than hero-bay is, and
   .hero__media::after is set to `background: none` above so the home scrim is
   not doubled. Measured per CLAUDE.md "Measuring contrast over photography":
   without this layer the h1 over svc-hero-ceramic sampled 7.47% of background
   pixels below 4.5:1 against an 11.96:1 mean, which fails the 0.5% floor. The
   remedy the spec names is an overlay, never a text colour change. */
.hero--svc .hero__media::after { background: var(--overlay-media-heavy); }

@media (min-width: 1024px) { .hero--svc { min-height: 68vh; min-height: 68svh; } }
