/* ============================================================================
   tokens.css — design token system for codymurri.com
   ----------------------------------------------------------------------------
   Four layers, each one a pure alias of the layer below it. The whole point is
   that a switch at Layer 3 or 4 needs ZERO per-component work, because nothing
   downstream ever names a raw value.

     1  PRIMITIVES     raw ramps + scales.            :root
     2  SEMANTIC       roles that components use.     .ctx-light / .ctx-dark
     3  TONE           re-points the neutral ramp.    [data-tone="warm"|"cold"]
     4  DENSITY        re-points type + space aliases.[data-density="…"]

   Rules of the road:
   - Components reference Layer 2 only. Never --cm-n-500, never #8A4B2E.
   - Layer 2 references Layer 1 only. Never a literal.
   - Tone works because Layer 2 aliases the neutral ramp by NAME, so swapping
     the ramp's values in Layer 3 repaints every surface for free.
   - Density works the same way: components use --cm-space-block, never 56px.

   Contexts are CLASSES, not html-level state, so light and dark can coexist on
   one page (a dark showcase band inside a light document).
   ============================================================================ */


/* ─── LAYER 1 · PRIMITIVES ────────────────────────────────────────────────── */

:root {

  /* Neutral ramp — paper → ink.
     Tone-reactive: Layer 3 redefines these in place. Values below are the WARM
     ramp, which is the site's original palette (n-50/100/600/900 are the old
     --paper / --paper-2 / --ink-soft / --ink, unchanged). */
  --cm-n-0:   #FFFFFF;
  --cm-n-25:  #F5F3EC;
  --cm-n-50:  #E8E6DE;
  --cm-n-100: #DEDBD0;
  --cm-n-200: #CFCBBB;
  --cm-n-300: #B3AE9C;
  --cm-n-400: #8F8A78;
  --cm-n-500: #6E695C;
  --cm-n-600: #4A4944;
  --cm-n-700: #35342F;
  --cm-n-800: #252420;
  --cm-n-900: #1B1B18;
  /* rgb triples for the ramp steps used at partial alpha. rgba() rather than
     color-mix() deliberately: color-mix is well supported now, but where it
     isn't the declaration is invalid and the border vanishes outright. */
  --cm-n-900-rgb: 27, 27, 24;
  --cm-n-25-rgb:  245, 243, 236;
  --cm-n-0-rgb:   255, 255, 255;
  --cm-n-800-rgb: 37, 36, 32;
  --cm-n-50-rgb:  232, 230, 222;

  /* Accent ramp — sienna. Not tone-reactive; it's the brand constant.
     500 and 700 are the original --sienna / --sienna-deep. */
  --cm-accent-100: #E5C9BB;
  --cm-accent-200: #D4A88F;
  --cm-accent-300: #C08765;
  --cm-accent-400: #A66744;
  --cm-accent-500: #8A4B2E;
  --cm-accent-600: #743E26;
  --cm-accent-700: #5F3220;
  --cm-accent-800: #462518;
  --cm-accent-900: #2E1810;

  /* Secondary — slate. 500 is the original --slate. */
  --cm-slate-300: #7A8B99;
  --cm-slate-500: #3D4F5C;
  --cm-slate-700: #2A3740;

  /* Plate materials — the dark card grounds and the tan photo placeholder.
     Distinct materials, deliberately off the neutral ramp. */
  --cm-plate-green-from: #26302F;
  --cm-plate-green-to:   #1C211F;
  --cm-plate-tan-from:   #D3CDB9;
  --cm-plate-tan-to:     #C3BDA7;
  --cm-plate-tan2-from:  #C9C2AC;
  --cm-plate-tan2-to:    #A9A189;

  /* Type families — kept byte-identical to the pre-token stacks. Adding
     fallbacks (Georgia / ui-monospace) is a real improvement for the webfont
     loading gap, but it changes metrics, so it's a separate decision. */
  --cm-font-serif: 'Fraunces', serif;
  --cm-font-mono:  'JetBrains Mono', monospace;

  /* Type scale — raw. Layer 4 re-points the semantic aliases, not these. */
  --cm-size-hero-1:   clamp(29px, 6.16vw, 81px);
  --cm-size-hero-2:   clamp(48px, 11vw, 168px);
  --cm-size-display:  clamp(64px, 12vw, 168px);
  --cm-size-stat:     56px;
  --cm-size-lead:     26px;
  --cm-size-note:     22px;
  --cm-size-h3:       19px;
  --cm-size-body:     16px;
  --cm-size-body-sm:  15px;
  --cm-size-body-xs:  14.5px;
  --cm-size-mono-md:  13px;
  --cm-size-mono-sm:  12.5px;
  --cm-size-mono-xs:  11.5px;
  --cm-size-mono-2xs: 11px;
  /* Mobile hero — the <=768px block runs its own doubled scale */
  --cm-size-hero-1-m: clamp(58px, 12.32vw, 162px);
  --cm-size-hero-2-m: clamp(72px, 18vw, 336px);


  /* Line height + tracking */
  --cm-leading-none:  0.88;
  --cm-leading-tight: 0.95;
  --cm-leading-snug:  1.05;
  --cm-leading-body:  1.55;
  --cm-tracking-tight:  -0.02em;
  --cm-tracking-snug:   -0.01em;
  --cm-tracking-mono:   0.02em;
  --cm-tracking-wide:   0.14em;

  /* Space scale — raw steps.
     The top of this scale is denser than it should be: 56/60, 70/72 and 90/96
     are near-duplicates inherited from hand-tuned values in the original
     stylesheet. They're preserved verbatim so this refactor moves zero pixels.
     Worth collapsing to 56 / 72 / 96 in a follow-up — that's a ~4px shift in
     four places and needs a design call, not a silent rounding. */
  --cm-space-1:  4px;
  --cm-space-2:  8px;
  --cm-space-3:  12px;
  --cm-space-4:  16px;
  --cm-space-5:  24px;
  --cm-space-6:  28px;
  --cm-space-7:  32px;
  --cm-space-8:  40px;
  --cm-space-9:  48px;
  --cm-space-10: 56px;
  --cm-space-11: 60px;
  --cm-space-12: 70px;
  --cm-space-13: 72px;
  --cm-space-14: 90px;
  --cm-space-15: 96px;
  --cm-space-16: 100px;

  /* Radius */
  --cm-radius-xs:   3px;
  --cm-radius-sm:   6px;
  --cm-radius-md:   8px;
  --cm-radius-lg:   10px;
  --cm-radius-xl:   20px;
  --cm-radius-pill: 999px;

  /* Motion */
  --cm-ease: cubic-bezier(0.4, 0, 0.2, 1);
  --cm-dur-fast: 0.12s;
  --cm-dur-base: 0.18s;
  --cm-dur-slow: 0.22s;
  /* Theme/tone crossfade. Zeroed under reduced-motion at the bottom of file. */
  --cm-dur-theme: 0.28s;

  /* Iconography — borrowed from the Origin prototype: 13px glyphs at 2.25
     stroke, Lucide geometry on a 24px grid. */
  --cm-icon-size: 13px;
  --cm-icon-stroke: 2.25;

  /* Layout */
  --cm-wrap-max: 1180px;
  /* Clearance from the visible bottom for the mobile hero text block: it must
     stay above the fixed .scroll-cue (40px up, ~15px tall). 86px leaves ~32px of
     air between the headline and the cue. */
  --cm-hero-bottom-m: 86px;

  /* Comfortable density defaults, restated here so the stylesheet stands on its
     own if [data-density] is ever absent. Layer 4 re-asserts and overrides
     these; equal specificity, later in file, so Layer 4 always wins. */
  --cm-type-hero-1:   var(--cm-size-hero-1);
  --cm-type-hero-2:   var(--cm-size-hero-2);
  --cm-type-display:  var(--cm-size-display);
  --cm-type-stat:     var(--cm-size-stat);
  --cm-type-lead:     var(--cm-size-lead);
  --cm-type-note:     var(--cm-size-note);
  --cm-type-h3:       var(--cm-size-h3);
  --cm-type-body:     var(--cm-size-body);
  --cm-type-body-sm:  var(--cm-size-body-sm);
  --cm-type-body-xs:  var(--cm-size-body-xs);
  --cm-type-mono-md:  var(--cm-size-mono-md);
  --cm-type-mono-sm:  var(--cm-size-mono-sm);
  --cm-type-mono-xs:  var(--cm-size-mono-xs);
  --cm-type-mono-2xs: var(--cm-size-mono-2xs);
  --cm-type-hero-1-m: var(--cm-size-hero-1-m);
  --cm-type-hero-2-m: var(--cm-size-hero-2-m);
  --cm-gap-section:  var(--cm-space-16);
  --cm-gap-block:    var(--cm-space-10);
  --cm-gap-item:     var(--cm-space-9);
  --cm-gap-grid:     var(--cm-space-8);
  --cm-gap-tight:    var(--cm-space-6);
  --cm-inset-page:   var(--cm-space-9);
  --cm-inset-page-sm: var(--cm-space-5);
  --cm-inset-hero:   var(--cm-space-11);
  --cm-pad-pill-y:    14px;
  --cm-pad-pill-x:    26px;
  --cm-pad-chip-y:    12px;
  --cm-pad-chip-x:    22px;
  --cm-pad-control-y: 7px;
  --cm-pad-control-x: 10px;
  --cm-pad-panel:    var(--cm-space-4);
  --cm-pad-trigger-y: 10px;
  --cm-pad-trigger-x: 16px;
  /* line-height is typography, so it is density-invariant by design */
  --cm-lead-body:    var(--cm-leading-body);
}


/* ─── LAYER 2 · SEMANTIC ROLES ────────────────────────────────────────────── */

/* Light. Reproduces the original palette exactly under the default warm tone. */
.ctx-light {
  color-scheme: light;

  --cm-surface-page:    var(--cm-n-50);   /* body ground */
  --cm-surface-raised:  var(--cm-n-0);    /* nav + hero, formerly hardcoded #fff */
  --cm-surface-sunken:  var(--cm-n-100);

  --cm-text-primary:    var(--cm-n-900);
  --cm-text-secondary:  var(--cm-n-600);
  --cm-text-tertiary:   var(--cm-slate-500);
  --cm-text-on-plate:   #EFECE3;

  --cm-rule: rgba(var(--cm-n-900-rgb), 0.16);

  --cm-accent:        var(--cm-accent-500);
  --cm-accent-strong: var(--cm-accent-700);
  /* Hero portrait strokes + the corner gridline motif */
  --cm-sketch-stroke:      var(--cm-accent);
  --cm-sketch-edge-width:  1.3;
  --cm-sketch-shade-width: 0.8;
  --cm-sketch-shade-alpha: 0.55;
  /* Temperature glyph tints — the prototype colours these two icons so warm and
     cool read at a glance rather than by label. Retuned off the blue/ochre pair
     to an ochre + slate that belong to this palette. */
  --cm-icon-warm: #C0863E;
  --cm-icon-cold: #6A5A8C;
  --cm-accent-rgb: 138, 75, 46;   /* accent-500 */
  --cm-gridline: rgba(var(--cm-accent-rgb), 0.35);
  --cm-gridline-opacity: 0.5;
  --cm-underlay-opacity: 0.07;

  /* Plates */
  --cm-plate-from: var(--cm-plate-green-from);
  --cm-plate-to:   var(--cm-plate-green-to);
  --cm-photo-from: var(--cm-plate-tan-from);
  --cm-photo-to:   var(--cm-plate-tan-to);
  --cm-photo-alt-from: var(--cm-plate-tan2-from);
  --cm-photo-alt-to:   var(--cm-plate-tan2-to);

  /* Shadows — rgb triple so opacity can flex per context */
  --cm-shadow-rgb: 27, 27, 24;
  --cm-shadow-soft:   0 18px 40px rgba(var(--cm-shadow-rgb), 0.15);
  --cm-shadow-strong: 0 18px 40px rgba(var(--cm-shadow-rgb), 0.25);
  --cm-shadow-lift:   0 10px 24px rgba(var(--cm-shadow-rgb), 0.25);

  /* Controls */
  --cm-btn-border:     var(--cm-text-primary);
  --cm-btn-fill-hover: var(--cm-text-primary);
  --cm-btn-text-hover: var(--cm-surface-page);
  --cm-btn-invert-fill: var(--cm-n-900);
  --cm-btn-invert-text: var(--cm-n-50);

  /* Scrim behind the mobile hero eyebrow */
  /* Field-notes cursor tag. It matches the page surface so it reads as part of
     the page rather than a foreign chip — which means it inverts with the theme
     just like everything else: light ground + dark type here, the reverse in
     .ctx-dark. Tone-reactive via the n-50 / n-900 triples. */
  --cm-tag-surface: rgba(var(--cm-n-50-rgb), 0.92);
  --cm-tag-foreground: var(--cm-text-primary);

  /* Scrim behind the mobile hero eyebrow. Derived from the hero's own
     surface (--cm-surface-raised) rather than a fixed near-black, so it
     reads as the background fading up instead of a grey box laid over it
     — and it follows tone automatically. */
  --cm-scrim: rgba(var(--cm-n-0-rgb), 0.92);

}

/* Dark — an art direction, not an inversion.
   Sienna at #8A4B2E goes muddy on a dark ground, so the accent lifts three
   steps to a terracotta that still reads as the same pigment. Thin portrait
   strokes get slightly heavier and the shade lines more opaque, because a dark
   ground eats hairlines that read fine on paper. Gridlines pull back — at 35%
   on dark they shout. Shadows go to true black and deepen, since a tinted
   shadow on a dark surface just looks like haze. */
.ctx-dark {
  color-scheme: dark;

  --cm-surface-page:    var(--cm-n-900);
  --cm-surface-raised:  var(--cm-n-800);  /* lifted, not pure black */
  --cm-surface-sunken:  #131311;

  --cm-text-primary:    var(--cm-n-25);
  --cm-text-secondary:  var(--cm-n-300);
  --cm-text-tertiary:   var(--cm-slate-300);
  --cm-text-on-plate:   #EFECE3;

  --cm-rule: rgba(var(--cm-n-25-rgb), 0.18);

  --cm-accent:        var(--cm-accent-300);
  --cm-accent-strong: var(--cm-accent-200);
  --cm-sketch-stroke:      var(--cm-accent);
  --cm-sketch-edge-width:  1.5;
  --cm-sketch-shade-width: 0.95;
  --cm-sketch-shade-alpha: 0.7;
  --cm-icon-warm: #E0B061;
  --cm-icon-cold: #B4A6D2;
  --cm-accent-rgb: 192, 135, 101; /* accent-300 */
  --cm-gridline: rgba(var(--cm-accent-rgb), 0.26);
  --cm-gridline-opacity: 0.42;
  --cm-underlay-opacity: 0.1;

  /* Plates barely separate from a dark page, so they lift instead of sink */
  --cm-plate-from: #2E3A38;
  --cm-plate-to:   #222A28;
  --cm-photo-from: #3A3630;
  --cm-photo-to:   #2C2925;
  --cm-photo-alt-from: #34302A;
  --cm-photo-alt-to:   #27241F;

  --cm-shadow-rgb: 0, 0, 0;
  --cm-shadow-soft:   0 18px 40px rgba(var(--cm-shadow-rgb), 0.45);
  --cm-shadow-strong: 0 18px 40px rgba(var(--cm-shadow-rgb), 0.6);
  --cm-shadow-lift:   0 10px 24px rgba(var(--cm-shadow-rgb), 0.55);

  --cm-btn-border:     var(--cm-text-primary);
  --cm-btn-fill-hover: var(--cm-text-primary);
  --cm-btn-text-hover: var(--cm-surface-page);
  --cm-btn-invert-fill: var(--cm-n-25);
  --cm-btn-invert-text: var(--cm-n-900);

  --cm-tag-surface: rgba(var(--cm-n-900-rgb), 0.82);
  --cm-tag-foreground: var(--cm-text-on-plate);
  --cm-scrim: rgba(var(--cm-n-800-rgb), 0.86);

}


/* ─── LAYER 3 · TONE ──────────────────────────────────────────────────────── */

/* Redefines the neutral ramp only. Every semantic role in Layer 2 aliases these
   by name, so both contexts re-tone for free — no per-context tone rules, and
   no component ever knows tone exists.
   Both states are explicit (no null-state default) so the active tone is always
   readable from the DOM and each has somewhere to hang overrides. */

  --cm-n-0-rgb:   255, 255, 255;
  --cm-n-800-rgb: 37, 36, 32;
/* Warm — the site's original paper. Ochre-leaning, printerly. */
[data-tone="warm"] {
  --cm-n-50-rgb:  232, 230, 222;
  --cm-n-900-rgb: 27, 27, 24;
  --cm-n-25-rgb:  245, 243, 236;
  --cm-n-0:   #FFFFFF;
  --cm-n-25:  #F5F3EC;
  --cm-n-50:  #E8E6DE;
  --cm-n-100: #DEDBD0;
  --cm-n-200: #CFCBBB;
  --cm-n-300: #B3AE9C;
  --cm-n-400: #8F8A78;
  --cm-n-500: #6E695C;
  --cm-n-600: #4A4944;
  --cm-n-700: #35342F;
  --cm-n-800: #252420;
  --cm-n-900: #1B1B18;
  --cm-slate-300: #7A8B99;
  --cm-slate-500: #3D4F5C;
  --cm-slate-700: #2A3740;
}

/* Cold — deep purple. Same lightness curve as warm, hue carried to ~278 with
   enough chroma to read as violet rather than blue-grey. Luminance is matched
   step-for-step against warm on purpose: contrast ratios hold either way, so
   the toggle reads as temperature, never as brightness. */
[data-tone="cold"] {
  --cm-n-50-rgb:  231, 228, 238;
  --cm-n-900-rgb: 28, 22, 38;
  --cm-n-25-rgb:  243, 241, 247;
  --cm-n-0-rgb:   254, 253, 255;
  --cm-n-800-rgb: 39, 32, 54;
  --cm-n-0:   #FEFDFF;
  --cm-n-25:  #F3F1F7;
  --cm-n-50:  #E7E4EE;
  --cm-n-100: #DCD8E6;
  --cm-n-200: #C9C4D6;
  --cm-n-300: #ACA4C0;
  --cm-n-400: #8A81A2;
  --cm-n-500: #6B6284;
  --cm-n-600: #4B4460;
  --cm-n-700: #372F49;
  --cm-n-800: #272036;
  --cm-n-900: #1C1626;
  /* The secondary follows temperature too. Leaving slate at its blue-grey
     original next to a violet ramp reads as two competing cool hues. */
  --cm-slate-300: #9A8FB8;
  --cm-slate-500: #574B76;
  --cm-slate-700: #3A3151;
}


/* ─── LAYER 4 · DENSITY ───────────────────────────────────────────────────── */

/* SPACING ONLY — deliberately not typography. Type size and line-height are
   identical in both states, so the typographic hierarchy reads the same and
   only the air around it moves.

   Density re-points the SPACE SCALE itself, exactly as Layer 3 re-points the
   colour ramp. That's why it needs no per-component work: every existing
   var(--cm-space-N) — and every --cm-gap-*/--cm-inset-* alias built on one —
   tightens automatically, including rules nobody remembered to update.

   Consequence worth knowing: the --cm-gap-* and --cm-inset-* aliases are
   defined ONCE, in :root. Overriding them here as well would compound with the
   rescaled steps and tighten twice. */

[data-density="comfortable"] {
  /* Control padding is the one axis not expressible on the space scale — these
     are optical values tuned to the type, so they're stated outright. */
  --cm-pad-pill-y:    14px;   /* .pill-btn */
  --cm-pad-pill-x:    26px;
  --cm-pad-chip-y:    12px;   /* .speedup-btn */
  --cm-pad-chip-x:    22px;
  --cm-pad-control-y: 7px;    /* segmented options */
  --cm-pad-control-x: 10px;
  --cm-pad-trigger-y: 10px;   /* display trigger */
  --cm-pad-trigger-x: 16px;
}

[data-density="compact"] {
  /* Steps 1–2 hold: 4px and 8px are micro-details (icon gaps, optical nudges)
     that read as broken rather than dense when shrunk. Everything above
     tightens on a curve — roughly 0.85x low, 0.65x high, so section rhythm
     collapses harder than component padding. */
  --cm-space-3:  10px;   /* 12  */
  --cm-space-4:  12px;   /* 16  */
  --cm-space-5:  18px;   /* 24  */
  --cm-space-6:  20px;   /* 28  */
  --cm-space-7:  22px;   /* 32  */
  --cm-space-8:  28px;   /* 40  */
  --cm-space-9:  32px;   /* 48  */
  --cm-space-10: 36px;   /* 56  */
  --cm-space-11: 40px;   /* 60  */
  --cm-space-12: 46px;   /* 70  */
  --cm-space-13: 48px;   /* 72  */
  --cm-space-14: 58px;   /* 90  */
  --cm-space-15: 62px;   /* 96  */
  --cm-space-16: 66px;   /* 100 */

  --cm-pad-pill-y:    9px;    /* 14 → 9  */
  --cm-pad-pill-x:    18px;   /* 26 → 18 */
  --cm-pad-chip-y:    8px;    /* 12 → 8  */
  --cm-pad-chip-x:    16px;   /* 22 → 16 */
  --cm-pad-control-y: 5px;    /* 7  → 5  */
  --cm-pad-control-x: 8px;    /* 10 → 8  */
  --cm-pad-trigger-y: 7px;    /* 10 → 7  */
  --cm-pad-trigger-x: 13px;   /* 16 → 13 */
}


/* ─── Cross-state transition ──────────────────────────────────────────────── */

/* Only the properties that actually change across theme/tone. Scoped to the
   elements that paint surfaces so it never fights the scroll/hover animations
   that already run on transforms and opacity. */
body,
nav,
.hero,
.pill-btn,
.feature-plate,
.scatter-item,
.hit-thumb {
  transition:
    background-color var(--cm-dur-theme) var(--cm-ease),
    border-color     var(--cm-dur-theme) var(--cm-ease),
    color            var(--cm-dur-theme) var(--cm-ease);
}

@media (prefers-reduced-motion: reduce) {
  :root { --cm-dur-theme: 0s; }
}

/* ─── Instant theme swap ────────────────────────────────────────────────────
   Only a handful of elements above carry a colour transition, so on a
   light/dark switch the un-transitioned type recolours immediately while those
   backgrounds fade over --cm-dur-theme. Two speeds at once reads as a glitch.

   theme-controls.js adds this class for a single frame around a theme change,
   so light/dark/auto lands all at once. Temperature and density changes do NOT
   use it — the cross-fade there is gradual by design and reads as intentional. */
.cm-instant,
.cm-instant *,
.cm-instant *::before,
.cm-instant *::after {
  transition: none !important;
}
