/* ============================================================
   Investor's Last Exam — design tokens
   Direction: ink editorial · jade accent. Two stamps: ink (the
   default, and the brand) and paper, chosen by the reader with
   the header switch — `html[data-stamp]` is the only thing that
   selects one, so the choice is the reader's and not the OS's.
   The light stamp is RE-STEPPED against its own surface, never
   a flip of the dark one.

   Both palettes were validated with
   dataviz/scripts/validate_palette.js; the reports are recorded
   at the foot of this file so they can be re-run rather than
   trusted.
   ============================================================ */

:root {
  color-scheme: dark;

  /* ---- ink planes ------------------------------------------ */
  --ink-void: #07090c;
  --ink-page: #0b0e13; /* validator surface */
  --ink-raised: #11151c;
  --ink-inset: #161b23;
  --ink-quote: #1d232d;

  /* ---- ivory ink ------------------------------------------- */
  --text-1: #f2ede4;
  --text-2: #b7bbc3;
  --text-3: #878d98; /* muted: axis + labels */
  --text-4: #5b616b; /* furthest back, never load-bearing */

  /* ---- hairlines ------------------------------------------- */
  --hair: rgba(242, 237, 228, 0.1);
  --hair-2: rgba(242, 237, 228, 0.18);
  --hair-3: rgba(242, 237, 228, 0.32);
  --grid: #1c222b; /* chart gridline, one step off surface */
  --axis: #2b323d; /* baseline / axis rule */

  /* ---- jade accent (UI chrome, links, focus) --------------- */
  --jade-100: #d3f4e9;
  --jade-200: #a6e8d3;
  --jade-300: #6fd8ba;
  --jade-400: #43c39f;
  --jade-500: #2fa588; /* series slot 1 */
  --jade-600: #25836d;
  --jade-700: #1c6252;
  --jade-800: #144538;
  --jade-900: #0e2b25;

  /* ---- accent roles ---------------------------------------
     The 100–900 jade ramp above is mode-independent. What
     changes between stamps is WHICH step plays each role, so
     every component references a role and none reaches into
     the ramp directly. */
  --accent: var(--jade-300); /* text-weight accent on the surface */
  --accent-strong: var(--jade-200); /* display emphasis, <em> */
  --accent-line: var(--jade-600); /* borders, hovered outlines */
  --accent-fill: var(--jade-500); /* solid button / active chip */
  --accent-fill-hi: var(--jade-400); /* its hover step */
  --accent-fill-soft: var(--jade-800); /* pressed chip, badge bed */
  --accent-fill-softer: var(--jade-900);
  --accent-on-fill: #04120e; /* ink that sits ON --accent-fill */
  --accent-on-soft: var(--jade-100); /* ink on --accent-fill-soft */
  --focus: var(--jade-300);

  /* ---- surfaces that are not planes -----------------------
     Scrims sit over content, so they carry the page colour at
     partial alpha rather than a plane token. */
  --scrim: rgba(11, 14, 19, 0.82); /* sticky header */
  --scrim-2: rgba(11, 14, 19, 0.9); /* sticky filter row */
  --tip-bg: rgba(22, 27, 35, 0.97);
  --tip-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
  --row-hover: rgba(242, 237, 228, 0.035);
  --bloom: rgba(47, 165, 136, 0.16); /* hero atmosphere */
  /* Jade at low alpha. A tint has to sit over --ink-raised AND
     over --ink-page, so it is alpha rather than a mixed step. */
  --accent-tint: rgba(47, 165, 136, 0.08); /* button hover */
  --ours-tint: rgba(47, 165, 136, 0.07); /* our row in a table */
  --ours-tint-hi: rgba(47, 165, 136, 0.11);
  --grain-o: 0.035;
  --grain-blend: normal;

  /* ---- categorical series (fixed order, NEVER cycled) -----
     1 jade · 2 peri · 3 amber · 4 rose · 5 sky
     adjacent CVD ΔE worst 10.1 (protan) / 7.6 (tritan)
     tritan sits in the 6–8 band → secondary encoding is
     mandatory: legend always present + direct labels + gaps.
     Slots 1–3 additionally pass --pairs all, so scatter,
     dot-plot and small-multiple forms cap at three series. */
  --series-1: #2fa588;
  --series-2: #6e77e2;
  --series-3: #bc862f;
  --series-4: #d2597f;
  --series-5: #3486bf;

  --series-1-soft: rgba(47, 165, 136, 0.14);
  --series-2-soft: rgba(110, 119, 226, 0.14);
  --series-3-soft: rgba(188, 134, 47, 0.14);

  /* de-emphasis hue for the emphasis form */
  --series-mute: #3a424e;

  /* ---- sequential ramp (magnitude: heatmap, meter) --------
     One hue. Ordered so the step nearest the SURFACE comes
     first — darkest-first here, lightest-first on paper — which
     is what lets a consumer drop the same leading steps in
     either stamp. Ordinal use starts at 400: 100–300 recede
     into #0b0e13 and 300 fails the surface-end contrast floor
     (1.79:1). See the validator report at the foot of the file. */
  --seq-100: #0f1a1f;
  --seq-200: #133029;
  --seq-300: #17453a;
  --seq-400: #1b5b4b;
  --seq-500: #21755f;
  --seq-600: #2a9078;
  --seq-700: #3fb497;
  --seq-800: #6ed4ba;
  --seq-900: #a8ecd8;

  /* ---- diverging (Δ to consensus): sky ↔ rose -------------- */
  --div-neg-2: #244a68;
  --div-neg-1: #3486bf;
  --div-mid: #383835;
  --div-pos-1: #d2597f;
  --div-pos-2: #7a2f47;

  /* ---- status (reserved — never a series) ------------------ */
  --status-good: #0ca30c;
  --status-warning: #fab219;
  --status-serious: #ec835a;
  --status-critical: #d03b3b;

  /* ---- chart roles ----------------------------------------
     Read out of here by assets/js/theme.js, so a hand-rolled
     SVG never carries a literal hex and CSS stays the single
     source of truth for both stamps. */
  --chart-surface: #0b0e13; /* what a mark is punched out of */
  --ref-ink: #b7bbc3; /* human baseline: small marks, rules */
  --ref-area: #5b616b; /* the same baseline as a flooded bar */
  --bar-ours: #6fd8ba;
  --bar-model: #2a9078;
  --frontier: #2fa588;
  --ink-on-dark: #f2ede4; /* the two inks inkOn() chooses from */
  --ink-on-light: #07090c;

  /* ---- type ------------------------------------------------
     Two families only: an editorial serif for prose display,
     a system sans for everything functional. Chart text,
     axis ticks, stat values and the hero figure stay sans —
     the serif never carries data. */
  --font-display: "Iowan Old Style", "Palatino Linotype", "Source Han Serif SC",
    "Songti SC", "Noto Serif CJK SC", Georgia, serif;
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
    monospace;

  --t-micro: 0.6875rem; /* 11px — eyebrow, tick */
  --t-fine: 0.75rem; /* 12px — table, legend */
  --t-small: 0.8125rem;
  --t-body: clamp(0.9375rem, 0.9rem + 0.15vw, 1rem);
  --t-lead: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
  --t-h4: clamp(1.0625rem, 1rem + 0.4vw, 1.3125rem);
  --t-h3: clamp(1.375rem, 1.2rem + 0.8vw, 1.9375rem);
  --t-h2: clamp(1.75rem, 1.35rem + 1.8vw, 3.125rem);
  /* Inner-page <h1>. One step down from the overview's, because a
     section title on page 2 should not shout as loudly as the
     claim the whole site opens with. */
  --t-h1-sm: clamp(2rem, 1.45rem + 2.6vw, 4rem);
  --t-h1: clamp(2.5rem, 1.4rem + 4.6vw, 5.75rem);
  --t-hero: clamp(3rem, 1.2rem + 7vw, 8rem);

  --lh-tight: 1.04;
  --lh-snug: 1.22;
  --lh-body: 1.62;

  /* ---- space ---------------------------------------------- */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;
  --s-8: 4rem;
  --s-9: 6rem;
  --s-section: clamp(3.5rem, 2.5rem + 4.5vw, 8rem);
  --gutter: clamp(1.25rem, 0.75rem + 2.5vw, 4.5rem);
  --measure: 68ch;
  --max: 1440px;

  /* ---- motion --------------------------------------------- */
  --d-fast: 140ms;
  --d-norm: 260ms;
  --d-slow: 620ms;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-io: cubic-bezier(0.65, 0, 0.35, 1);

  /* ---- chart geometry (dataviz mark specs) ----------------- */
  --bar-max: 24px; /* never fill the band */
  --bar-round: 4px; /* rounded data-end, square at baseline */
  --line-w: 2px;
  --gap-surface: 2px; /* white does the separating */
  --dot-r: 5px;
}

/* ============================================================
   PAPER STAMP — html[data-stamp="paper"]
   Only what actually changes is restated. Type, space, motion
   and chart geometry are stamp-independent and stay above.

   An attribute rather than a media query, because the switch has
   to be able to disagree with the OS. It is also why nothing here
   is duplicated: were the media query kept as a default, every
   token below would need restating a third time to let a reader
   on a light desktop choose ink.

   This is not an inversion. Each slot was re-stepped against
   the paper surface #f6f3ec and re-run through the validator,
   because a hue that clears CVD separation on #0b0e13 has no
   obligation to clear it on paper — and the sequential ramp
   has to reverse direction to keep "nearest the surface" as
   its lightest end.
   ============================================================ */
:root[data-stamp="paper"] {
  color-scheme: light;

  /* ---- paper planes -------------------------------------
     --ink-page stays the name of the page plane in both
     stamps; only its value moves. Void is the deepest
     recess (footer), raised/inset step toward the reader. */
  --ink-void: #e8e2d5;
  --ink-page: #f6f3ec; /* validator surface */
  --ink-raised: #fffdf7;
  --ink-inset: #ede8dd;
  --ink-quote: #e6f0ea;

  /* ---- graphite ink ------------------------------------ */
  --text-1: #15181d;
  --text-2: #464d57;
  --text-3: #626a75;
  --text-4: #8b939d;

  /* ---- hairlines: ink at alpha, not ivory -------------- */
  --hair: rgba(11, 14, 19, 0.12);
  --hair-2: rgba(11, 14, 19, 0.2);
  --hair-3: rgba(11, 14, 19, 0.34);
  --grid: #e5e0d4;
  --axis: #cdc6b7;

  /* ---- accent roles: the ramp is the same, the steps
     are not. On paper the accent has to be DARKER than
     the surface to hold contrast, so each role moves
     three to four steps down the same jade ramp. */
  --accent: var(--jade-700);
  --accent-strong: var(--jade-800);
  --accent-line: var(--jade-500);
  --accent-fill: var(--jade-600);
  --accent-fill-hi: var(--jade-700);
  --accent-fill-soft: var(--jade-100);
  --accent-fill-softer: #eaf7f1;
  --accent-on-fill: #f4fbf8;
  --accent-on-soft: var(--jade-800);
  --focus: var(--jade-600);

  /* ---- scrims and atmosphere --------------------------- */
  --scrim: rgba(246, 243, 236, 0.86);
  --scrim-2: rgba(246, 243, 236, 0.92);
  --tip-bg: rgba(255, 253, 247, 0.98);
  --tip-shadow: 0 12px 32px rgba(11, 14, 19, 0.16);
  --row-hover: rgba(11, 14, 19, 0.04);
  --bloom: rgba(47, 165, 136, 0.14);
  /* Jade tints need more alpha on paper than on ink: 7% over
     #0b0e13 reads as a lit row, 7% over #f6f3ec is invisible. */
  --accent-tint: rgba(31, 138, 112, 0.09);
  --ours-tint: rgba(31, 138, 112, 0.08);
  --ours-tint-hi: rgba(31, 138, 112, 0.14);
  /* The grain is one fixed feTurbulence data-URI shared by
     both stamps. Screened over ink it lightens; over paper
     the same layer has to multiply or it fogs the page. */
  --grain-o: 0.05;
  --grain-blend: multiply;

  /* ---- categorical series, re-stepped ------------------
     1 jade · 2 peri · 3 amber · 4 rose · 5 sky — same
     identities in the same fixed order, so a model keeps
     its hue across stamps and only its lightness moves.
     Adjacent CVD worst ΔE 8.5 (deutan) / 6.3 (tritan):
     tritan again in the 6–8 band, so the same secondary
     encoding obligation carries over. Slots 1–3 pass
     --pairs all, so the three-series cap is unchanged. */
  --series-1: #1f8a70;
  --series-2: #5560cf;
  --series-3: #9a6b12;
  --series-4: #c2456c;
  --series-5: #2073a8;

  --series-1-soft: rgba(31, 138, 112, 0.12);
  --series-2-soft: rgba(85, 96, 207, 0.12);
  --series-3-soft: rgba(154, 107, 18, 0.12);

  --series-mute: #7a838e;

  /* ---- sequential ramp, reversed and re-stepped --------
     Ordered lightest→darkest here and darkest→lightest in
     the ink stamp: both put the step that recedes into the
     surface FIRST, which is what lets consumers drop the
     same two leading steps in either stamp. */
  --seq-100: #d9ede4;
  --seq-200: #a3d3bf;
  --seq-300: #63b498;
  --seq-400: #4aa286;
  --seq-500: #348d71;
  --seq-600: #28765e;
  --seq-700: #1d5f4c;
  --seq-800: #15493a;
  --seq-900: #0e3428;

  /* ---- diverging: declared for completeness, unused ---- */
  --div-neg-2: #1b5a80;
  --div-neg-1: #2073a8;
  --div-mid: #b8b2a4;
  --div-pos-1: #c2456c;
  --div-pos-2: #8a3050;

  /* ---- status, darkened to hold on paper --------------- */
  --status-good: #15803d;
  --status-warning: #b45309;
  --status-serious: #c2410c;
  --status-critical: #b91c1c;

  /* ---- chart roles ------------------------------------- */
  --chart-surface: #f6f3ec;
  --ref-ink: #626a75;
  --ref-area: #9aa2ac;
  --bar-ours: #1d5f4c;
  --bar-model: #4aa286;
  --frontier: #1f8a70;
}

/* ============================================================
   VALIDATOR REPORTS — re-runnable, not decorative
   Run from the dataviz skill directory; every line below is the
   output of the command above it, not a paraphrase.

   INK STAMP (surface #0b0e13)
     validate_palette.js "#2fa588,#6e77e2,#bc862f,#d2597f,#3486bf" \
       --mode dark --surface "#0b0e13"
       → ALL PASS. Worst adjacent #3486bf↔#d2597f ΔE 10.1 protan
         · tritan 7.6; normal-vision floor 16.8.
     … "#2fa588,#6e77e2,#bc862f" --mode dark --pairs all
       → ALL PASS. Worst all-pairs ΔE 10.8 protan; normal 17.4.
     … "#1b5b4b,#21755f,#2a9078,#3fb497,#6ed4ba,#a8ecd8" \
       --mode dark --surface "#0b0e13" --ordinal
       → ALL PASS. Surface-end #1b5b4b at 2.43:1 (floor 2.0).

   PAPER STAMP (surface #f6f3ec)
     … "#1f8a70,#5560cf,#9a6b12,#c2456c,#2073a8" \
       --mode light --surface "#f6f3ec"
       → ALL PASS. Worst adjacent #c2456c↔#9a6b12 ΔE 8.5 deutan
         · tritan 6.3; normal-vision floor 16.7.
     … "#1f8a70,#5560cf,#9a6b12" --mode light \
       --surface "#f6f3ec" --pairs all
       → ALL PASS. Worst all-pairs ΔE 10.5 deutan; normal 15.8.
     … "#4aa286,#348d71,#28765e,#1d5f4c,#15493a,#0e3428" \
       --mode light --surface "#f6f3ec" --ordinal
       → ALL PASS. Surface-end #4aa286 at 2.79:1 (floor 2.0).

   Two consequences that are load-bearing in the JS:

   1. The ordinal runs above are --seq-400…900, i.e. SEQ.slice(3).
      Consumers drop the THREE steps nearest the surface, not two:
      at slice(2) the ink stamp's #17453a sits at 1.79:1 and FAILS
      the surface-end floor. Because each stamp orders its ramp
      surface-first, the same slice is correct in both.

   2. Both stamps carry a tritan ΔE inside the 6–8 band on one
      adjacent pair, legal ONLY with secondary encoding: a legend
      is always present, ≥2 series are direct-labelled, and every
      abutting fill is separated by --gap-surface.
   ============================================================ */
