/* ================================================================
   DESIGN TOKENS
   Single source of truth for all design decisions.
   All components consume tokens via var() — never hardcode values.
   ================================================================ */

:root {

  /* ── Color Primitives ─────────────────────────────────────── */
  --white:          #ffffff;
  --off-white:      #f8f8f8;
  --gray-50:        #f0f0f0;
  --gray-100:       #e8e8e8;
  --gray-200:       #d6d6d6;
  --gray-300:       #b0b0b0;
  --gray-400:       #999999;
  --gray-500:       #666666;
  --gray-600:       #464646;
  --gray-900:       #2d2d2d;
  --black:          #111111;

  /* Warm paper stock — the Warm Precision surface system */
  --paper:          #f9f7f2;  /* page — warm paper */
  --paper-alt:      #f2efe8;  /* recessed panel (footer) — warmer stock */

  /* Warm ink — the type ramp. Warm Precision put warmth in the stock but
     left every text tier achromatic (R=G=B), which reads cold on warm paper.
     These are the neutral ramp rotated toward the paper's own hue and
     darkened until each tier clears WCAG AA on --paper AND --paper-alt.
     The --gray-* ramp above stays for fills, borders, and dark surfaces —
     it is not ink. Ratios below are against --paper (#f9f7f2). */
  --ink-900:        #2c2620;  /* primary   — 13.9:1 (was #2d2d2d, 13.2:1) */
  --ink-600:        #4a443e;  /*  body     —  8.4:1 (was #464646,  9.0:1) */
  --ink-500:        #5f5952;  /* secondary —  5.9:1 (was #666666,  5.3:1) */
  --ink-400:        #6e6862;  /* tertiary  —  5.1:1 (was #999999,  2.7:1 ✗) */

  --teal-hero:      #1c3a3e;
  --teal-accent:    #00aa90;
  --teal-accent-dk: #008a74;
  /* Ink-weight teal — the accent is a display colour (2.7:1 on paper) and
     cannot carry small text. This is the same hue taken down until it clears
     AA on BOTH stocks, so labels and metadata can be teal anywhere.
     Marks, the hero, and the contour field keep --teal-accent.
     4.9:1 on --paper · 4.6:1 on --paper-alt. Tune against --paper-alt, not
     --paper: the first cut of this token was #007d68, which passed on the
     page (4.7:1) and failed inside every plate and the footer (4.4:1). */
  --teal-ink:       #007a65;

  /* Mineral pigment — traditional color, ground-malachite family.
     Introduced during the preloader's art-direction exploration; now
     the Japan case study's page-local accent (JP annotation labels,
     fold-compare reverse face). Caption-level garnish only — teal
     stays primary everywhere. */
  --byakuroku:      #a8d8b9;  /* 白緑 — pale malachite (nipponcolors) */

  /* Pindo rose — the Pindo case study's page-local accent, and it is now
     **Pindo's actual brand red**, not a muted draw from it.

     IT USED TO BE #c0304f, AND THAT WAS COSTING CHROMA FOR NOTHING. The
     muted value was chosen when this was a whisper for the Chinese state
     chips, under a rule reserving the real red for product screenshots.
     Once the case page took the product's colour as its page accent, that
     rule stopped applying — and measured in OKLCH the muted value was
     **C=0.180 against the brand's C=0.204, a 13% loss of chroma buying
     0.03 of contrast**. #cb1d45 is 5.16:1 on paper and 4.81:1 on the mat:
     AA on both, and more saturated. When a "safe" variant is both duller
     AND no more legible, it is not a trade, it is just duller.

     The `.cn-label` chips still read as a whisper, because their muting
     lives in the `color-mix()` ratios (15% on the ground, 82% on the text),
     which is the right place for it — 5.19:1 on the chip and 6.62:1 on
     paper, a clear step below the accent's own 5.16 so the rail never
     outshouts the column. **The brand red cannot set the chip text
     directly** — on its own 15% tint it is 4.46:1 and fails AA. It also
     puts the page in step with the Figma specimen board, where brand-500
     has always been #CB1D45. */
  --pindo-rose:     #cb1d45;

  /* Fiserv orange — the page-local accent for the two Fiserv case studies
     (Dev Studio, Vision Next). `--fiserv-orange` is the published brand
     value: #FF6600, Pantone 1505 C.

     IT NEEDS A THREE-STEP LADDER AND ROSE DID NOT, for one measurable
     reason: at **2.74:1 on paper** the brand orange is the exact twin of
     `--teal-accent` (also 2.74:1) — a display value that fails text. Rose
     starts at 5.16:1 and serves every role from one value. So orange copies
     teal's structure rather than rose's: the true brand colour where only
     3:1 is required, darker steps where 4.5:1 is.

     DARKEN IN OKLCH, NOT BY MIXING TOWARD BLACK. The first cut of these
     steps was #c44b00 / #b24300, produced by dropping sRGB lightness — and
     in sRGB that drains chroma with it, which is why they read as brown
     rather than orange. The fix is to walk L down in OKLCH while pushing C
     back out to the gamut edge at each step, so only luminance leaves.
     `--fiserv-dk` gained 10% chroma that way (C 0.169 → 0.186) and got
     BRIGHTER, not darker, because large serif only owes 3:1.

     `--fiserv-ink` barely moved (C 0.157 → 0.160) and that is the honest
     limit, not an oversight: **orange cannot be both vivid and 4.5:1 on
     light paper.** The vivid orange lives where it is legal — fills, marks,
     and the large accent word. Both darker steps hold the hue (24° → 23°)
     so they still read as the brand.

     Checked against BOTH surfaces, because case accents land on paper and
     on the `--surface-alt` mats, and the mat is always the tighter of the
     two. `--fiserv-dk` is large serif only — never set body copy in it. */
  /* 2.74 paper / 2.56 mat — under BOTH the 4.5 text floor and the 3:1
     non-text floor. It is legal today only because no Fiserv page draws a
     meaning-carrying fill; it is for decorative marks. **Before using it on
     a bar, a dot or anything a reader has to interpret, re-check it — or
     use `--fiserv-dk` (3.44/3.21), which clears the non-text floor.** The
     same caveat applies to `--teal-accent`, its exact twin. */
  --fiserv-orange:  #ff6600;   /* decorative mark only — never text, never a data fill */
  --fiserv-dk:      #e25a02;   /* large serif — 3.44 paper / 3.21 mat */
  --fiserv-ink:     #ba4903;   /* small text  — 4.85 paper / 4.52 mat, AA both */

  /* ── Semantic Surface Tokens ──────────────────────────────── */
  --surface-page:       var(--paper);
  --surface-alt:        var(--paper-alt);
  --surface-hero:       var(--teal-hero);
  --surface-overlay:    rgba(28, 58, 62, 0.72);
  /* THE FIGURE CARD. Three stocks were tried behind a picture and the first
     two failed the same way — as a BAND framing the image:
       `--paper-tint-2` #ece6da (L* 91.5) — 5.0 L* under the board it framed
         and 5.8 under the page it sat on, so the frame was the darkest thing
         in the figure and the eye found it before the picture;
       `--ink-900` #2c2620 — decisive (80.9 L* of separation) but far too
         heavy: 7–8 near-black blocks down a page whose thesis is that it
         reads as one sheet of paper.
     The answer was not a third colour, it was removing the band: the picture
     is now a LID reaching the card's left, right and top edges, so the stock
     only ever appears UNDER the caption. That makes this a surface you read,
     not a surface that frames — which is `--surface-alt` by the two-plate
     rule. Borrowed wholesale from `.course-stepper--rail`'s card, which had
     been doing exactly this all along. */
  --surface-mount:      var(--surface-alt);

  /* ── Semantic Text Tokens ─────────────────────────────────── */
  --text-primary:       var(--ink-900);
  --text-secondary:     var(--ink-500);
  --text-tertiary:      var(--ink-400);
  --text-body:          var(--ink-600);
  --text-on-dark:       #e8f0ef;
  --text-on-dark-dim:   rgba(232, 240, 239, 0.65);
  /* Display accent — large type and marks only. Small text uses
     --text-accent-ink; large serif on paper uses --accent-dk. */
  --text-accent:        var(--teal-accent);
  --text-accent-ink:    var(--teal-ink);

  /* ── Semantic Border Tokens ───────────────────────────────── */
  --border-light:       var(--gray-100);
  --border-mid:         var(--gray-200);

  /* ── Divider Tokens ───────────────────────────────────────── */
  --divider-section:    var(--gray-900);  /* 2px — between page sections */
  --divider-content:    var(--gray-900);  /* 1px — between content rows  */

  /* ── Focus Tokens ─────────────────────────────────────────── */
  /* Keyboard focus is a designed state here, not a browser default —
     see `css/components/focus.css`. Ink rather than accent: the teal is a
     labelling colour and reads as decoration, while focus has to read as
     the system talking to you. */
  /* The value that reads AGAINST `--text-primary` when it is used as a fill
     (the pill's clip-path sweep). On paper that is white; inside the footer's
     ink block `--text-primary` is light, so this flips to the block's ground.
     Without it the pill's hover text was white-on-white in the footer. */
  --fill-contrast:      var(--white);

  --focus-ring:         var(--ink-900);
  --focus-ring-on-dark: var(--text-on-dark);
  --focus-ring-width:   2px;
  --focus-ring-offset:  3px;

  /* ── Semantic Accent Tokens ───────────────────────────────── */
  --accent:             var(--teal-accent);
  --accent-dk:          var(--teal-accent-dk);

  /* ── Navbar glass ─────────────────────────────────────────── */
  /* THE PILL IS THE ONLY TRANSLUCENT SURFACE ON THE SITE, and it is allowed
     to be one for the same reason it is allowed to carry the cursor
     spotlight: it is UI chrome floating over the document, not a sheet of
     the document. Paper is opaque. Do not take these tokens anywhere else.

     The pill was `rgba(16, 16, 16, 0.46)` — a dark slab over warm paper, and
     the one place the page went cold. It is now the page's own recessed stock
     at 76%, so what shows through reads as paper seen through paper.

     `color-mix` rather than a literal rgba so the alpha rides the real token;
     if the paper stock is ever retuned, the glass follows it.

     THERE IS NO `--nav-slot-hover`. A hovered slot was briefly `--paper` at
     94% — the page tone one step above the track's `--paper-alt`, on the
     reasoning that a chip which lights up has to sit above its track. It is
     deleted: at rest the bar is transparent over the page, so a paper chip
     was paper on paper and the hover simply did not exist until you scrolled.
     The slot now fills with `--text-primary` and flips its label to
     `--fill-contrast`, which is `.btn-pill`'s pairing exactly — one fill, one
     ink, every surface on the site. */
  --nav-glass:          color-mix(in srgb, var(--paper-alt) 76%, transparent);
  --nav-glass-edge:     color-mix(in srgb, var(--ink-900) 12%, transparent);
  /* The travelling edge-light. On a dark pill this was white; on warm glass a
     highlight is invisible (the stock is already near-white), so the mark
     that moves is INK — the same inversion the crease uses, where the fold is
     a dark valley and the catch-light rides above it. */
  --nav-edge-mark:      color-mix(in srgb, var(--ink-900) 46%, transparent);
  --nav-edge-mark-soft: color-mix(in srgb, var(--ink-900) 6%, transparent);

  /* ── Typography / Font stacks ─────────────────────────────── */
  --font-display:   'PP Editorial New', 'Editorial New', Georgia, 'Times New Roman', serif;
  --font-body:      'PP Neue Montreal', 'Neue Montreal', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono:      'Neue Montreal Mono', 'DM Mono', ui-monospace, monospace;
  --font-ui:        'Noto Sans JP', 'Helvetica Neue', sans-serif;

  /* ── Font system (semantic types) ─────────────────────────── */
  /* h1: 72px / 80px — PP Editorial New */
  --type-h1-size:   72px;
  --type-h1-line:   80px;
  --type-h1-font:   var(--font-display);

  /* h2: 64px / 72px — PP Editorial New */
  --type-h2-size:   64px;
  --type-h2-line:   72px;
  --type-h2-font:   var(--font-display);

  /* h3: 40px / 48px — PP Editorial New */
  --type-h3-size:   40px;
  --type-h3-line:   48px;
  --type-h3-font:   var(--font-display);

  /* h4: 24px / 32px — PP Editorial New */
  --type-h4-size:   24px;
  --type-h4-line:   32px;
  --type-h4-font:   var(--font-display);

  /* body 1: 16px / 24px — PP Neue Montreal */
  --type-body1-size: 16px;
  --type-body1-line: 24px;
  --type-body1-font: var(--font-body);

  /* body 2: 14px / 20px — PP Neue Montreal */
  --type-body2-size: 14px;
  --type-body2-line: 20px;
  --type-body2-font: var(--font-body);

  /* display: 24px / 32px — PP Neue Montreal */
  --type-display-size:  24px;
  --type-display-line:  32px;
  --type-display-font:  var(--font-body);

  /* caption: 14px / 24px — Neue Montreal Mono */
  --type-caption-size: 14px;
  --type-caption-line: 24px;
  --type-caption-font: var(--font-mono);

  /* ── Case-study ladder ────────────────────────────────────────
     Long-form case pages run a WIDER ladder than the homepage. The shared
     scale puts h1 (72) and h2 (64) only 8px apart, which reads flat over a
     long document, so the case pages step landmark → chapter → beat much
     harder. `.case-page`-scoped in case.css; the homepage is untouched.

     These were hardcoded literals inside two @media blocks (≥1025 and ≤768)
     with NOTHING defined for 769–1024 — so in that band the pages silently
     fell back to the shared scale and whole tiers merged (a beat heading and
     the next-project card both landed on 32px; the pull quote and the
     component h3s both on 24px). Tokens exist so the ladder is defined at
     every breakpoint. If you add a tier, add all three values. */
  /* THE READING MEASURE. One number for the case pages' content column —
     the `.case-grid` track, every plate that aligns to it, and the cover's
     text. It was hand-copied as `680px` in six places, which is how the
     cover drifted onto measures (14ch title, 560px thesis) that matched
     nothing else on the page. */
  --case-rail:   220px;
  --case-column: 680px;
  /* The page's true content width: rail + gap + column. Anything that wants
     to span "full width" spans THIS. It was 948 sitting inside a container
     hardcoded at 1120 (1040 inner): first the cover's full-width elements ran
     to the container edge and overhung every block below them by 92px, and
     once they were capped here instead, that same 92px became dead space on
     the right — the whole page then sat 46px left of the viewport centre
     (measured 193 left / 285 right at 1425). */
  --case-canvas: calc(var(--case-rail) + var(--sp-12) + var(--case-column));
  /* THE CONTAINER IS THE CANVAS PLUS ITS INSET — it is not a width of its
     own. `.case-cover` / `.case-section` / `.case-footer` each carried a
     literal `1120px` that predated `--case-canvas`, and nothing on the page
     ever spanned its 1040 inner. Derived, the container is centred on exactly
     what the grid fills, at every breakpoint: 1028 here, 760 at ≤1024 where
     the canvas becomes the column. Never restate a width here — change the
     rail or the column and the container follows. */
  --case-container: calc(var(--case-canvas) + var(--gutter-inner) * 2);

  --type-case-landmark:  90px;   /* cover title — the page's single peak */
  --type-case-landmark-line: 1.03;
  /* The mid-page thesis. It used to share `--type-case-landmark`, which made
     it tie the cover title: two 90px landmarks on one page, and at that size
     a six-word sentence is almost guaranteed to strand its last word on a
     line of its own. A clear step down makes the cover title unambiguously
     the peak and gives the thesis a measure it can actually break well in. */
  --type-case-thesis:    64px;
  --type-case-thesis-line: 1.08;
  --type-case-chapter:   52px;   /* the three chapter headings */
  --type-case-chapter-line: 1.06;
  --type-case-beat:      32px;   /* connective beats (brief/outcomes/…) */
  --type-case-quote:     26px;   /* pull quotes, inside the body flow */
  /* Titles INSIDE a component plate (fold-compare face, stepper panel).
     These used `--type-h4-size`, which the shared scale does not override at
     ≤1024 — so they stayed 24px while everything around them shrank, and the
     pull quote slipped below them. Part of the ladder so the order holds. */
  --type-case-component: 24px;
  --type-case-lede:      21px;   /* lede + cover thesis */
  /* Numerals are their own register — big and expressive, but deliberately
     capped BELOW `--type-case-chapter` so a statistic never outranks the
     heading of the section it sits in. */
  --type-case-numeral:   48px;

  /* Legacy type scale (aliases where needed) ──────────────────── */
  --text-2xs:   10px;
  --text-xs:    12px;
  --text-sm:    var(--type-body2-size);
  --text-base:  var(--type-body2-size);
  --text-md:    var(--type-body1-size);
  --text-lg:    20px;
  --text-xl:    var(--type-h4-size);
  --text-2xl:   var(--type-h3-size);
  --text-3xl:   var(--type-h2-size);
  --text-4xl:   var(--type-h1-size);

  /* ── Line Heights ─────────────────────────────────────────── */
  --leading-none:     1;
  --leading-tight:    1.1;
  --leading-snug:     1.2;
  --leading-normal:   1.5;
  --leading-relaxed:  1.6;

  /* ── Letter Spacing ───────────────────────────────────────── */
  --tracking-tight:   -0.02em;
  --tracking-normal:   0;
  --tracking-wide:     0.06em;
  --tracking-wider:    0.1em;
  /* The uppercase-mono LABEL register. This value was the single most
     repeated literal in the codebase (11 occurrences) with no token behind
     it, which is how the case pages drifted to six different tracking
     values — 0.02 / 0.04 / 0.06 / 0.08 / 0.1 / 0.12em — for what is
     visually one component. Every uppercase mono label uses this. */
  --tracking-label:    0.08em;
  /* Data, not a label: mono code/identifiers that must stay readable as a
     string (the Pindo address segments). Uppercase labels never use it. */
  --tracking-code:     0.02em;

  /* ── Font Weights ─────────────────────────────────────────── */
  --weight-light:    300;
  --weight-book:     350;  /* PP Neue Montreal Book — default for all body text */
  --weight-regular:  400;  /* Reserved for display headings (Editorial New) */
  --weight-medium:   500;
  --weight-bold:     700;

  /* ── Spacing Scale (multiples of 4px; smallest gutter 4px) ──── */
  --sp-1:    4px;   /* smallest */
  --sp-2:    8px;
  --sp-3:    12px;
  --sp-4:    16px;
  --sp-5:    20px;
  --sp-6:    24px;
  --sp-8:    32px;
  --sp-10:   40px;
  --sp-12:   48px;
  --sp-16:   64px;
  --sp-20:   80px;
  --sp-24:   96px;

  /* ── Border Radius (multiples of 8px where possible) ────────── */
  --radius-xs:    4px;
  --radius-sm:    8px;
  --radius-md:    8px;
  --radius-lg:    16px;
  --radius-xl:    24px;
  --radius-pill:  96px;
  --radius-full:  9999px;

  /* ── Surface radius — RESPONSIVE (stepped down in layout.css) ──
     Two roles, both Apple-squircled at point of use:
       --radius-panel → the big page surfaces: hero (header) + footer
       --radius-card  → the smaller cards: selected-work cards + image cards
     Desktop base below; tablet/mobile overrides live in layout.css. */
  --radius-panel: 48px;
  /* 24px on a 1274px-wide media plate reads inflated — radius should not
     scale with the box. The reference (burner.pro) runs 8px on a 1377px
     block; 16 keeps the squircle legible while sharpening the big surfaces.
     Small components keep their own smaller radii. */
  --radius-card:  16px;

  /* ── Layout (gutter & gap: multiples of 4px) ───────────────── */
  --page-max:     1440px;
  --content-width: min(95vw, 1440px);
  --gutter:       24px;   /* 6×4 — base horizontal inset */
  --gutter-inner: var(--sp-10);   /* section, hero, header, footer left/right — 40px */
  --gap-col:      40px;

  /* ── Shadows ──────────────────────────────────────────────── */
  --shadow-sm:    0 2px 8px rgba(0, 0, 0, 0.06);
  --shadow-md:    0 8px 24px rgba(0, 0, 0, 0.08);
  --shadow-lg:    0 20px 60px rgba(0, 0, 0, 0.12);
  --shadow-xl:    0 32px 80px rgba(0, 0, 0, 0.16);

  /* ── Paper & Fold (Warm Precision signature) ──────────────── */
  --crease-shadow:    rgba(45, 40, 30, 0.07);    /* warm fold valley   */
  --crease-highlight: rgba(255, 253, 248, 0.85); /* warm fold catchlight */
  --fold-line:        rgba(45, 40, 30, 0.10);    /* scored panel edge  */
  --paper-grain:      0.028;                      /* grain overlay opacity */

  /* ── The drawn rule (crease house-mark) ────────────────────────
     ONE silhouette for every dividing line in the system: hairline at
     both ends, swelling mid-span — a crease a hand pressed, not a line a
     tool ruled. Consumers pick a WEIGHT by setting the box height; the
     shape scales with it, so the section crease and the content rule read
     as the same hand at two pressures.
     The centerline is LEVEL (top and bottom edges mirror exactly about
     y=6). It used to waver ±1.8px, and over a 1200px run that drift read
     as a line that had been hung crooked, not as a line drawn by hand.
     The drawn quality now lives entirely in the PRESSURE profile: the
     swell peaks left of centre and is heavier at 1/4 span than at 3/4.
     Keep the mirror — don't put a wavering centerline back.
     Used as a `mask`, so the ink stays a token on the element.

     IT IS A PRE-RASTERISED PNG (300x12, alpha only), NOT AN SVG — deliberately.
     The band is thinner than a device pixel for most of its length (~0.5px at
     the ends on a 1.5x display), and a path rasteriser resolves vertical
     coverage in QUARTER-pixel steps. Because the edges here are almost
     horizontal, one quarter-step held for hundreds of columns and then jumped
     all at once: the rule stepped visibly along its length — a staircase.
     Measured on the live shape, the worst column-to-column change was 0.25 of
     a pixel's ink; it appeared on 22 of 60 width x DPR x box-height
     combinations, and never at 1x (which is why it was easy to miss). Denser
     path geometry does not fix it — the quantisation is in the rasteriser, not
     the curve. A raster mask is resampled by image filtering instead, which is
     continuous: worst change 0.03, zero bad combinations across the same 60.
     The silhouette is geometrically identical — each pixel holds the exact
     analytic coverage of the curve below, so ink range, taper and pressure are
     unchanged.
     SOURCE CURVE (regenerate from this; viewBox 0 0 1200 12):
       M0 5.65 C120 5.52 210 5.12 300 5 C420 4.88 520 4.8 600 4.8
       C700 4.8 820 4.94 900 5.05 C1010 5.24 1115 5.47 1200 5.65
       L1200 6.35 C1115 6.53 1010 6.76 900 6.95 C820 7.06 700 7.2 600 7.2
       C520 7.2 420 7.12 300 7 C210 6.88 120 6.48 0 6.35 Z
     Do not "restore" it to that SVG — the staircase comes back with it.
     See DESIGN.md → Art Language → The crease. */
  --rule-silhouette: url("data:image/png;base64,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");
  --rule-box-section: 10px;   /* section crease — 0.4px ends, swells to ~2.3px */
  --rule-box-fine:     6px;   /* content rule   — 0.25px ends, swells to ~1.25px */

  /* Warm-paper tonal ramp — folded facets (lightest → deepest shadow) */
  --paper-tint-1:   #f6f3ec;   /* fold catching the most light */
  --paper-tint-2:   #ece6da;
  --paper-tint-3:   #e7e1d4;
  --paper-tint-4:   #d8d0bf;
  --paper-tint-5:   #cdc4b2;   /* deepest fold shadow */

  /* ── Motion ───────────────────────────────────────────────── */
  --ease-out:       cubic-bezier(0.16, 1, 0.3, 1);
  /* Unused by design, kept as the documented shape of a SYMMETRIC curve.
     Nothing on this site accelerates into a stop: content arrives and
     decelerates (--ease-out, 55 of 68 uses). The two places that legitimately
     need symmetry — the curtain and the preloader fold — declare their own
     tuned curves. If you find yourself reaching for this, check first that
     the thing you are animating really does start from rest AND end at rest. */
  --ease-in-out:    cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring:    cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-editorial: cubic-bezier(0.25, 0.1, 0, 1);
  /* Curtain wipe — the preloader's fold curve. Strong in, strong out, so
     the panel launches and lands decisively instead of drifting.
     NOT for the page transition any more: a hard in-out curve is exactly
     what made the navigation read as rigid. See --ease-soft. */
  --ease-curtain:   cubic-bezier(0.76, 0, 0.24, 1);
  /* Page transition — near-linear in, long gentle out. Nothing snaps, nothing
     overshoots, nothing announces itself. Everything in the home <-> case
     navigation (the dissolve and the flight) runs on this one curve so the
     whole thing reads as a single unhurried motion. */
  --ease-soft:      cubic-bezier(0.25, 0.1, 0.35, 1);
  /* The FLIGHT curve — a real SPRING, baked into `linear()`.
     For the shared element only.

     WHY NOT A CUBIC-BEZIER. Every bezier is a single interpolation segment:
     velocity jumps from zero at the start and is cut off at the end. Curves
     tuned to feel "decisive" (a steep initial slope) are the ones that read
     as HARD — decisive is not the opposite of stiff, it IS stiff. A damped
     spring instead has continuous velocity: it eases out of rest, carries
     momentum, and settles. That settle is the whole of the soft, expensive
     quality; no bezier can express it.

     WHY NOT GSAP. GSAP can spring, but it drives motion from rAF on the main
     thread — which is exactly what was saturated (ScrollTrigger, p5, Lenis,
     a dozen modules) when the flight stuttered. `linear()` bakes the spring
     shape into a CSS timing function, so it stays on the compositor.

     Damped oscillator, zeta 0.82 / omega0 5.6, sampled at 24 points. Half
     the distance is covered by t=0.29 (quick, never abrupt) and it overshoots
     1.1% before settling — about 5px on the real card-to-cover morph, felt
     rather than seen. The reference's own springs sit at zeta 0.58–0.70, but
     they only ever move 5–35px; that damping across ~1000px would bounce
     ~30px and read cheap. Damping has to be chosen for the DISTANCE. */
  --ease-flight: linear(0.0000, 0.0240, 0.0842, 0.1666, 0.2603, 0.3575, 0.4528, 0.5424, 0.6241, 0.6968, 0.7600, 0.8139, 0.8591, 0.8962, 0.9262, 0.9500, 0.9685, 0.9825, 0.9929, 1.0002, 1.0053, 1.0085, 1.0102, 1.0110, 1.0000);

  /* Page-transition timing. Previously undefined — the durations lived only
     as CSS fallbacks plus duplicated JS constants. They are real tokens now,
     but the JS still mirrors them (a blocking head script cannot read CSS),
     so LEAVE_MS / REVEAL_MS / FLIGHT_MS in every page's director must move
     with these. */
  /* The three beats are deliberately DIFFERENT lengths and start at
     different moments. Running them together on one duration was what made
     the navigation read as flat — orchestration is what gives an arrival
     depth: the veil clears first, the image is still travelling when the
     copy starts, and the copy is still settling when the image lands. */
  --pt-leave:       0.2s;    /* content dissolves away  (JS: LEAVE_MS 200)   */
  --pt-reveal:      0.38s;   /* content dissolves in    (JS: REVEAL_MS 380)  */
  /* Longer than the bezier version was, because a spring needs room for its
     tail. The move is visually resolved well before this — the extra time is
     the settle, which is imperceptible as duration and is what reads as
     softness. Shorten it and the spring gets clipped back into a hard stop. */
  --pt-flight:      0.68s;   /* shared element travels  (JS: FLIGHT_MS 680)  */

  /* Three steps, all three in use. `--dur-instant` (80ms) and `--dur-crawl`
     (900ms) were removed: neither was referenced anywhere, and the 900ms
     crawl is the value that made the card hover feel sluggish before it was
     retuned — leaving it defined invited its return. The GSAP side has its
     own ladder (`DUR` in `js/modules/motion.js`) because its lengths are
     entrance-scale, not interaction-scale.

     CHOREOGRAPHED ONE-OFFS ARE EXEMPT, and there are exactly four: the
     navbar pill morph (0.4s width), the fold-compare turn (0.7s), the
     case-viz bar (1.2s), and the page-transition tokens (`--pt-*`). Each is
     tuned to a specific gesture and documented at its site. Everything else
     uses these three. */
  --dur-fast:     180ms;
  --dur-normal:   320ms;
  --dur-slow:     600ms;

  /* THE FILL IS THE ONE GESTURE WITH ITS OWN LENGTH, and the name is doing
     real work. 900ms was previously defined as `--dur-crawl` and deleted for
     the reason recorded above: a generic fourth step on the ladder is an
     invitation, and the last thing that reached for it was a card hover it
     made sluggish. This is not that token. It is the corner-origin `clip-path`
     fill (DESIGN.md → The fill), and only that — a big surface sweeping in,
     where the length IS the gesture rather than a delay in front of a state
     change. Do not use it for a transform, an opacity, or anything that could
     be described as "a slow version of the normal thing". */
  --dur-fill:     900ms;

  /* ── Breakpoints (reference only — use in @media) ─────────── */
  /* --bp-xl:    1440px   Desktop large */
  /* --bp-lg:    1024px   Desktop / tablet landscape */
  /* --bp-md:    768px    Tablet portrait */
  /* --bp-sm:    480px    Mobile landscape / large phones */
  /* --bp-xs:    375px    Small phones */

  /* ── Z-index Layers ───────────────────────────────────────── */
  --z-base:       0;
  --z-raised:     10;
  --z-dropdown:   20;
  --z-overlay:    50;
  --z-navbar:     90;
  --z-cursor:     999;
  /* The page-transition curtain sits above everything, including the
     preloader splash — it must be able to cover a fresh page's first paint. */
  --z-curtain:    10000;

  /* ── Touch Targets (mobile) ───────────────────────────────── */
  --touch-min:    44px;   /* WCAG minimum touch target */
  --touch-comfortable: 48px;
}
