/* 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; } }

/* ==========================================================================
   13 QUOTE — split. Form on the left, NAP panel on --radius-slab on the right.
   There is no form server: the form's action is a mailto: to the studio, which
   the browser performs with JS off. With JS on, js/modules/quote.js intercepts
   the submit and answers in .quote__sent, which carries the same message on a
   link the visitor presses. 0 images.
   ========================================================================== */

.quote { background: var(--surface-page); }

.quote__inner { display: grid; gap: var(--space-stack); align-items: start; }

.quote__form { display: grid; gap: var(--space-sm); }

.field { display: grid; gap: var(--space-3xs); }
.field--wide { grid-column: 1 / -1; }

.field__label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.field__req { color: var(--text-accent); }

.field__input,
.field__select,
.field__textarea {
  --chamfer: var(--chamfer-sm);
  width: 100%;
  min-height: var(--control-h);
  padding: var(--space-2xs) var(--space-sm);
  background: var(--surface-inset);
  border: var(--border-width-hair) solid var(--border-strong);
  border-radius: var(--radius-inset);
  clip-path: var(--chamfer-clip);
  color: var(--text-primary);
  font-size: var(--text-body-sm);
  transition: border-color var(--dur-fast) var(--ease-state), background-color var(--dur-fast) var(--ease-state);
}
.field__textarea { min-height: var(--space-5xl); resize: vertical; line-height: var(--leading-body); }

/* iOS SAFARI ZOOMS THE VIEWPORT on focus for any field whose computed size is
   under 16px, and --text-body-sm is 15. On the site's only booking path that
   throws the layout sideways in the middle of typing and leaves the page
   zoomed afterwards. The other cure, maximum-scale=1 in the viewport meta,
   also kills pinch zoom for low-vision users, so it is not on the table.
   --text-body is exactly 16px, which is the threshold, and the fields are the
   only thing on the page that changes.
   Two conditions, because the two populations are not the same set: every
   phone and portrait tablet by width, and every touch screen at any width by
   pointer. Above both, the 15px calibration stands. */
@media (max-width: 1023px), (pointer: coarse) {
  .field__input,
  .field__select,
  .field__textarea { font-size: var(--text-body); }
}

/* THE SELECT IS THE ONE CONTROL EVERY ENGINE DRAWS DIFFERENTLY. Chrome puts a
   grey chevron inside the padding box, Firefox on Windows draws its own arrow
   and paints the popup from the control's own colours, and Safari and iOS
   overlay a rounded native bezel that ignores the chamfer entirely. Three
   renderings of one field. appearance: none takes the control back in all
   three; the -moz- and -webkit- lines stay because the unprefixed property is
   younger than the engines this pass is aimed at.
   The chevron is redrawn on the wrapper, not on the select: a background-image
   on the control itself is wiped by the `background` shorthand in the hover
   rule below, and this site has no gradient chevrons. Two borders on a square,
   turned 45deg — the same construction as the site's arrow marks. */
.field__select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-inline-end: calc(var(--space-sm) * 2 + var(--icon-sm));
}
.field--select { position: relative; }
.field--select::after {
  content: "";
  position: absolute;
  inset-inline-end: var(--space-sm);
  inset-block-end: calc(var(--control-h) / 2);
  width: var(--space-2xs);
  height: var(--space-2xs);
  border-inline-end: var(--border-width-medium) solid var(--text-muted);
  border-block-end: var(--border-width-medium) solid var(--text-muted);
  transform: translate3d(0, 50%, 0) rotate(45deg);
  pointer-events: none;
}
/* the popup list is UA chrome and inherits nothing, so it is inked here or it
   ships as near-black on near-black in Firefox on Windows, which paints the
   list from the control's own colours rather than from the system menu */
.field__select option { background: var(--surface-inset); color: var(--text-primary); }
.field__input:hover,
.field__select:hover,
.field__textarea:hover { border-color: var(--border-soft); background-color: var(--surface-inset-hover); }
/* chamfered, so the ring is drawn inside rather than as a clipped outline */
.field__input:focus-visible,
.field__select:focus-visible,
.field__textarea:focus-visible {
  outline: none;
  border-color: var(--border-accent);
  box-shadow: var(--focus-ring-inset);
}
.field__input::placeholder, .field__textarea::placeholder { color: var(--text-faint); }

.quote__consent {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-2xs);
  align-items: start;
  font-size: var(--text-caption);
  line-height: var(--leading-snug);
  color: var(--text-muted);
}
.quote__consent input { accent-color: var(--surface-accent); width: var(--icon-md); height: var(--icon-md); }

.quote__submit { justify-self: start; }

/* --- the confirmation panel ----------------------------------------------
   Swapped in for the form after a valid submit, and swapped back out by the
   "edit" button. Both boxes declare their own display, so both need the
   [hidden] pair: an author `display` beats the UA sheet's [hidden] rule and
   the panel would otherwise ship visible. */
.quote__form[hidden],
.quote__sent[hidden] { display: none; }

.quote__sent {
  --chamfer: var(--chamfer-md);
  --chamfer-edge-ink: var(--border-accent-soft);
  display: grid;
  justify-items: start;
  gap: var(--space-sm);
  padding: var(--card-pad-lg);
  border: var(--border-width-hair) solid var(--border-accent-soft);
  border-radius: var(--radius-card);
  clip-path: var(--chamfer-clip);
  background: var(--chamfer-edge), var(--surface-accent-wash);
}
/* chamfered: the outline would be clipped, so the ring is drawn inside */
.quote__sent:focus-visible { outline: none; box-shadow: var(--focus-ring-inset); }

.quote__sent-title { font-size: var(--text-h3); color: var(--text-primary); }
.quote__sent-body {
  max-width: var(--width-measure);
  font-size: var(--text-body-sm);
  line-height: var(--leading-body);
  color: var(--text-secondary);
}
.quote__sent-fallback {
  max-width: var(--width-measure);
  font-size: var(--text-caption);
  line-height: var(--leading-body);
  color: var(--text-muted);
}
.quote__sent-link {
  color: var(--text-accent);
  transition: color var(--dur-fast) var(--ease-state);
}
.quote__sent-link:hover { color: var(--text-accent-strong); }

/* the handoff and the way back, side by side until the column is too narrow */
.quote__sent-acts { display: flex; flex-wrap: wrap; gap: var(--space-2xs); }

/* --- the NAP panel ------------------------------------------------------- */
.quote__nap {
  --chamfer: var(--chamfer-lg);
  display: grid;
  gap: var(--space-md);
  padding: var(--card-pad-lg);
  background: var(--chamfer-edge), var(--surface-raised);
  border: var(--border-width-hair) solid var(--border-hairline);
  border-radius: var(--radius-slab);
  clip-path: var(--chamfer-clip);
}

.nap__row { display: grid; gap: var(--space-3xs); }
.nap__key {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.nap__val { font-size: var(--text-body-sm); line-height: var(--leading-body); color: var(--text-secondary); }
.nap__val--big {
  font-family: var(--font-mono);
  font-size: var(--text-h4);
  font-weight: var(--weight-bold);
  color: var(--text-primary);
  transition: color var(--dur-fast) var(--ease-state);
}
a.nap__val--big:hover { color: var(--text-accent); }

.nap__hours { display: grid; gap: var(--space-3xs); }
.nap__hours-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-sm);
  /* every horizontal rule on this site is a tick rule (docs/uniqueness.md
     move 6). This row was the last plain hairline left. */
  padding-block: var(--space-2xs);
  background: var(--tick-rule-fine);
  background-position: bottom;
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  color: var(--text-muted);
}
.nap__hours-row b { color: var(--text-secondary); font-weight: var(--weight-medium); }

.nap__certs { display: flex; flex-wrap: wrap; gap: var(--space-2xs); }
.nap__cert {
  padding: var(--space-3xs) var(--space-2xs);
  border: var(--border-width-hair) solid var(--border-accent-soft);
  border-radius: var(--radius-pill);
  background: var(--surface-accent-wash);
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-mono-label);
}

/* --- /book/: the form IS the page ----------------------------------------
   The only page on the site with no hero, so the block has to clear the fixed
   header itself, and the masthead sets at display scale rather than at h2 —
   .sec-title would put the h1:body ratio at 3.5:1 and fail the type-contrast
   law. Same reasoning specs/020 records for .hero__title. The confirmation
   plate grows with it: at card scale it read as a notice dropped into a page
   whose whole job is the action it answers. */
.quote--page { padding-block-start: calc(var(--header-h) + var(--space-xl)); }
.quote--page .sec-title {
  font-size: var(--text-display);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
}
.quote--page .quote__sent { justify-self: stretch; --chamfer: var(--chamfer-lg); }
.quote--page .quote__sent-title { font-size: var(--text-h2); }

@media (min-width: 768px) {
  .quote__form { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .quote__inner { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); column-gap: var(--space-2xl); }
}
