/* ==========================================================================
   KUNDALINI SPINES — DESIGN TOKENS
   Single source of truth for color, type, spacing, motion, and breakpoints.
   Every component should reference these variables — no hardcoded values.

   REPALETTED + RETYPED AUG 13 2026 from design_handoff_full_system (the owner's
   full-system drop). The warm set (amber light, bone #D8D0BE, deep crimson) and
   the Big Shoulders / Source Serif families were replaced wholesale. VARIABLE
   NAMES are unchanged on purpose — every page re-reads them without edits —
   but the VALUES below are the cold system. The owner's call: the old palette
   fought the imagery, which is blue-white electrical discharge on near-black.
   ========================================================================== */

:root {
  /* THE SITE DECLARES ITS OWN SCHEME (Aug 23 2026, V2HANDOFF 45 open item 17,
     the light-mode half). This is NOT a light mode and it is not the start of
     one. It tells the UA that the page is already dark, which buys three things
     that were previously left to chance: the canvas is painted dark BEFORE
     first paint, so the white flash on a cold load goes; scrollbars, the text
     caret and any control this repo has not styled by hand render dark instead
     of light-on-dark; and a form control added later cannot arrive as a white
     rectangle on a black page.

     WHY THERE IS NO prefers-color-scheme BLOCK ANYWHERE, and why item 17's
     other half should stay closed. A light theme here is not a token flip. The
     background is six url() sky layers in css/star-bg.css with baked black
     points and a mix-blend-mode: screen twinkle stack, and there is no light
     version of that photograph. Every contrast figure measured in this repo,
     including the whole alpha sweep in css/purchase.css, is calibrated against
     --color-black and that one image; a light theme invalidates all of them at
     once. Honouring a preference the design cannot actually serve would be
     worse than declaring the one it does. */
  color-scheme: dark;

  /* ---- Color: core palette (cold; authored against the nebula hero) ----
     Black dominates. The neutrals are cold. Signal red is a rare interruption:
     two uses per page is the budget. The spine's light is white, not warm. */
  --color-black:       #03040F;  /* Archive Black — dominant background, blue-shifted */
  --color-black-deep:  #080B10;  /* second ground: sections that sit off the primary */
  --color-white:       #E4E8EB;  /* Spine Glow — the light the navigator emits, and high-contrast accents */
  --color-bone:        #D6D5D0;  /* Cold Bone — long-form text; the warm bone (#D8D0BE) retired */
  /* SIGNAL RED, AND THE NAME MATCHES THE VALUE AGAIN. These two hex values are
     the site's whole accent: tokens.css is loaded everywhere, so they reach
     Transmissions, About and the shutter pages, not just the home lab. The
     budget the header states — roughly two uses a page — is what keeps this an
     interruption rather than a colour scheme.

         #7E2630  h353 s54 l32   2.15:1 on #03040F   <- --color-crimson
         #A1333E  h354 s52 l42   2.97:1              <- --color-crimson-lit

     THE VIOLET EXPERIMENT, AUG 17-18 2026, REVERTED. For one day these held a
     violet pair — h272 s54 l36 and h272 s52 l46; the exact hexes are in commit
     e0fa910 and are deliberately not repeated here, so that grepping this tree
     for a violet value finds a live one or nothing. Its hue was sampled from
     the deep-field clip (h253-272, peaking at f20) with the red's saturation
     carried over, because the clip's own purple is s21 and a literal pixel
     sample was mud.
     Lightness had to RISE to hold contrast — blue-violet carries less luminance
     than red at the same HSL lightness, so l32 would have given 1.79:1 — and
     the pair was tuned to l36/l46 to land on 2.15:1 and 2.98:1, parity with the
     red figures above rather than coincidence.

     WHY IT CAME BACK. The deep-field background that shipped after it carries a
     RED GLOW in one of its scenes, so the accent belongs in that family: an
     accent that answers something already on screen reads as deliberate, and
     one that answers nothing reads as a second palette. The measurement that
     argued against the violet from the start says the same thing from the other
     side — the site's cold band runs 204-230 (V2HANDOFF 36), Signal Red sits
     ~123 degrees outside it and the violet only 42, so the violet was a
     structurally QUIETER accent, closer to being part of the sky than a signal
     against it. Do not re-run this swap on hue alone; the background is the
     thing that decides it now.

     TOKEN NAMES: --color-crimson holds a crimson today, but the names outlive
     the values by design. Renaming them touches 15 files and every one of those
     edits is a chance to miss a usage, where a value swap is two lines and
     reaches everything through the cascade — which is exactly why the violet
     day cost two lines here and not fifteen files. Same tolerance the codebase
     already extends to --spine-lit and --spine-dim, which describe the opposite
     of what they do and say so loudly. */
  --color-crimson:     #7E2630;  /* Signal Red — rare interruption/accent only */
  --color-crimson-lit: #A1333E;  /* hover/active/live states */
  --color-moonlight:   #9DB2C0;  /* cold blue — secondary text and imagery alike */

  /* ---- Color: derived grayscale (cold slate, not neutral gray) ---- */
  --color-gray-900: #080B10;
  --color-gray-800: #131B23;
  --color-gray-700: #1A252D;
  --color-gray-600: #394750;
  --color-gray-500: #57676F;
  --color-gray-400: #78888F;
  --color-gray-300: #9DB2C0;

  /* ---- Color: semantic roles ---- */
  --bg-primary:        var(--color-black);
  --bg-secondary:      var(--color-black-deep);
  --bg-surface:        var(--color-gray-800);
  --bg-surface-raised: var(--color-gray-700);
  --text-primary:      var(--color-bone);
  --text-secondary:    var(--color-moonlight);
  --text-muted:        var(--color-gray-400);
  --border-subtle:     var(--color-gray-600);
  --interface-muted:   var(--color-gray-500);
  --accent-critical:   var(--color-crimson);
  --spine-glow:        var(--color-white);
  --focus-ring:        var(--color-white);

  /* Per-track accent. Sampled from the real cover art at runtime by the
     carousel (accentFromImage); the JSON value is only a dark fallback. */
  --track-accent: var(--color-white);

  /* ---- Typography: four roles across two families, retuned Aug 2026 ----
     Display  — Archivo, wdth 72 / wght 750, -0.03em, lh 0.9, UPPERCASE.
                Headings only. Tight enough to lock up as a block of ink.
     Showcase — Archivo, wdth 110 / wght 800. Replaces the retired stencil cut:
                the same family stretched wide, so a showcase line reads as an
                expanded version of the display face rather than a fourth voice.
     Body     — IBM Plex Sans 400 / 17px / lh 1.65 / +0.01em. Long-form, captions.
     Mono     — IBM Plex Mono 500 / 12px / +0.08em, UPPERCASE. Record numbers,
                timestamps, readouts.
     Label    — Rajdhani 600 / 13px / +0.18em, UPPERCASE. Nav, buttons, eyebrows,
                chips — every piece of small interface type.
     Wordmark — Archivo 900 / wdth 62. The two-line KUNDALINI SPINES lockup only.
     Magazine — Anton. About page only: masthead, section titles, pull lines.

     WHY LABELS ARE THEIR OWN FACE: the display spec is a lockup setting. At nav
     and button size, wdth 72 at -0.03em uppercase closes up and stops being
     readable, so small interface type moved to Rajdhani, which is built for it.
     Loaded via <link> in <head> — Archivo as a VARIABLE font across both axes,
     because the system uses three distinct cuts of it. font-stretch is the
     MATCH KEY, not decoration: a rule asking for Archivo at the default width
     matches no named instance and silently falls back ~20% wider. */
  --font-display:  'Archivo', 'Arial Narrow', sans-serif;
  --font-showcase: 'Archivo', 'Arial Narrow', sans-serif;
  --font-body:     'IBM Plex Sans', system-ui, sans-serif;
  --font-mono:     'IBM Plex Mono', 'Courier New', monospace;
  --font-label:    'Rajdhani', 'Arial Narrow', sans-serif;
  --font-mag:      'Anton', Impact, 'Arial Narrow', sans-serif;
  --font-wordmark: 'Archivo', 'Arial Narrow', sans-serif;
  /* COMPAT ALIAS. Big Shoulders Stencil is retired; the showcase cut is its
     replacement. css/track-experience.css, css/transmissions.css and
     css/components.css still read this name — they get Archivo (at default
     width unless the rule also sets font-stretch, which .text-showcase does). */
  --font-display-stencil: var(--font-showcase);

  /* Archivo axis settings — the three cuts. Always apply BOTH the
     font-variation-settings and the matching font-stretch. */
  --display-wdth:      72;
  --display-wght:      750;
  --display-stretch:   72%;
  --showcase-wdth:     110;
  --showcase-wght:     800;
  --showcase-stretch:  110%;
  --wordmark-stretch:  62%;   /* the match key — see css/wordmark.css */
  --wordmark-weight:   900;
  --wordmark-track:    0.046em; /* settled in the tuner Aug 10 2026 */

  /* Scale — retuned to Archivo. It sets narrower than Big Shoulders at the same
     px, so every step above body grew; the hero cap is the owner's 132px. */
  --fs-hero:    clamp(2.75rem, 9vw, 8.25rem); /* 44 → 132px */
  --fs-h1:      clamp(2rem, 5vw, 4rem);
  --fs-h2:      clamp(1.55rem, 3vw, 2.5rem);
  --fs-h3:      clamp(1.2rem, 1.9vw, 1.6rem);
  --fs-body-lg: clamp(1.125rem, calc(1.04rem + 0.34vw), 1.25rem);
  --fs-body:    clamp(1rem, calc(0.96rem + 0.19vw), 1.0625rem); /* 16 → 17px */
  --fs-caption: 0.875rem;
  --fs-mono:    0.75rem;   /* 12px */
  --fs-label:   0.8125rem; /* 13px */

  --lh-display: 0.9;   /* the lockup setting — h1/h2 only */
  --lh-tight:   0.92;
  --lh-heading: 1.05;  /* small headings, where 0.9 collides */
  --lh-body:    1.65;

  --tracking-display:  -0.03em; /* the lockup setting */
  --tracking-heading:  -0.01em; /* h3/h4 — -0.03em closes up below ~1.6rem */
  --tracking-label:     0.18em; /* Rajdhani: nav, buttons, eyebrows */
  --tracking-mono:      0.08em; /* Plex Mono readouts */
  --tracking-body:      0.01em;

  /* ---- Spacing scale (4px base) ---- */
  --space-1: 0.25rem;   /* 4px */
  --space-2: 0.5rem;    /* 8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-6: 1.5rem;    /* 24px */
  --space-8: 2rem;      /* 32px */
  --space-12: 3rem;     /* 48px */
  --space-16: 4rem;     /* 64px */
  --space-24: 6rem;     /* 96px */
  --space-32: 8rem;     /* 128px */

  /* ---- Grid / layout ---- */
  --container-max: 1280px;
  --container-pad: var(--space-6);
  --grid-gap: var(--space-6);

  /* TWO DIFFERENT JOBS, TWO DIFFERENT VALUES.
     --nav-h is written from JS on every frame (js/nav.js) and tracks the
     CURRENT bar as it collapses. It is correct for scroll offsets and
     scroll-margin-top, and WRONG for anything that affects layout: feeding a
     scroll-varying value into padding makes document height a function of
     scroll position, which shortens the page as you read it and invalidates
     any position measured before the scroll.
     --nav-h-max is the EXPANDED bar height, static and declarative. Layout
     clearance reads this one. */
  --nav-h: 72px;     /* fallback only; the live value is measured and republished */
  --nav-h-max: 92px;

  /* ---- Borders & surfaces ---- */
  --radius-sharp: 0px;     /* default — severe/institutional, not soft */
  --radius-min:   2px;     /* only for small interactive controls if needed */
  --border-hairline: 1px solid var(--border-subtle);

  /* ---- Motion (respects prefers-reduced-motion, see architecture.md) ---- */
  --motion-fast:  150ms;
  --motion-base:  300ms;
  --motion-slow:  600ms;
  --motion-hero:  1200ms;
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);

  /* ---- Breakpoints (reference only — used in @media queries directly) ----
     mobile:  0–479px   (base styles)
     sm:      480px
     md:      768px
     lg:      1024px
     xl:      1280px
     xxl:     1440px */
}

@media (max-width: 760px) {
  :root { --nav-h-max: 62px; }
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-fast: 0ms;
    --motion-base: 0ms;
    --motion-slow: 0ms;
    --motion-hero: 0ms;
  }
}
