/* ==========================================================================
   Cliff Hwang — Design Tokens  v0.5

   v0.1 recorded the Framer site exactly as measured.
   v0.2 consolidates it:
     · every spacing and radius value is now a multiple of 4
     · colors are a structured ramp instead of a flat list
     · near-duplicate values are collapsed
   v0.5 expresses every length in REM against a 16px root (set on <html> in
     base.css, font-size:100%). The px scale is unchanged — 1rem = 16px, so
     24px is now 1.5rem and every value still lands on the 4px grid exactly
     (any integer px ÷ 16 is an exact terminating decimal). The payoff: the
     whole system now scales with a reader's browser font-size preference.
     Viewport breakpoints in the media queries below stay in px on purpose —
     they gate on device width, not text size. Colors, blur radii, the pill
     sentinel (9999px) and the Framer glass-bevel offsets also stay as-is.

   NAMING: spacing uses index naming — --ch-space-N is N x 4px (= N x 0.25rem).
   space-6 = 1.5rem = 24px. The name itself encodes the 4px rule, and it keeps the
   existing pages working. Radius keeps its semantic names for the same
   reason. Removed steps and renamed colors have deprecated aliases at the
   bottom of this file, so nothing breaks mid-migration.

   Values that changed from the measurement are marked  [was: N].
   Full drift table in README.md.
   ========================================================================== */

:root {

  /* =======================================================================
     1. COLOR — NEUTRAL RAMP
     11 steps. The four measured anchors are preserved exactly; the rest
     fill gaps so a step exists at every level. Perceptual spacing is even
     at the light end and tightens in the shadows, which is what a dark UI
     needs.
     ======================================================================= */
  --ch-neutral-0:    #ffffff;  /* MEASURED — primary text                    */
  --ch-neutral-100:  #e0e0e0;  /* MEASURED — secondary text (Framer style)   */
  --ch-neutral-200:  #c4c4c4;  /* derived                                    */
  --ch-neutral-300:  #a0a0a0;  /* derived — muted text, 7.1:1 on bg          */
  --ch-neutral-400:  #7a7a7a;  /* derived — disabled, 4.4:1 (large only)     */
  --ch-neutral-500:  #575757;  /* derived — decorative only, 2.6:1           */
  --ch-neutral-600:  #3b3b3b;  /* MEASURED — chip background                 */
  --ch-neutral-700:  #262626;  /* derived                                    */
  --ch-neutral-800:  #1c1c1c;  /* MEASURED — raised surface                  */
  --ch-neutral-900:  #121212;  /* MEASURED — page background (Framer style)  */
  --ch-neutral-950:  #0a0a0a;  /* derived — deepest surface                  */

  /* #d7d7d7 was a third light grey, 3.5% from #e0e0e0 — imperceptible drift.
     Collapsed into neutral-100. #171717 appeared on one marquee gradient.
     Collapsed into neutral-800. */

  /* =======================================================================
     2. COLOR — ACCENT RAMP
     The site had one flat accent with no states. hover lifts toward white,
     which is the correct direction on a dark UI.
     ======================================================================= */
  --ch-amber-300:    #ffd966;  /* derived — subtle emphasis            */
  --ch-amber-400:    #ffcc33;  /* derived — hover                      */
  --ch-amber-500:    #ffbf00;  /* MEASURED — base, 11.4:1 on page bg   */
  --ch-amber-600:    #d9a300;  /* derived — pressed                    */
  --ch-amber-700:    #a67d00;  /* derived — for use on light surfaces  */

  --ch-amber-a10:    rgba(255, 191, 0, 0.10);  /* derived — tinted surface */
  --ch-amber-a20:    rgba(255, 191, 0, 0.20);  /* derived — accent border  */

  /* GREEN — the /tdo case-study accent since the Sept 2026 re-publish.
     Built with the same transforms as the amber ramp above (400 = +20% toward
     white, 300 = +40%, 600 = 85% of base, 700 = 65%), so the two ramps behave
     identically: hover lifts toward white, which is the right direction on a
     dark UI.

     Contrast on the page background (#121212):
       300  9.44:1     400  7.43:1     500  5.95:1     600  4.45:1
     500 is the measured value and clears AA for body text; 400 is the only
     step that also clears AAA, which is why hover goes up rather than down.
     700 is for light surfaces (6.51:1 on white), matching amber-700's role. */
  --ch-green-300:    #7bcb67;  /* derived — subtle emphasis            */
  --ch-green-400:    #4fb935;  /* derived — hover                      */
  --ch-green-500:    #23a802;  /* MEASURED — base, 5.95:1 on page bg   */
  --ch-green-600:    #1e8f02;  /* derived — pressed                    */
  --ch-green-700:    #176d01;  /* derived — for use on light surfaces  */

  --ch-green-a10:    rgba(35, 168, 2, 0.10);   /* derived — tinted surface */
  --ch-green-a20:    rgba(35, 168, 2, 0.20);   /* derived — accent border  */

  /* BLUE — the /dls case-study accent since the Sept 2026 re-publish, which
     replaced the earlier #7ebf71 green. Same transforms as the amber and
     green ramps (400 = +20% toward white, 300 = +40%, 600 = 85% of base,
     700 = 65%), so all three behave identically under hover and press.

     Contrast on the page background (#121212):
       300  9.96:1     400  7.74:1     500  5.97:1     600  4.46:1
     500 is the measured value and clears AA for body text; 400 is the first
     step that also clears AAA, so hover goes up. 700 is for light surfaces
     (6.47:1 on white), matching amber-700 and green-700's role. The ramp
     tracks green's almost exactly — 5.97 vs 5.95 at the base — so swapping a
     study's accent never changes which steps are legal for which role. */
  --ch-blue-300:     #aabfd9;  /* derived — subtle emphasis            */
  --ch-blue-400:     #8da9cc;  /* derived — hover                      */
  --ch-blue-500:     #7194bf;  /* MEASURED — base, 5.97:1 on page bg   */
  --ch-blue-600:     #607ea2;  /* derived — pressed                    */
  --ch-blue-700:     #49607c;  /* derived — for use on light surfaces  */

  --ch-blue-a10:     rgba(113, 148, 191, 0.10);  /* derived — tinted surface */
  --ch-blue-a20:     rgba(113, 148, 191, 0.20);  /* derived — accent border  */

  /* CLAY — the /snap case-study accent, rgb(217,93,93). Re-checked against the
     live site on 2026-09-09: /snap did NOT change in the Sept re-publish that
     moved /tdo to green and /dls to blue. All six eyebrow runs still compute
     to rgb(217,93,93), and both page images are byte-identical to the copies
     in assets/.

     NAMED "clay", NOT "red", on purpose. --ch-red-* is already taken by the
     status palette in section 2b (Tailwind's #ef4444 danger scale). A brand
     accent and a danger signal are different jobs and must not share a name —
     the first draft of this ramp did call itself --ch-red-* and was silently
     shadowed by the danger scale further down the file, which rendered the
     eyebrows in Tailwind red. Clay is the hue's own name and collides with
     nothing.

     Same transforms as the amber, green and blue ramps (400 = +20% toward
     white, 300 = +40%, 600 = 85% of base, 700 = 65%).

     Contrast on the page background (#121212):
       300  8.76:1     400  6.63:1     500  5.06:1     600  3.81:1
     500 is the measured value and clears AA for body text. NOTE this ramp
     sits a little darker than green and blue: 400 clears AA but NOT AAA
     (6.63 vs green's 7.43), so 300 is the first AAA step — hover still goes
     up, just one step further than the other two studies. 600 is 3.81:1 and
     is a pressed/decorative step only, never body text. 700 is for light
     surfaces (7.38:1 on white), matching the other ramps' 700 role. */
  --ch-clay-300:     #e89e9e;  /* derived — subtle emphasis, first AAA step */
  --ch-clay-400:     #e17d7d;  /* derived — hover                      */
  --ch-clay-500:     #d95d5d;  /* MEASURED — base, 5.06:1 on page bg   */
  --ch-clay-600:     #b84f4f;  /* derived — pressed, 3.81:1 non-text   */
  --ch-clay-700:     #8d3c3c;  /* derived — for use on light surfaces  */

  --ch-clay-a10:     rgba(217, 93, 93, 0.10);    /* derived — tinted surface */
  --ch-clay-a20:     rgba(217, 93, 93, 0.20);    /* derived — accent border  */

  /* =======================================================================
     3. COLOR — ALPHA
     Scrim base consolidated to pure black. The measurement used three
     different bases — rgba(2,2,2,·) twice, rgba(16,15,16,·) once — which
     is drift, not intent. At these opacities the difference is invisible.
     ======================================================================= */
  --ch-scrim-60:     rgba(0, 0, 0, 0.6);   /* [was: rgba(2,2,2,.6)]      */
  --ch-scrim-70:     rgba(0, 0, 0, 0.7);   /* [was: rgba(16,15,16,.7)]   */
  --ch-scrim-80:     rgba(0, 0, 0, 0.8);   /* [was: rgba(2,2,2,.8)] nav  */

  --ch-white-a03:    rgba(255, 255, 255, 0.03);  /* MEASURED — glass fill */
  --ch-white-a08:    rgba(255, 255, 255, 0.08);  /* derived — hairline    */
  --ch-white-a16:    rgba(255, 255, 255, 0.16);  /* derived — strong rule */

  /* =======================================================================
     4. COLOR — STATUS PRIMITIVES  (Tailwind v3.4 palette)
     Replaces the five Framer "decorative" pairs, which were removed: they
     were unused on the site and every one of them failed text contrast
     (2.0–2.5:1 against its own partner).

     Hues chosen so nothing collides with the brand accent. Warning uses
     ORANGE rather than Tailwind amber, because Tailwind amber-500 (#f59e0b)
     is within a few degrees of the brand accent #ffbf00 — a warning must
     not read as "brand".

     Only the four steps each role needs are pulled in.
     ======================================================================= */

  /* emerald — success */
  --ch-emerald-300:  #6ee7b7;
  --ch-emerald-400:  #34d399;   /* 9.8:1 on page bg */
  --ch-emerald-500:  #10b981;   /* 7.4:1 on page bg */
  --ch-emerald-950:  #022c22;

  /* orange — warning */
  --ch-orange-300:   #fdba74;
  --ch-orange-400:   #fb923c;   /* 8.3:1 on page bg */
  --ch-orange-500:   #f97316;   /* 6.7:1 on page bg */
  --ch-orange-950:   #431407;

  /* red — danger */
  --ch-red-300:      #fca5a5;
  --ch-red-400:      #f87171;   /* 6.8:1 on page bg */
  --ch-red-500:      #ef4444;   /* 5.0:1 on page bg */
  --ch-red-950:      #450a0a;

  /* sky — info */
  --ch-sky-300:      #7dd3fc;
  --ch-sky-400:      #38bdf8;   /* 8.7:1 on page bg */
  --ch-sky-500:      #0ea5e9;
  --ch-sky-950:      #082f49;

  /* Light surfaces (Tailwind neutral) — for any section that inverts */
  --ch-neutral-50:   #fafafa;
  --ch-neutral-75:   #f5f5f5;   /* Tailwind neutral-100 */

  /* =======================================================================
     5. COLOR — SEMANTIC
     The only layer components should reference.
     ======================================================================= */

  /* Surfaces */
  --ch-bg:               var(--ch-neutral-900);
  --ch-bg-subtle:        var(--ch-neutral-800);   /* tool cards */
  --ch-bg-muted:         var(--ch-neutral-700);
  --ch-bg-strong:        var(--ch-neutral-600);   /* chips      */
  --ch-bg-glass:         var(--ch-white-a03);
  --ch-bg-nav:           var(--ch-scrim-80);
  --ch-bg-scrim:         var(--ch-scrim-60);
  --ch-bg-inverse:       var(--ch-neutral-0);

  /* Text — contrast ratios are against --ch-bg (#121212) */
  --ch-text:             var(--ch-neutral-0);     /* 18.7:1 */
  --ch-text-secondary:   var(--ch-neutral-100);   /* 14.1:1 */
  --ch-text-muted:       var(--ch-neutral-300);   /*  7.1:1 */
  --ch-text-disabled:    var(--ch-neutral-400);   /*  4.4:1 — large text only */
  --ch-text-accent:      var(--ch-amber-500);     /* 11.4:1 */
  --ch-text-on-light:    var(--ch-neutral-900);   /* copy on any light surface */

  /* Borders */
  --ch-border:           var(--ch-white-a08);
  --ch-border-strong:    var(--ch-white-a16);
  --ch-border-accent:    var(--ch-amber-a20);

  /* Interactive */
  --ch-accent:           var(--ch-amber-500);
  --ch-accent-hover:     var(--ch-amber-400);
  --ch-accent-pressed:   var(--ch-amber-600);
  --ch-accent-subtle:    var(--ch-amber-a10);
  --ch-link-hover:       var(--ch-amber-400);   /* [was: flat #ffbf00] */
  --ch-focus-ring:       var(--ch-amber-500);

  /* Light surface — for sections that invert out of the dark theme */
  --ch-surface-light:      var(--ch-neutral-50);
  --ch-surface-light-alt:  var(--ch-neutral-75);

  /* Status — same four-part pattern for every role, so they are
     interchangeable in markup:
       -text     400 step · for text and icons on the dark background
       -solid    500 step · filled badges and bars; pair with -on-solid text
       -surface  950 step · tinted background block
       -border   500 step at 32% · outlines on tinted blocks           */

  --ch-success-text:     var(--ch-emerald-400);          /* 9.8:1 */
  --ch-success-solid:    var(--ch-emerald-500);
  --ch-success-surface:  var(--ch-emerald-950);
  --ch-success-border:   rgba(16, 185, 129, 0.32);

  --ch-warning-text:     var(--ch-orange-400);           /* 8.3:1 */
  --ch-warning-solid:    var(--ch-orange-500);
  --ch-warning-surface:  var(--ch-orange-950);
  --ch-warning-border:   rgba(249, 115, 22, 0.32);

  --ch-danger-text:      var(--ch-red-400);              /* 6.8:1 */
  --ch-danger-solid:     var(--ch-red-500);
  --ch-danger-surface:   var(--ch-red-950);
  --ch-danger-border:    rgba(239, 68, 68, 0.32);

  --ch-info-text:        var(--ch-sky-400);              /* 8.7:1 */
  --ch-info-solid:       var(--ch-sky-500);
  --ch-info-surface:     var(--ch-sky-950);
  --ch-info-border:      rgba(14, 165, 233, 0.32);

  /* Text to sit on any -solid fill. All four 500 steps are mid-tone, so a
     near-black reads better than white on every one of them. */
  --ch-text-on-solid:    var(--ch-neutral-950);

  /* =======================================================================
     6. TYPEFACES — v0.4, one family

     Explicit direction: every font on the site is now Noto Sans, Google
     Fonts. This replaces Inter (text/UI) and Newsreader (display/editorial)
     both — a deliberate design decision, not a measurement, so it isn't
     flagged as drift the way the rest of this file's [was: ...] notes are.

     --ch-font-display and --ch-font-body are kept as separate tokens (and
     the >= 32px / <= 24px boundary they used to split on still exists in
     principle) so weight/size roles remain easy to retarget later, but both
     resolve to the same family now. --ch-font-serif — the oversized italic
     editorial accent Lora/Newsreader used to carry — uses Noto Sans italic.
     ======================================================================= */
  --ch-font-display:       "Noto Sans", sans-serif;   /* [was: Newsreader, before that Montserrat] */
  --ch-font-body:          "Noto Sans", sans-serif;   /* [was: Inter] */
  --ch-font-serif:         var(--ch-font-display);    /* [was: Newsreader italic, before that Lora] */

  /* Two weights only (per direction): 400 and 700. The scale keeps its four
     role-names for stability, but medium / semibold / bold all resolve to the
     single 700 emphasis tier — so every role renders as either 400 or 700.
     (The emphasis tier was 600 in v0.6; raised to 700.) */
  --ch-weight-regular:     400;
  --ch-weight-medium:      700;   /* [was: 500, then 600] emphasis tier */
  --ch-weight-semibold:    700;
  --ch-weight-bold:        700;
  /* deprecated — resolve to a loaded weight so nothing renders synthesised */
  --ch-weight-extralight:  400;
  --ch-weight-black:       700;

  /* =======================================================================
     7. TYPE SCALE — v0.3, rebuilt

     Two things changed. Both were flagged as open questions in v0.2.

     1. THE HIERARCHY WAS INVERTED. Measured, the hero was 28.8px and the
        section label was 64px — the largest type on the page was "Spotlight",
        not the headline. The ladder now descends properly:

          hero 72  >  section 56  >  project-title 40  >  subhead 24  >  body 16

     2. LINE HEIGHT IS NOW `normal` (v0.6). Earlier versions set a fixed rem
        line height per role so every line box landed on the 4px grid. Per
        explicit direction that was dropped: every --ch-*-lh token — and every
        line-height declaration in base/components/site — is now `normal`, so
        each role uses the font's own natural leading. The 4px line-box grid no
        longer holds; only the font SIZES remain on the rem/4px scale. Sizes
        keep their own responsive steps; leading follows the size automatically.

     Token NAMES are unchanged so nothing downstream breaks; only the values
     and the family each role resolves to have moved.

     display-1 (--ch-hero-*) was also retuned in v0.6 to match the home hero
     it feeds — 2.5rem / 2rem / 1.5rem (40/32/24), no tracking — rather than the former
     72px, which no live page used at full size.
     ======================================================================= */

  /* Line heights are `normal` (v0.6) — the font's natural leading. The
     --ch-*-lh tokens are kept (name-stable for any consumer) but all resolve
     to `normal`. The former fixed-rem leading, which kept every line box a
     multiple of 4, is documented in git history. */

  /* -- Display roles ------------------------------------------------ */
  --ch-hero-size:          2.5rem;    --ch-hero-lh:        normal;  /* [was: 28.8/1.3, then 2rem]  */
  --ch-hero-ls:            normal;                             /* [was: -0.04em]   */

  --ch-section-size:       2rem;    --ch-section-lh:     normal;  /* [was: 64/1.125, then 2.5rem] — matches display-3 */
  --ch-section-ls:         -0.015em;

  --ch-project-title-size:    2rem;    --ch-project-title-lh:  normal;  /* [was: 40/45.7144] */
  --ch-project-title-ls:      -0.01em;

  --ch-wordmark-size:      1.5rem;    --ch-wordmark-lh:    normal;  /* [was: 24/28.8]   */
  --ch-wordmark-ls:        -0.01em;                             /* [was: 0]         */


  /* -- Text & UI roles ------------------------------------------------- */
  /* body-lg — one role for large body text. v0.3 merged hero-sub (the lead
     paragraph) and card-body into this: both computed Inter 20/32 desktop and
     18/28 mobile, differing only by hero-sub's -0.01em tracking, which is kept.
     Neither old name described the merged role, so both are aliases below. */
  --ch-body-lg-size:       1.25rem;    --ch-body-lg-lh:     normal;
  --ch-body-lg-ls:         -0.01em;

  --ch-subhead-size:       1.5rem;    --ch-subhead-lh:     normal;  /* [was: 24/34]     */
  --ch-subhead-ls:         -0.01em;


  /* One body role. v0.2 carried body (16/24) and body-loose (16/28), which
     differed only in leading — 16 markup usages against 1, so the split was
     not earning its keep. Collapsed to 16/24. (The v0.3 --ch-body-lh-loose
     alias and the unused serif / eyebrow / caption roles were removed in
     v0.5 — see README.) */
  --ch-body-size:          1rem;    --ch-body-lh:        normal;


  --ch-nav-size:           1rem;    --ch-nav-lh:         normal;  /* unchanged        */
  --ch-nav-ls:             -0.01em;

  --ch-chip-size:          0.875rem;    --ch-chip-lh:        normal;  /* [was: 19.6]      */
  --ch-chip-ls:            -0.01em;




  /* =======================================================================
     7b. CASE STUDY — measured from /dls, /tdo, /snap  (2026-08-13)

     All three case studies share one Framer template. The only thing that
     varies per study is the accent on the eyebrow labels.

     Type here is measured, not derived. Two roles already existed in the
     system and matched exactly, so they are reused rather than duplicated:
       · hero title   -> --ch-project-title-size / -lh  (40/28px, 1.143)
       · stat number  -> --ch-section-size / -lh     (64/44/34px, 1.125)
     ======================================================================= */
  /* Per-study accent — one value per case study, no overlap. Every study now
     points at a step in its own ramp (section 2) rather than a loose hex, so
     hover/pressed states resolve consistently across all three:
       /tdo  --ch-green-500   /dls  --ch-blue-500   /snap --ch-clay-500

     History: an early note recorded /snap as #ffbf00 — wrong, that is /tdo.
     The Sept 2026 re-publish then moved /tdo amber -> green and /dls green
     -> blue. /snap was re-checked against the live site on 2026-09-09 and is
     UNCHANGED: all six eyebrow runs still compute to rgb(217,93,93), and both
     page images are byte-identical to the copies in assets/. The rendered
     value has won over the Framer style table every time (that table's green
     was #6f9954, which never matched any page); those colors are now deleted. */
  --ch-case-accent-tdo:    var(--ch-green-500);   /* MEASURED 2026-09-08 */
  --ch-case-accent-dls:    var(--ch-blue-500);    /* MEASURED 2026-09-08 */
  --ch-case-accent-snap:   var(--ch-clay-500);     /* MEASURED 2026-09-09 */
  /* Superseded bases, kept so nothing breaks mid-migration. --ch-case-accent-red
     is the pre-ramp /snap alias: same color, now sourced from the red ramp. */
  --ch-case-accent-amber:  var(--ch-amber-500);   /* pre-Sept /tdo    */
  --ch-case-accent-green:  #7ebf71;               /* pre-Sept /dls    */
  --ch-case-accent-red:    var(--ch-case-accent-snap);   /* renamed -> -snap */
  --ch-case-accent:        var(--ch-case-accent-amber);  /* per-page default */

  /* Container. Narrower than --ch-content-max: case studies are a reading
     column, not the home page's full-bleed grid. */
  --ch-case-max:           57.875rem;
  --ch-case-pad:           0;     /* hero + cover well  */
  --ch-case-pad-text:      2rem;    /* prose column  [was: 30] */

  /* Section rhythm. These are off the --ch-space-N ladder on purpose —
     they are page-composition offsets (the 168px clears the fixed nav),
     not reusable spacing steps, so they don't belong in the scale.

     --ch-case-intro-top is a FLOOR, not breathing room: the nav is
     position: fixed, so the hero title lands under it unless this clears
     nav inset + pill height. Derivation at each breakpoint:
       desktop  24 + 72 + 72 = 168   (generous hero air)
       tablet   24 + 72 + 24 = 120   (the home hero's own floor)
       mobile   16 + 72 + 24 = 112
     Same shape as .ch-home-hero's padding-block-start calc in site.css. */
  --ch-case-intro-top:     10.5rem;
  --ch-case-intro-bottom:  2rem;
  --ch-case-intro-gap:     2.5rem;
  --ch-case-body-top:      5rem;
  --ch-case-body-bottom:   5rem;
  --ch-case-body-gap:      8rem;   /* == --ch-space-32 */

  /* Type */
  --ch-case-block-size:    2rem;   --ch-case-block-lh:   normal;
  --ch-case-sub-size:      1.25rem;   --ch-case-sub-lh:     normal;
  --ch-case-meta-size:     0.75rem;   --ch-case-meta-lh:    normal;
  --ch-case-metaval-size:  0.875rem;   --ch-case-metaval-lh: normal;

  /* Paragraph rhythm inside the prose column: every block after the first
     gets this as margin-block-start. */
  --ch-case-prose-gap:     var(--ch-space-5);   /* 20px */

  /* =======================================================================
     8. SPACING — 4px grid,  --ch-space-N == N x 0.25rem == N x 4px
     16 steps. Every value is a multiple of 4 by construction (0.25rem = 4px).
     ======================================================================= */
  --ch-space-1:    0.25rem;
  --ch-space-2:    0.5rem;
  --ch-space-3:    0.75rem;   /* [was also: 10px] the site's most common gap, 38x */
  --ch-space-4:    1rem;   /* [was also: 14px] chip inline padding             */
  --ch-space-5:    1.25rem;
  --ch-space-6:    1.5rem;
  --ch-space-8:    2rem;
  --ch-space-10:   2.5rem;   /* [was also: 36px]  */
  --ch-space-12:   3rem;
  --ch-space-16:   4rem;   /* [was also: 60px] footer top */
  --ch-space-20:   5rem;   /* [was also: 84px]  */
  --ch-space-24:   6rem;   /* [was: 100px] hero stack gap */
  --ch-space-30:   7.5rem;
  --ch-space-32:   8rem;
  --ch-space-40:   10rem;  /* [was also: 150px] footer bottom */
  --ch-space-60:   15rem;  /* [was: 246px] hero top */

  /* Dropped: 5px and 6px gaps → space-1 and space-2 */

  /* =======================================================================
     9. RADIUS — 4px grid, semantic names retained
     ======================================================================= */
  --ch-radius-sm:    0.5rem;     /* chips                                    */
  --ch-radius-md:    0.75rem;    /* [was: 10px]                              */
  --ch-radius-lg:    1rem;    /* tool cards                               */
  --ch-radius-xl:    1.5rem;
  --ch-radius-2xl:   2rem;    /* glass cards, covers  [was also: 30px]    */
  --ch-radius-3xl:   3rem;    /* nav wrapper          [was also: 50px]    */
  --ch-radius-full:  9999px;  /* nav pill, Resume button                  */
  --ch-radius-xs:    0.25rem;     /* focus ring rounding                      */

  /* The Resume button measured radius 36 on a 36px-tall element — already a
     pill, so it now uses radius-full. The odd 36 and 60 steps are gone. */

  /* =======================================================================
     10. LAYOUT
     ======================================================================= */
  --ch-page-max:      90rem;
  --ch-content-max:   90rem;   /* [was: 1280px] — desktop container widened per direction */
  --ch-gutter:        2rem;
  --ch-nav-height:    4.5rem;   /* [was: 70px] */
  --ch-chip-height:   2.75rem;   /* [was: 40px] — follows from 12px padding,
                                 and clears the 44px touch target */
  --ch-grid-gap:      3rem;
  --ch-stack-gap:     1.5rem;

  /* Control heights — buttons and chips. shadcn's ladder is h-8/h-9/h-10
     (32/36/40); this uses 32/40/48 so every step stays on the 4px grid AND
     the default clears the 44px touch target once you count the focus ring.
     --ch-chip-height (2.75rem) is the measured tag size and stays as it is. */
  --ch-control-h-sm:  2rem;
  --ch-control-h:     2.5rem;
  --ch-control-h-lg:  3rem;
  --ch-chip-h-sm:     1.75rem;

  /* =======================================================================
     11. EFFECTS
     Glass bevel offsets are Framer's own fractional values, kept verbatim —
     a rendering artifact, not a spacing decision, so the 4px rule does not
     apply.
     ======================================================================= */
  --ch-glass-edge-light:
    rgba(255, 255, 255, 0.18) 0px 0.602187px 0.602187px -1.25px inset,
    rgba(255, 255, 255, 0.16) 0px 2.28853px 2.28853px -2.5px inset,
    rgba(255, 255, 255, 0.06) 0px 10px 10px -3.75px inset;

  --ch-glass-edge-dark:
    rgba(0, 0, 0, 0.18) 0px 0.602187px 0.602187px -1.25px inset,
    rgba(0, 0, 0, 0.16) 0px 2.28853px 2.28853px -2.5px inset,
    rgba(0, 0, 0, 0.06) 0px 10px 10px -3.75px inset;

  --ch-glass-sheen: linear-gradient(
    315deg,
    rgba(255, 255, 255, 0.7) 0%,
    rgba(120, 120, 120, 0) 30%,
    rgba(120, 120, 120, 0) 70%,
    rgba(255, 255, 255, 0.7) 100%
  );

  --ch-blur-nav:    blur(5px);
  --ch-blur-glass:  blur(12px) saturate(140%);  /* APPROXIMATION — see README */

  /* =======================================================================
     12. MOTION — house convention, not measured
     ======================================================================= */
  --ch-duration-fast:  150ms;   /* interaction — hover / focus                 */
  --ch-duration-base:  250ms;
  --ch-duration-slow:  500ms;   /* entrance — the scroll-reveal duration  [was: 900ms] */
  --ch-stagger:        80ms;    /* delay step between staggered reveals          */
  --ch-ease:           cubic-bezier(0.4, 0, 0.2, 1);   /* interaction            */
  --ch-ease-out:       cubic-bezier(0.16, 1, 0.3, 1);  /* expo out — entrances   */
  --ch-ease-inout:     cubic-bezier(0.45, 0, 0.55, 1); /* ambient loops          */

  --ch-z-nav:          3;

  /* =======================================================================
     13. DEPRECATED ALIASES
     v0.1 names kept alive so existing pages keep rendering. Each points at
     its v0.2 replacement. Migrate at your convenience, then delete this
     block — nothing in the system itself refers to these.
     ======================================================================= */

  /* colors */
  --ch-white:            var(--ch-neutral-0);
  --ch-black:            #000000;
  --ch-ink-900:          var(--ch-neutral-900);
  --ch-ink-850:          var(--ch-neutral-800);
  --ch-ink-800:          var(--ch-neutral-800);   /* #171717 absorbed */
  --ch-ink-700:          var(--ch-neutral-600);
  --ch-grey-100:         var(--ch-neutral-100);
  --ch-grey-200:         var(--ch-neutral-100);   /* #d7d7d7 absorbed */
  --ch-bg-raised:        var(--ch-bg-subtle);
  --ch-bg-chip:          var(--ch-bg-strong);
  --ch-text-tertiary:    var(--ch-text-secondary);
  --ch-alpha-white-03:   var(--ch-white-a03);
  --ch-alpha-black-80:   var(--ch-scrim-80);
  --ch-alpha-black-60:   var(--ch-scrim-60);
  --ch-alpha-ink-70:     var(--ch-scrim-70);

  /* The v0.1 --ch-pair-* aliases are GONE, not re-pointed. They referenced
     the five Framer decorative pairs, which have been deleted outright:
     unused on every page, and each one failed text contrast against its own
     partner (2.0–2.5:1). Nothing referenced them. Use the status tokens in
     section 5, or --ch-case-accent-* for per-study accents. */

  /* spacing steps removed by the 4px consolidation */
  --ch-space-2-5:        var(--ch-space-3);    /* 10px → 12px  */
  --ch-space-9:          var(--ch-space-10);   /* 36px → 40px  */
  --ch-space-21:         var(--ch-space-20);   /* 84px → 80px  */
  --ch-space-25:         var(--ch-space-24);   /* 100px → 96px */
  --ch-space-37:         var(--ch-space-40);   /* 150px → 160px */
  --ch-space-61:         var(--ch-space-60);   /* 246px → 240px */

  /* radius */
  --ch-radius-btn:       var(--ch-radius-full);


}

/* ==========================================================================
   BREAKPOINTS — ≤767 · 768–1023 · ≥1024   (mobile · tablet · desktop)
   ========================================================================== */

@media (max-width: 1023.98px) {
  :root {
    /* type — every line box stays a multiple of 4 at this breakpoint too */
    --ch-hero-size:       2rem;   --ch-hero-lh:        normal;
    --ch-section-size:    1.75rem;   --ch-section-lh:     normal;
    --ch-project-title-size: 1.75rem;   --ch-project-title-lh:  normal;
    --ch-subhead-size:    1.25rem;   --ch-subhead-lh:     normal;
    --ch-gutter:         1.5rem;

    /* case study */
    --ch-case-pad:           1.5rem;
    --ch-case-pad-text:      1.5rem;   /* CORRECTED from 32. At 1024 the live
                                        prose column measures 961 of a 1009
                                        viewport — a 24px inset, the same as
                                        --ch-case-pad. The 2rem inset is a
                                        desktop-only value. */
    --ch-case-block-size:    1.75rem;
    --ch-case-intro-top:     7.5rem;   /* CORRECTED from 6.75rem (108px): that
                                        left 12px under a 96px-bottom glass
                                        pill and the title read as touching
                                        it. 24 + 72 + 24 = 120. */
    --ch-case-intro-bottom:  0;
    --ch-case-body-top:      3.75rem;
  }
}

@media (max-width: 767.98px) {
  :root {
    /* type — 34px was the one off-grid size left at this breakpoint */
    --ch-hero-size:        1.5rem;   --ch-hero-lh:        normal;   /* display-1 mobile = 24px */
    --ch-section-size:     1.5rem;   --ch-section-lh:     normal;   /* [was: 34] */
    --ch-project-title-size:  1.5rem;   --ch-project-title-lh:  normal;
    --ch-body-lg-size:     1.125rem;   --ch-body-lg-lh:     normal;
    --ch-subhead-size:     1.125rem;   --ch-subhead-lh:     normal;
    --ch-gutter:           1rem;
    --ch-grid-gap:         2rem;

    /* case study */
    --ch-case-pad:           1rem;
    --ch-case-pad-text:      1rem;
    --ch-case-block-size:    1.5rem;
    --ch-case-sub-size:      1.125rem;
    --ch-case-intro-top:     7rem;     /* CORRECTED from 5rem (80px): the pill
                                        bottoms out at 88px on mobile, so the
                                        title sat 8px UNDER it. 16 + 72 + 24
                                        = 112. */
    --ch-case-intro-gap:     1.5rem;   /* CORRECTED from 32. Re-measured at 390px
                                        on /snap and /tdo — both report a 24px
                                        section gap, already a 4px step. */
    --ch-case-body-top:      2.5rem;
  }
}
