/* ============================================================================
   TOKENS

   The typographic and material language is Freepol's, unchanged: paper white,
   ink, hairline rules, Instrument Serif over Roboto with IBM Plex Mono for
   anything that behaves like a label, a timer or a count.

   What is NOT inherited is Freepol's refusal of colour. That refusal is
   argued — on a politics platform any accent reads as partisan — and the
   argument does not carry over. This app has to say right, wrong, easy, hard
   and four domains at a glance, and hue is the fastest carrier for all four.
   So colour is admitted, and then fenced: every value below is tied to a
   meaning, and nothing is decorative. Freepol's own theme.css already does
   this with --disc and --debate; this is the same move, further along.

   Three jobs, three different kinds of palette, per the rules in the dataviz
   skill:

     status        correct / incorrect. Reserved. Never used as a series
                   colour, and never shipped without an icon and a word, so
                   the meaning survives colour blindness and greyscale print.
     sequential    difficulty. Easy→Hard is ordered, so it is ONE hue getting
                   darker, not three unrelated hues.
     categorical   the four domains within a section. Fixed order, never
                   cycled; a fifth series does not exist because a section
                   only ever has four domains.

   The categorical steps are not taste. They were run through
   scripts/validate_palette.js against this canvas in both modes and adjusted
   until every check passed; an earlier hand-picked set failed protanopia
   separation at ΔE 2.8, and a later "improvement" that reordered them failed
   harder. The order below is load-bearing — teal, orange, green, plum — and
   reordering it breaks the adjacent-pair separation. Do not resort them.

   Worst adjacent pair is ΔE 6.0–6.5 (deuteranopia), which sits in the band
   that is only legal alongside a second, non-colour encoding. That condition
   is met because domain charts carry direct labels; if a chart is ever built
   here without them, the labels are the thing to add back, not the colours to
   change.
   ========================================================================= */

:root{
  /* ---- inherited from Freepol, verbatim ---------------------------------- */
  --canvas:#F1F0ED;
  --panel:#FAF9F7;
  --ink:#191917;
  --ink-2:#3A3A36;
  --ink-3:#5F5F58;
  --rule:#D8D6D0;
  --rule-strong:#8A887F;

  --display:"Instrument Serif",Georgia,"Times New Roman",serif;
  --body:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;

  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-soft:cubic-bezier(.4,0,.2,1);

  --gut:clamp(20px,5vw,72px);
  --maxw:1240px;

  /* Gradients need the canvas colour with alpha on it, and there is no way to
     put alpha on a hex custom property. Same trick as Freepol: publish it
     again as three numbers, and change both together or neither. */
  --canvas-rgb:241,240,237;

  /* ---- status: reserved, always paired with an icon and a word ----------- */
  --correct:#2E7D4F;
  --correct-soft:#E4EFE7;
  --correct-line:#9CC4AC;
  --wrong:#A8321F;
  --wrong-soft:#F6E5E1;
  --wrong-line:#D3A296;

  /* ---- sequential: difficulty, one ochre hue getting darker -------------- */
  --diff-easy:#D9A441;
  --diff-medium:#B87A1E;
  --diff-hard:#8A5410;
  --diff-easy-soft:#F7EEDC;
  --diff-medium-soft:#F2E4CC;
  --diff-hard-soft:#EBD9BE;

  /* ---- categorical: the four domains in a section. Order is fixed. ------- */
  --cat-1:#1B6FA8;
  --cat-2:#C25A1C;
  --cat-3:#1F8A6D;
  --cat-4:#A65A9C;

  /* Chart furniture stays recessive; ink tokens carry all text, never the
     series colour. */
  --grid:#E3E1DC;
  --axis:#C3C1BA;
}

/* ============================================================================
   DARK

   Not the light palette inverted. Freepol's reasoning holds: pure white on
   pure black vibrates, and this app is read for hours at a stretch. Warm near
   black, text a little under white.

   The categorical steps are SELECTED for this surface, not flipped — an
   automatic lightening failed the lightness band outright. These were
   validated against #14150F on their own.

   Three states, and an explicit light choice has to beat the media query,
   which is why every block is written twice.
   ========================================================================= */

@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --canvas:#14150F;
    --panel:#1C1D17;
    --ink:#EDEDE6;
    --ink-2:#C9C9C0;
    --ink-3:#9A9A90;
    --rule:#2E2F27;
    --rule-strong:#4A4B41;
    --canvas-rgb:20,21,15;

    --correct:#5FB584;
    --correct-soft:#18291F;
    --correct-line:#37624A;
    --wrong:#E08268;
    --wrong-soft:#2C1A16;
    --wrong-line:#6B3A2E;

    --diff-easy:#E0B45F;
    --diff-medium:#C08A33;
    --diff-hard:#9A6520;
    --diff-easy-soft:#2A2418;
    --diff-medium-soft:#251F14;
    --diff-hard-soft:#201A11;

    --cat-1:#3E93C8;
    --cat-2:#CE7029;
    --cat-3:#2E9878;
    --cat-4:#AE71A6;

    --grid:#26271F;
    --axis:#3A3B32;

    color-scheme:dark;
  }
}

:root[data-theme="dark"]{
  --canvas:#14150F;
  --panel:#1C1D17;
  --ink:#EDEDE6;
  --ink-2:#C9C9C0;
  --ink-3:#9A9A90;
  --rule:#2E2F27;
  --rule-strong:#4A4B41;
  --canvas-rgb:20,21,15;

  --correct:#5FB584;
  --correct-soft:#18291F;
  --correct-line:#37624A;
  --wrong:#E08268;
  --wrong-soft:#2C1A16;
  --wrong-line:#6B3A2E;

  --diff-easy:#E0B45F;
  --diff-medium:#C08A33;
  --diff-hard:#9A6520;
  --diff-easy-soft:#2A2418;
  --diff-medium-soft:#251F14;
  --diff-hard-soft:#201A11;

  --cat-1:#3E93C8;
  --cat-2:#CE7029;
  --cat-3:#2E9878;
  --cat-4:#AE71A6;

  --grid:#26271F;
  --axis:#3A3B32;

  color-scheme:dark;
}

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

/* ============================================================================
   BASE
   ========================================================================= */

*,*::before,*::after{box-sizing:border-box}

/* hidden is only display:none in the browser's own stylesheet, and any rule
   here that sets display beats it. This bit Freepol once — a guest bar that
   would not go away — so it is pinned here from the start. */
[hidden]{display:none!important}

html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
html,body{overflow-x:clip}

body{
  margin:0;
  background:var(--canvas);
  color:var(--ink);
  font-family:var(--body);
  font-size:clamp(15px,1.02vw,17px);
  font-weight:300;
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
}

h1,h2,h3{font-family:var(--display);font-weight:400;letter-spacing:-.012em;line-height:1.02;margin:0}
h1{font-size:clamp(38px,6.2vw,84px)}
h2{font-size:clamp(31px,4.4vw,60px)}
h3{font-size:clamp(22px,2.3vw,31px);line-height:1.1}

p{margin:0 0 1.05em}
p:last-child{margin-bottom:0}

.lede{font-size:clamp(17px,1.42vw,21px);line-height:1.55;color:var(--ink-2);max-width:44ch}

.kicker{
  font-family:var(--mono);font-size:11px;font-weight:500;
  letter-spacing:.17em;text-transform:uppercase;color:var(--ink-3);
  margin:0;
}

.rule{height:1px;background:var(--rule);border:0;margin:0;max-width:var(--maxw);margin-inline:auto}

.wrap{max-width:var(--maxw);margin:0 auto;padding:clamp(76px,11vh,140px) var(--gut)}

/* ---- buttons: Freepol's, unchanged ------------------------------------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  font-family:var(--mono);font-size:11.5px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;
  padding:13px 22px;text-decoration:none;border:1px solid var(--ink);
  background:var(--ink);color:var(--canvas);
  cursor:pointer;
  transition:background .38s var(--ease),color .38s var(--ease),transform .38s var(--ease);
}
.btn:hover{background:transparent;color:var(--ink);transform:translateY(-2px)}
.btn.ghost{background:transparent;color:var(--ink);border-color:var(--rule-strong)}
.btn.ghost:hover{background:var(--ink);color:var(--canvas);border-color:var(--ink)}
.btn:disabled{opacity:.4;cursor:not-allowed;transform:none}
@media (pointer:coarse){ .btn{min-height:44px} }

.badge{
  font-family:var(--mono);font-size:10.5px;font-weight:500;letter-spacing:.13em;text-transform:uppercase;
  color:var(--ink-3);border:1px solid var(--rule-strong);border-radius:999px;
  padding:5px 11px;white-space:nowrap;
}

/* ---- difficulty pill: colour plus the word, never colour alone ---------- */
.diff{
  font-family:var(--mono);font-size:10px;font-weight:500;letter-spacing:.13em;text-transform:uppercase;
  padding:3px 9px;border-radius:999px;border:1px solid currentColor;white-space:nowrap;
}
.diff.easy{color:var(--diff-easy);background:var(--diff-easy-soft)}
.diff.medium{color:var(--diff-medium);background:var(--diff-medium-soft)}
.diff.hard{color:var(--diff-hard);background:var(--diff-hard-soft)}

/* ---- correctness: colour, an icon and a word all say the same thing ----- */
.verdict{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);
         font-size:11px;letter-spacing:.11em;text-transform:uppercase}
.verdict.correct{color:var(--correct)}
.verdict.wrong{color:var(--wrong)}
.verdict svg{width:13px;height:13px;flex:none;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{transition-duration:.01ms!important;animation-duration:.01ms!important}
}
