/* ==========================================================================
   Design tokens — "Measured"
   --------------------------------------------------------------------------
   THE IDEA

   Everything on this site is about measurement under real-world constraint:
   four ground-state solvers benchmarked against each other, 3.24 s to a CO₂
   ground state, "the resolution that finally held". So the site is dressed as
   a laboratory instrument bound into a scientific journal — warm paper and
   ruled hairlines, oversized optical-serif numerals, mono readouts on every
   label, and a single phosphor-green signal colour.

   That means a few deliberate non-choices:

     - No indigo/violet, and no gradient on white. That palette is the default
       "developer portfolio" look and says nothing about this work.
     - No system font stack. Fraunces (display), Archivo (body) and JetBrains
       Mono (readouts) are self-hosted in fonts.css.
     - One accent, used sparingly and always to mean something — a live state,
       a link, a measured value. Colour is signal here, not decoration.

   --------------------------------------------------------------------------
   HOW THEMING WORKS

   Every colour, size and timing on the site resolves to something in this
   file. Change a value here and it propagates everywhere.

   Theming works in three layers, and all three are needed:

     :root                                    the complete light palette
     @media (prefers-color-scheme: dark)      the OS setting, guarded so an
                                              explicit light choice still wins
     :root[data-theme="dark"]                 the on-page toggle, which must
                                              beat the OS in both directions

   A colour must never have its only definition inside a media query, or it
   disappears for readers in the other mode.
   ========================================================================== */

:root {
  /* ---- type ------------------------------------------------------------
     Three families, three jobs. The fallbacks matter: `swap` means these are
     what people see for the first few hundred milliseconds, so each stack
     falls back to something with roughly the right proportions rather than
     collapsing to Times.                                                   */
  --font-display: "Fraunces", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --font-sans: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, "Liberation Mono", monospace;

  /* Fluid scale: each step interpolates between a phone and a desktop size.
     The top steps are deliberately steeper than the old scale — this design
     wants a real jump between a section title and a headline, not a gentle
     ramp where everything is nearly the same size.                         */
  --step--2: clamp(0.70rem, 0.68rem + 0.09vw, 0.76rem);
  --step--1: clamp(0.79rem, 0.77rem + 0.11vw, 0.86rem);
  --step-0:  clamp(0.97rem, 0.93rem + 0.19vw, 1.08rem);
  --step-1:  clamp(1.15rem, 1.08rem + 0.35vw, 1.36rem);
  --step-2:  clamp(1.38rem, 1.25rem + 0.62vw, 1.72rem);
  --step-3:  clamp(1.66rem, 1.44rem + 1.02vw, 2.24rem);
  --step-4:  clamp(1.99rem, 1.64rem + 1.66vw, 2.94rem);
  --step-5:  clamp(2.39rem, 1.84rem + 2.62vw, 3.86rem);
  --step-6:  clamp(2.87rem, 2.00rem + 4.15vw, 5.20rem);

  --leading-tight: 1.02;
  --leading-snug: 1.28;
  --leading-normal: 1.68;

  --tracking-tight: -0.021em;
  --tracking-display: -0.032em;
  /* Mono readouts are letterspaced wide — that is the instrument-panel cue. */
  --tracking-wide: 0.15em;
  --tracking-wider: 0.22em;

  /* Fraunces variable axes. WONK is the swashy alternate; SOFT rounds the
     terminals. Headlines get WONK on and stay optically sharp; small display
     text turns WONK off so it does not get noisy at 16px.                  */
  --wonk-on: "WONK" 1, "SOFT" 0;
  --wonk-off: "WONK" 0, "SOFT" 0;

  /* ---- spacing & shape ------------------------------------------------- */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs: 0.75rem;
  --space-s: 1rem;
  --space-m: 1.5rem;
  --space-l: 2.25rem;
  --space-xl: 3.5rem;
  --space-2xl: 5rem;
  --space-3xl: 7.5rem;

  /* Square. Not "mostly square" — square. Corners are the loudest signal of
     the generic soft-SaaS look, and this design earns its warmth from paper
     and type instead. Anything that must be a circle (status dots) uses
     border-radius: 50% directly rather than a large radius token.          */
  --radius-xs: 0px;
  --radius-s: 0px;
  --radius-m: 0px;
  --radius-l: 0px;
  --radius-xl: 0px;

  --measure: 66ch;
  --shell-max: 1240px;
  --shell-pad: clamp(1.15rem, 4vw, 3rem);
  --rule: 1px;

  /* ---- motion ---------------------------------------------------------- */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  --dur-fast: 140ms;
  --dur-base: 260ms;
  --dur-slow: 520ms;
  --dur-reveal: 720ms;

  /* ---- LIGHT PALETTE (the default definition of every colour) ----------
     Warm paper, not white. Every neutral carries a little yellow so the page
     reads as printed stock rather than a blank canvas.                     */
  color-scheme: light;

  --bg: #f2efe6;
  --bg-deep: #e8e4d7;
  --surface: #fbf9f3;
  --surface-2: #eeeade;
  --surface-raised: rgba(251, 249, 243, 0.86);
  --surface-glass: rgba(242, 239, 230, 0.80);

  --border: rgba(28, 24, 14, 0.14);
  --border-strong: rgba(28, 24, 14, 0.26);

  --ink: #17150f;
  --ink-2: #494437;
  --ink-3: #736c5a;
  --ink-inverse: #f7f5ee;

  /* Signal olive. Deep enough to carry body-weight link text on paper
     (6.16:1 against --bg) while still reading as the same hue family as the
     phosphor lime that replaces it in the dark theme.                      */
  --accent: #3f6212;
  --accent-2: #9a3412;
  --accent-3: #155e75;
  --accent-4: #8d6300;
  --accent-ink: #f7f5ee;
  --accent-soft: rgba(63, 98, 18, 0.12);
  --accent-glow: rgba(63, 98, 18, 0.26);

  /* Most screenshots on this site are light-background UI captures and
     scientific figures. On a light page they sit naturally; on a dark page a
     raw white rectangle glares and reads as a hole punched through the card.
     Card thumbnails are damped by this filter and restored on hover. Figures
     inside an article are never damped — a chart has to stay readable. */
  --card-image-filter: none;

  /* Warm-tinted shadows. A neutral grey shadow on warm paper looks like a
     smudge; these carry the same hue as the stock.                         */
  --shadow-s: 0 1px 2px rgba(40, 32, 12, 0.07), 0 2px 8px rgba(40, 32, 12, 0.05);
  --shadow-m: 0 4px 12px rgba(40, 32, 12, 0.08), 0 16px 40px rgba(40, 32, 12, 0.08);
  --shadow-l: 0 10px 30px rgba(40, 32, 12, 0.10), 0 32px 80px rgba(40, 32, 12, 0.12);

  /* Backdrop: ruled grid, particle field, film grain. */
  --grid-line: rgba(28, 24, 14, 0.05);
  --particle: rgba(63, 98, 18, 0.40);
  --grain-opacity: 0.030;

  /* ---- data visualisation ---------------------------------------------
     `#0891b2` and `#c2410c` are NOT free choices. They were validated with
     the palette checker: they clear the OKLCH lightness band, the chroma
     floor, protan/deutan separation (dE 20.1), the normal-vision floor
     (dE 29.0) and 3:1 contrast on both surfaces. They are unchanged by this
     redesign, and the surfaces below were re-checked against them rather
     than assumed — measured 3.50:1 and extrapolated 4.92:1 on the light
     surface, 4.95:1 and 3.52:1 on the dark one. Change any of these four
     values and re-run the validator, or the chart stops being readable for
     colourblind visitors.                                                  */
  --viz-surface: #fbf9f3;
  --viz-measured: #0891b2;
  --viz-extrapolated: #c2410c;
  --viz-ink: #17150f;
  --viz-ink-2: #494437;
  --viz-muted: #736c5a;
  --viz-grid: rgba(28, 24, 14, 0.10);
  --viz-axis: rgba(28, 24, 14, 0.24);
}

/* ---- DARK PALETTE ------------------------------------------------------
   Graphite with a green cast and phosphor lime on top — an oscilloscope, not
   a dimmed version of the light theme.

   Declared twice on purpose: once for the OS preference (guarded so that an
   explicit light choice on the html element still wins) and once for the
   toggle itself.                                                          */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --bg: #0d0e0b;
    --bg-deep: #070806;
    --surface: #141611;
    --surface-2: #1b1e17;
    --surface-raised: rgba(20, 22, 17, 0.78);
    --surface-glass: rgba(13, 14, 11, 0.76);

    --border: rgba(226, 232, 200, 0.11);
    --border-strong: rgba(226, 232, 200, 0.21);

    --ink: #f0eee4;
    --ink-2: #b6b3a3;
    --ink-3: #85806f;
    --ink-inverse: #0d0e0b;

    --accent: #c4f042;
    --accent-2: #fb923c;
    --accent-3: #67e8f9;
    --accent-4: #fcd34d;
    --accent-ink: #10160a;
    --accent-soft: rgba(196, 240, 66, 0.13);
    --accent-glow: rgba(196, 240, 66, 0.30);

    --card-image-filter: brightness(0.8) contrast(1.06) saturate(0.9);

    --shadow-s: 0 1px 2px rgba(0, 0, 0, 0.5), 0 2px 10px rgba(0, 0, 0, 0.4);
    --shadow-m: 0 6px 18px rgba(0, 0, 0, 0.52), 0 20px 48px rgba(0, 0, 0, 0.46);
    --shadow-l: 0 12px 34px rgba(0, 0, 0, 0.6), 0 40px 90px rgba(0, 0, 0, 0.55);

    --grid-line: rgba(226, 232, 200, 0.045);
    --particle: rgba(196, 240, 66, 0.42);
    --grain-opacity: 0.040;

    --viz-surface: #141611;
    --viz-measured: #0891b2;
    --viz-extrapolated: #c2410c;
    --viz-ink: #f0eee4;
    --viz-ink-2: #b6b3a3;
    --viz-muted: #85806f;
    --viz-grid: rgba(226, 232, 200, 0.10);
    --viz-axis: rgba(226, 232, 200, 0.22);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0d0e0b;
  --bg-deep: #070806;
  --surface: #141611;
  --surface-2: #1b1e17;
  --surface-raised: rgba(20, 22, 17, 0.78);
  --surface-glass: rgba(13, 14, 11, 0.76);

  --border: rgba(226, 232, 200, 0.11);
  --border-strong: rgba(226, 232, 200, 0.21);

  --ink: #f0eee4;
  --ink-2: #b6b3a3;
  --ink-3: #85806f;
  --ink-inverse: #0d0e0b;

  --accent: #c4f042;
  --accent-2: #fb923c;
  --accent-3: #67e8f9;
  --accent-4: #fcd34d;
  --accent-ink: #10160a;
  --accent-soft: rgba(196, 240, 66, 0.13);
  --accent-glow: rgba(196, 240, 66, 0.30);

  --card-image-filter: brightness(0.8) contrast(1.06) saturate(0.9);

  --shadow-s: 0 1px 2px rgba(0, 0, 0, 0.5), 0 2px 10px rgba(0, 0, 0, 0.4);
  --shadow-m: 0 6px 18px rgba(0, 0, 0, 0.52), 0 20px 48px rgba(0, 0, 0, 0.46);
  --shadow-l: 0 12px 34px rgba(0, 0, 0, 0.6), 0 40px 90px rgba(0, 0, 0, 0.55);

  --grid-line: rgba(226, 232, 200, 0.045);
  --particle: rgba(196, 240, 66, 0.42);
  --grain-opacity: 0.040;

  --viz-surface: #141611;
  --viz-measured: #0891b2;
  --viz-extrapolated: #c2410c;
  --viz-ink: #f0eee4;
  --viz-ink-2: #b6b3a3;
  --viz-muted: #85806f;
  --viz-grid: rgba(226, 232, 200, 0.10);
  --viz-axis: rgba(226, 232, 200, 0.22);
}

/* ---- Per-element accent overrides --------------------------------------
   A card sets its own accent by class, so the same component markup can be
   olive on one card and clay on the next without any inline style (which the
   Content-Security-Policy forbids).

   The class names stay hue-neutral (`accent-1` … `accent-4`) rather than
   naming a colour, because the colour they resolve to legitimately differs
   between the two themes — `accent-cyan` that renders olive in light mode
   would be a lie in the markup.                                           */
.accent-1 { --card-accent: var(--accent); --card-glow: var(--accent-glow); }
.accent-2 { --card-accent: var(--accent-2); --card-glow: rgba(251, 146, 60, 0.30); }
.accent-3 { --card-accent: var(--accent-3); --card-glow: rgba(103, 232, 249, 0.28); }
.accent-4 { --card-accent: var(--accent-4); --card-glow: rgba(252, 211, 77, 0.28); }

:root:not([data-theme="dark"]) .accent-2 { --card-glow: rgba(154, 52, 18, 0.24); }
:root:not([data-theme="dark"]) .accent-3 { --card-glow: rgba(21, 94, 117, 0.22); }
:root:not([data-theme="dark"]) .accent-4 { --card-glow: rgba(141, 99, 0, 0.24); }
