@charset "utf-8";
/* =====================================================================
   theryanbanik.com -- THE WORKING NOTEBOOK
   ---------------------------------------------------------------------
   One stylesheet. No CDN, no web fonts, no build step, no framework.

   The organising idea: this page is a lab notebook, so it is TYPESET,
   not styled. That means a real margin rail carrying real marginalia, a
   fixed left origin every element hangs from, and three type registers
   that each do exactly one job:

       serif  -> a human wrote this
       mono   -> a machine printed this
       sans   -> this is an instrument label

   The semantic colour rule that must hold on every surface, canvas
   included: the VISITOR writes in pencil (--graphite), the SYSTEM replies
   in ink (--pen), the GRADING is in red (--red-pencil). You always know
   which marks are yours.

   Section index
     1  TOKENS
     2  RESET + BASE
     3  TYPE SCALE + REGISTERS
     4  THE GRID AND THE MARGIN RAIL
     5  SECTIONS, MASTHEAD, RHYTHM
     6  FIGURE CARDS
     7  CANVASES
     8  CONTROLS  (range, buttons, readouts, verdicts, legends)
     9  THE DISCLOSURE PANEL
    10  PROSE DETAILS
    11  TABLES
    12  UTILITIES
    13  RESPONSIVE
    14  PRINT
    15  REDUCED MOTION
   ===================================================================== */


/* =====================================================================
   1  TOKENS
   ---------------------------------------------------------------------
   Every colour on this site -- CSS and canvas alike -- resolves through
   one of these names. boot.js reads them with getComputedStyle via
   api.css(), so a demo never holds a hex value of its own and dark mode
   costs the JS nothing.

   Saturation budget: no saturated colour may exceed ~4% of a figure's
   area. These hues are chosen to survive that discipline -- the page is
   carried by warm greys, and colour is used the way a pen is.
   ===================================================================== */
:root {
  color-scheme: light;

  /* -- surfaces ---------------------------------------------------- */
  --paper:      #FBFAF6;   /* page ground: warm ivory, never pure white */
  --card:       #FFFFFF;   /* figure surface, raised by LUMINANCE only  */
  --rule:       #E6E1D6;   /* hairlines: dividers, gridlines, the rail  */

  /* -- ink levels -------------------------------------------------- */
  --ink:        #15181C;   /* body prose and headings                   */
  --ink-2:      #454C55;   /* captions, axis labels, lead paragraphs    */
  --ink-3:      #6A7079;   /* units, ticks, marginalia, paths. >=13px.  */

  /* -- the pen: the machine's mark, and the ideal ------------------- */
  --pen:        #1F4FA8;   /* links, handles, y=x, solved chain, refs   */
  --pen-wash:   #DEE7F8;   /* reachable workspace, CI bands, hover halo */

  /* -- the red pencil: CORRECTIONS ONLY ----------------------------- */
  --red-pencil: #B23B2E;   /* OVERCONFIDENT, UNREACHABLE, REJECTED      */
  --red-wash:   #F7E3E0;   /* fill behind a failed state                */

  /* -- the visitor's own marks -------------------------------------- */
  --graphite:   #59606B;   /* every dot they generate, raw scatter      */

  /* -- exactly two green states, and one amber ---------------------- */
  --green-ink:  #27714F;   /* WELL_CALIBRATED, a bucket reaching n>=5   */
  --amber-ink:  #8F6309;   /* INSUFFICIENT_DATA, near-singular, unsure  */

  /* -- LEGACY ALIASES ----------------------------------------------
     js/arm.js was written against an older, darker token set and calls
     api.css('--dim'), '--faint', '--accent', '--surface'. Without these
     it silently falls back to its own dark-theme hexes and draws a dark
     figure on warm paper. Mapping them onto the real palette keeps that
     demo correct and costs nothing. Delete this block once arm.js is
     ported to the palette names above.                                */
  --dim:     var(--ink-2);
  --faint:   var(--rule);
  --accent:  var(--pen);
  --surface: var(--card);

  /* -- type registers ---------------------------------------------- */
  --serif: ui-serif, Georgia, "Times New Roman", Times, serif;
  --mono:  ui-monospace, SFMono-Regular, "Cascadia Mono", Menlo, Consolas,
           "Liberation Mono", monospace;
  --sans:  system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
           Arial, sans-serif;

  /* -- type scale (11/13/15/17/19/23/27/34/44/56 px) ---------------- */
  --t-11: 0.6875rem;
  --t-13: 0.8125rem;
  --t-15: 0.9375rem;
  --t-17: 1.0625rem;
  --t-19: 1.1875rem;
  --t-23: 1.4375rem;
  --t-27: 1.6875rem;
  --t-34: 2.125rem;
  --t-44: 2.75rem;
  --t-56: 3.5rem;

  /* -- layout ------------------------------------------------------- */
  --measure: 33rem;   /* prose column: ~68 characters at 19px serif     */
  --fig:     44rem;   /* standard figure card                           */
  --wide:    62rem;   /* wide figure card, and the assembly width       */
  --rail:    15rem;   /* the margin rail                                */
  --gutter:   3rem;   /* rail -> column gap                             */
  --pad-x:    1.5rem; /* page edge padding                              */

  /* -- vertical rhythm, 8px base ------------------------------------ */
  --gap-section: 96px;  /* between top-level sections (56 on mobile)    */
  --gap-fig:     40px;  /* between a figure and the paragraph above it  */
  --pad-card:    28px;  /* inside a figure card                         */
  --gap-cap:     12px;  /* card -> caption                              */

  --radius: 2px;        /* the only radius on the site                  */
}

/* ---------------------------------------------------------------------
   Dark mode inverts LUMINANCE and keeps every hue ROLE identical. Only
   the tokens are redefined -- not one layout or type rule below changes.
   ------------------------------------------------------------------ */
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;

    --paper:      #14161A;
    --card:       #1B1E23;
    --rule:       #2A2E35;
    --ink:        #E9E6E0;
    --ink-2:      #A8AEB6;
    --ink-3:      #848A93;
    --pen:        #7FA8F0;
    --pen-wash:   #1D2B44;
    --red-pencil: #E4796B;
    --red-wash:   #3A2220;
    --graphite:   #9AA0A8;
    --green-ink:  #57B98C;
    --amber-ink:  #DCA83C;
  }
}


/* =====================================================================
   2  RESET + BASE
   ===================================================================== */
*, *::before, *::after { box-sizing: border-box; }

/* Smooth scrolling is BANNED. An anchor jump should be instantaneous --
   animating it steals control from the reader and breaks find-in-page.  */
html {
  scroll-behavior: auto;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--t-19);
  line-height: 1.62;
  font-weight: 400;
  hyphens: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-kerning: normal;
  font-variant-ligatures: common-ligatures;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ol, ul, pre { margin: 0; }
ol, ul { padding-left: 1.25em; }

img, svg, video, canvas { max-width: 100%; }
img, svg, video { height: auto; display: block; }

/* Prose gets `pretty` (no orphans, better rag); headings get `balance`
   (even line lengths across 2-3 lines). Both degrade to nothing. */
p, li, dd, figcaption, blockquote { text-wrap: pretty; }
h1, h2, h3, h4, h5, h6, .fig-title, .lede { text-wrap: balance; }

/* Never remove an outline without replacing it. This is the replacement,
   and it is the only one on the site. */
:focus-visible {
  outline: 2px solid var(--pen);
  outline-offset: 2px;
  border-radius: var(--radius);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--pen-wash); color: var(--ink); }

hr {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: var(--gap-fig) 0;
}

/* Skip link: visible only when focused, but a real, reachable control. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  background: var(--card);
  color: var(--pen);
  font-family: var(--sans);
  font-size: var(--t-13);
  padding: 10px 14px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.skip:focus { left: var(--pad-x); top: var(--pad-x); }


/* =====================================================================
   3  TYPE SCALE + REGISTERS
   ---------------------------------------------------------------------
   Three registers, each with a job. The single highest-value decision in
   this whole file is `tabular-nums slashed-zero` on every readout: digits
   must not shuffle sideways while a slider is being dragged, and a zero
   must not be mistaken for an O in a machine printout.
   ===================================================================== */

/* -- register helpers ------------------------------------------------ */
.serif { font-family: var(--serif); font-weight: 400; }
.sans  { font-family: var(--sans); }
.mono  { font-family: var(--mono); font-variant-ligatures: none; }

/* Anything that prints a NUMBER wears these. */
.readout, .num, .tbl td, .tbl th,
code, kbd, samp, pre, .path, .rail, .verdict {
  font-variant-numeric: tabular-nums slashed-zero;
}

/* -- the scale, as utilities ---------------------------------------- */
.t11 { font-size: var(--t-11); }
.t13 { font-size: var(--t-13); }
.t15 { font-size: var(--t-15); }
.t17 { font-size: var(--t-17); }
.t19 { font-size: var(--t-19); }
.t23 { font-size: var(--t-23); }
.t27 { font-size: var(--t-27); }
.t34 { font-size: var(--t-34); }
.t44 { font-size: var(--t-44); }
.t56 { font-size: var(--t-56); }

/* -- headings: SANS, because a heading is an instrument label -------- */
h1, h2, h3, h4 {
  font-family: var(--sans);
  color: var(--ink);
  line-height: 1.14;
  letter-spacing: -0.02em;
}

h1 {
  font-size: var(--t-44);
  font-weight: 700;
  letter-spacing: -0.03em;
}

h2 {
  font-size: var(--t-34);
  font-weight: 600;
  line-height: 1.16;
  margin-top: 0;
}

h3, .fig-title {
  font-family: var(--sans);
  font-size: var(--t-27);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--ink);
}

h4 {
  font-size: var(--t-23);
  font-weight: 600;
  line-height: 1.25;
}

/* h5/h6 are label-scale: small caps-ish sans, used as run-in headers. */
h5, h6, .kicker, .fig-num, .eyebrow {
  font-family: var(--sans);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  line-height: 1.3;
  color: var(--ink-2);   /* --ink-3 is never allowed below 13px */
}
h5 { font-size: var(--t-13); letter-spacing: 0.1em; }
h6 { font-size: var(--t-11); }
.kicker, .eyebrow { font-size: var(--t-11); }

/* -- lead paragraph -------------------------------------------------- */
.lede {
  font-family: var(--serif);
  font-size: var(--t-23);
  line-height: 1.5;
  color: var(--ink-2);
}

/* -- small print ----------------------------------------------------- */
small, .small {
  font-size: var(--t-15);
  color: var(--ink-2);
  line-height: 1.55;
}


/* =====================================================================
   4  THE GRID AND THE MARGIN RAIL
   ---------------------------------------------------------------------
   This is the signature of the whole direction, so it is worth being
   precise about what it does.

   A `.leaf` is one row of the notebook: an aside in the margin and a
   column of content beside it.

       <div class="leaf">
         <aside class="rail"> ... marginalia ... </aside>
         <div class="col">    ... content ...   </div>
       </div>

   At >=1180px the leaf becomes a three-track grid --

       [ --rail 15rem ][ --gutter 3rem ][ column, up to --wide ]

   -- and the whole 80rem assembly is centred with `justify-content`.
   Because that assembly width is the SAME on every leaf on the page, the
   left edge of the rail, the left edge of the prose, and the left edge of
   every figure card all land on one constant origin. That constant origin
   is what makes the page read as typeset rather than styled: the text is
   ragged on the right and dead straight on the left, exactly like a book.

   Content inside .col then sets its own measure:
       prose        -> --measure (33rem)
       .fig         -> --fig     (44rem)
       .fig--wide   -> --wide    (62rem)
   all sharing that left edge.

   Below 1180px the rail collapses INLINE, above the content it annotates,
   as an indented note with a 2px --rule left border -- same 13px mono, so
   the marginalia never disappears, it just changes seat.
   ===================================================================== */

.leaf {
  display: block;
  padding-inline: var(--pad-x);
}

/* The rail, collapsed (mobile-first). An indented note in the flow. */
.rail {
  font-family: var(--mono);
  font-size: var(--t-13);
  line-height: 1.55;
  color: var(--ink-3);
  border-left: 2px solid var(--rule);
  padding-left: 14px;
  margin: 0 0 24px 0;
  max-width: var(--measure);
}

/* One note inside the rail. Several stack with real space between. */
.note { margin: 0; }
.note + .note { margin-top: 14px; }

/* The tiny sans label that titles a note ("ALGORITHM", "SOURCE"). */
.note-h {
  display: block;
  font-family: var(--sans);
  font-size: var(--t-11);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-2);
  margin-bottom: 3px;
}

/* A real file path. Must be allowed to break anywhere -- a path that
   forces a horizontal scrollbar on a phone is a bug, not a detail. */
.path {
  font-family: var(--mono);
  font-size: var(--t-13);
  color: var(--ink-3);
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* The content column. min-width:0 so a wide child (a <pre>, a table)
   can scroll inside itself instead of blowing the grid track open. */
.col { min-width: 0; }

/* Column rhythm: prose flows, figures get their own breathing room. */
.col > * + * { margin-top: 0.9em; }
.col > p + p { margin-top: 1.05em; }
.col > h2 { margin-top: 1.6em; }
.col > h3 { margin-top: 1.5em; }
.col > h4 { margin-top: 1.4em; }
.col > h2:first-child,
.col > h3:first-child,
.col > h4:first-child { margin-top: 0; }

/* Prose sets its own measure; instruments are allowed to be wider. */
.col > p,
.col > ul,
.col > ol,
.col > dl,
.col > blockquote,
.col > .lede,
.col > h2,
.col > h3,
.col > h4 { max-width: var(--measure); }

.col > .figure,
.col > .panel { max-width: var(--fig); }
.col > .figure--wide,
.col > .panel--wide { max-width: var(--wide); }

/* 40px between a figure and the paragraph introducing it. */
.col > .figure,
.col > .panel {
  margin-top: var(--gap-fig);
  margin-bottom: var(--gap-fig);
}

@media (min-width: 1180px) {
  .leaf {
    display: grid;
    grid-template-columns: var(--rail) var(--gutter) minmax(0, var(--wide));
    justify-content: center;
    align-items: start;
    padding-inline: var(--pad-x);
  }

  .rail {
    grid-column: 1;
    border-left: 0;
    padding-left: 0;
    margin: 0;
    max-width: none;
    text-align: left;
    /* Optical alignment: 13px/1.55 mono cap-height sits ~3.6px higher in
       its line box than 19px/1.62 serif does in its. Nudge the rail down
       so the first line of a note and the first line of the paragraph it
       annotates share a baseline. */
    padding-top: 0.25rem;
  }
  /* When the column starts with a HEADING rather than a paragraph, the
     nudge is wrong -- opt out. */
  .rail--top { padding-top: 0; }
  /* Push a note down to sit beside a later paragraph. */
  .rail--drop-1 { padding-top: 2.2rem; }
  .rail--drop-2 { padding-top: 4.4rem; }

  .col { grid-column: 3; }
}


/* =====================================================================
   5  SECTIONS, MASTHEAD, RHYTHM
   ===================================================================== */
.section { margin: 0; padding: 0; }
.section + .section { margin-top: var(--gap-section); }

/* A hairline between sections, drawn on the same origin as the column so
   it reads as a ruled line in a notebook rather than a page divider. */
.section--ruled { border-top: 1px solid var(--rule); padding-top: 40px; }

.masthead {
  padding-top: 88px;
  padding-bottom: 8px;
}
.masthead h1 {
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(2.125rem, 1.2rem + 4.2vw, 3.5rem);
  letter-spacing: -0.03em;
  line-height: 1.04;
  margin: 0;
  max-width: 20ch;
}
.masthead .kicker { margin-bottom: 18px; }
.masthead .lede { margin-top: 22px; }

.site-footer {
  margin-top: var(--gap-section);
  padding: 32px 0 88px;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: var(--t-13);
  color: var(--ink-3);
  line-height: 1.7;
}


/* =====================================================================
   6  FIGURE CARDS
   ---------------------------------------------------------------------
   The card is raised by LUMINANCE ONLY: --card sits a hair above --paper
   and that is the entire elevation system. No border, no shadow, 2px
   radius. A drop shadow here would be a lie about depth on a page whose
   whole conceit is flat paper.

   Furniture, top to bottom:
       .fig-num    11px caps sans, sitting ON a hairline rule
       .fig        the card:  .fig-title  ->  canvas  ->  .controls
       .fig-cap    15px sans --ink-2, 12px below the card
   ===================================================================== */

.figure { margin: var(--gap-fig) 0; }

.fig-num {
  display: block;
  border-top: 1px solid var(--rule);
  padding-top: 10px;
  margin-bottom: 12px;
  font-family: var(--sans);
  font-size: var(--t-11);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-2);
}
/* The number itself in --ink-3 would break the 13px floor, so the
   separator carries the tone instead. */
.fig-num .sep { color: var(--rule); padding: 0 0.5em; }

.fig {
  background: var(--card);
  border: 0;
  box-shadow: none;
  border-radius: var(--radius);
  padding: var(--pad-card);
}

.fig-title { margin: 0 0 6px; }

.fig-sub {
  font-family: var(--sans);
  font-size: var(--t-15);
  color: var(--ink-2);
  line-height: 1.5;
  margin: 0 0 18px;
  max-width: 52ch;
}
.fig-title + canvas,
.fig-title + .canvas-wrap { margin-top: 18px; }

.fig-cap {
  margin-top: var(--gap-cap);
  font-family: var(--sans);
  font-size: var(--t-15);
  line-height: 1.5;
  color: var(--ink-2);
  max-width: var(--measure);
}
.fig-cap .path,
.fig-cap code { font-size: var(--t-13); }

/* A hairline divider inside a card, e.g. between canvas and controls. */
.fig-div {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: 20px 0;
}


/* =====================================================================
   7  CANVASES
   ---------------------------------------------------------------------
   HEIGHT COMES FROM aspect-ratio, not a fixed pixel height. boot.js sizes
   the backing store off getBoundingClientRect() on every debounced
   resize, so a ratio-driven box is responsive for free and there is no
   breakpoint list to maintain. `min-height` guards the narrow end, where
   16:10 would otherwise collapse a figure to a letterbox.

   TOUCH: boot.js only calls preventDefault() once a demo has called
   api.claim(), so the default is `touch-action: pan-y` -- vertical scroll
   past a figure always works, horizontal drags belong to the demo. A
   figure that genuinely needs TWO-AXIS dragging (a target you pull around
   a plane) must opt in with .canvas-drag, which takes the whole gesture.
   ===================================================================== */

.fig canvas,
canvas[data-demo] {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  min-height: 15rem;
  max-height: 78vh;
  background: transparent;
  touch-action: pan-y;
  /* A canvas the visitor can focus for the keyboard path. */
  border-radius: var(--radius);
}

/* Aspect-ratio modifiers.
   NOTE the `canvas.` qualifier on every one of these. The base rule above
   is `canvas[data-demo]`, which is (0,1,1); a bare `.ar-2x1` is (0,1,0)
   and would LOSE to it. Qualifying by element matches specificity and
   lets source order decide, which is what a modifier is supposed to do. */
canvas.ar-1x1 { aspect-ratio: 1 / 1; }
canvas.ar-4x3 { aspect-ratio: 4 / 3; }
canvas.ar-3x2 { aspect-ratio: 3 / 2; }
canvas.ar-2x1 { aspect-ratio: 2 / 1; }
canvas.ar-4x5 { aspect-ratio: 4 / 5; min-height: 20rem; }

/* Two-axis dragging: this element owns the gesture, so the page will NOT
   scroll under a finger inside it. Only put this on a figure whose whole
   point is pulling something around a plane. Same specificity note. */
canvas.canvas-drag { touch-action: none; }

/* A focusable canvas must show it. */
canvas[tabindex]:focus-visible {
  outline: 2px solid var(--pen);
  outline-offset: 3px;
}

/* A demo that has been frozen by prefers-reduced-motion. boot.js stamps
   data-static on it after drawing one honest frame. */
canvas[data-static] { cursor: default; }

/* Optional wrapper when a figure needs an overlay or a caption strip
   pinned to the canvas box. */
.canvas-wrap { position: relative; }

/* The keyboard/screen-reader status line that every draggable needs.
   Sits directly under the canvas in mono so it reads as machine output. */
.live {
  display: block;
  margin-top: 10px;
  font-family: var(--mono);
  font-size: var(--t-13);
  line-height: 1.5;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums slashed-zero;
  min-height: 1.5em;   /* reserve the line so the page never jumps */
}

/* A one-line instruction, e.g. "drag the target -- arrow keys also work". */
.hint {
  display: block;
  margin-top: 8px;
  font-family: var(--sans);
  font-size: var(--t-13);
  color: var(--ink-3);
}


/* =====================================================================
   8  CONTROLS
   ---------------------------------------------------------------------
   Controls sit BELOW the figure they control, always, so a thumb on a
   phone never covers the diagram it is steering.

   The range input is styled to read as a drafting instrument: a 2px
   --rule track, a 14px --pen dot, and a 22px invisible hit area around
   the dot so it is actually grabbable. The dot is painted as a radial
   gradient inside a 22px thumb rather than as a 14px thumb, because that
   is the only way to get a large hit target and a small mark in both
   WebKit and Gecko without a wrapper element.
   ===================================================================== */

.controls {
  display: grid;
  gap: 16px;
  margin-top: 22px;
}
.controls--2col { grid-template-columns: 1fr; }
@media (min-width: 720px) {
  .controls--2col { grid-template-columns: 1fr 1fr; column-gap: 32px; }
}

/* One labelled instrument: label | slider | readout. */
.control {
  display: grid;
  grid-template-columns: 1fr max-content;
  grid-template-rows: auto auto;
  align-items: center;
  column-gap: 14px;
  row-gap: 6px;
  min-width: 0;
}
/* Placement is EXPLICIT, not auto, because the source order is
   label -> slider -> readout but the phone layout wants the slider full
   width on a second row. Every selector also matches the bare element, so
   an author who forgets the .slider or .readout class still gets a sane
   layout instead of a half-width slider. */
.control > .control-label,
.control > label            { grid-area: 1 / 1 / 2 / 2; }
.control > .readout,
.control > output           { grid-area: 1 / 2 / 2 / 3; justify-self: end; }
.control > .slider,
.control > input[type="range"] { grid-area: 2 / 1 / 3 / 3; }

@media (min-width: 560px) {
  /* Inline on anything wider than a phone: it reads as a rack of gauges. */
  .control {
    grid-template-columns: minmax(6rem, max-content) minmax(0, 1fr) minmax(3.5rem, max-content);
    grid-template-rows: auto;
  }
  .control > .control-label,
  .control > label            { grid-area: 1 / 1 / 2 / 2; }
  .control > .slider,
  .control > input[type="range"] { grid-area: 1 / 2 / 2 / 3; }
  .control > .readout,
  .control > output           { grid-area: 1 / 3 / 2 / 4; }
}

.control-label {
  font-family: var(--sans);
  font-size: var(--t-13);
  font-weight: 500;
  line-height: 1.3;
  color: var(--ink-2);
}

/* The unit, printed after the label in the machine's register. */
.control-label .unit {
  font-family: var(--mono);
  font-size: var(--t-13);
  color: var(--ink-3);
  font-weight: 400;
}

/* -- the readout ----------------------------------------------------- */
.readout {
  font-family: var(--mono);
  font-size: var(--t-13);
  line-height: 1.4;
  color: var(--ink);
  font-variant-numeric: tabular-nums slashed-zero;
  font-variant-ligatures: none;
  white-space: nowrap;
}
.readout--lg { font-size: var(--t-17); }
.readout--dim { color: var(--ink-3); }

/* -- range ------------------------------------------------------------ */
.slider,
input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  min-width: 0;
  height: 22px;            /* the hit area, not the visible track */
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: grab;
  touch-action: pan-y;     /* the browser still scrolls; the thumb drags */
}
.slider:active,
input[type="range"]:active { cursor: grabbing; }

/* WebKit / Blink */
input[type="range"]::-webkit-slider-runnable-track {
  height: 2px;
  background: var(--rule);
  border: 0;
  border-radius: 1px;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 22px;
  height: 22px;
  margin-top: -10px;       /* (2 - 22) / 2, to centre on the 2px track */
  border: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
              var(--pen) 0 6.6px, transparent 7.2px);
  cursor: grab;
}
input[type="range"]:active::-webkit-slider-thumb { cursor: grabbing; }

/* A halo on hover/focus so the handle announces itself without moving. */
input[type="range"]:hover::-webkit-slider-thumb,
input[type="range"]:focus-visible::-webkit-slider-thumb {
  background:
    radial-gradient(circle at 50% 50%, var(--pen) 0 6.6px, transparent 7.2px),
    radial-gradient(circle at 50% 50%, var(--pen-wash) 0 10.5px, transparent 11px);
}

/* Gecko */
input[type="range"]::-moz-range-track {
  height: 2px;
  background: var(--rule);
  border: 0;
  border-radius: 1px;
}
input[type="range"]::-moz-range-thumb {
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
              var(--pen) 0 6.6px, transparent 7.2px);
  cursor: grab;
}
input[type="range"]:hover::-moz-range-thumb,
input[type="range"]:focus-visible::-moz-range-thumb {
  background:
    radial-gradient(circle at 50% 50%, var(--pen) 0 6.6px, transparent 7.2px),
    radial-gradient(circle at 50% 50%, var(--pen-wash) 0 10.5px, transparent 11px);
}
input[type="range"]::-moz-focus-outer { border: 0; }

input[type="range"]:focus-visible {
  outline: 2px solid var(--pen);
  outline-offset: 4px;
  border-radius: 11px;
}

input[type="range"]:disabled { cursor: not-allowed; opacity: 0.45; }

/* -- buttons ---------------------------------------------------------- */
.btns {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.btn,
button {
  font-family: var(--sans);
  font-size: var(--t-13);
  font-weight: 500;
  line-height: 1;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 9px 14px;
  cursor: pointer;
  transition: border-color 120ms linear, color 120ms linear;
  -webkit-appearance: none;
  appearance: none;
}
.btn:hover,
button:hover { border-color: var(--pen); color: var(--pen); }
.btn:active,
button:active { background: var(--pen-wash); }
.btn[aria-pressed="true"],
.btn.is-on {
  border-color: var(--pen);
  color: var(--pen);
  background: var(--pen-wash);
}
.btn:disabled,
button:disabled {
  cursor: not-allowed;
  color: var(--ink-3);
  border-color: var(--rule);
  background: transparent;
}

/* On --paper (outside a card) the chip needs its own ground to read. */
.col > .btns .btn { background: var(--card); }

/* -- checkbox / radio rows -------------------------------------------- */
.check {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--sans);
  font-size: var(--t-13);
  color: var(--ink-2);
  cursor: pointer;
}
.check input[type="checkbox"],
.check input[type="radio"] { accent-color: var(--pen); width: 15px; height: 15px; margin: 0; }

/* -- verdicts ---------------------------------------------------------
   A verdict is ALWAYS the word, printed in mono. Colour is a second,
   redundant channel -- never the only one. */
.verdict {
  display: inline-block;
  font-family: var(--mono);
  font-size: var(--t-13);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums slashed-zero;
  color: var(--ink-2);
}
.v-good { color: var(--green-ink); }
.v-warn { color: var(--amber-ink); }
.v-bad  { color: var(--red-pencil); }

/* A verdict given a ground, for the one place per figure it earns it. */
.verdict--block {
  padding: 6px 10px;
  border-radius: var(--radius);
  background: transparent;
}
.verdict--block.v-bad  { background: var(--red-wash); }
.verdict--block.v-good { background: transparent; box-shadow: inset 0 0 0 1px var(--green-ink); }
.verdict--block.v-warn { background: transparent; box-shadow: inset 0 0 0 1px var(--amber-ink); }

/* -- legend / key -----------------------------------------------------
   Names the marks on the canvas beside it, in the same colours, so the
   semantic rule (pencil = yours, ink = the machine's, red = the grade)
   is legible without a paragraph explaining it. */
.key {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  font-family: var(--sans);
  font-size: var(--t-13);
  color: var(--ink-2);
}
.key-item { display: inline-flex; align-items: center; gap: 8px; }
.key-swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: var(--ink-3);
}
.key-swatch--pen      { background: var(--pen); }
.key-swatch--wash     { background: var(--pen-wash); box-shadow: inset 0 0 0 1px var(--rule); }
.key-swatch--graphite { background: var(--graphite); }
.key-swatch--red      { background: var(--red-pencil); }
.key-swatch--green    { background: var(--green-ink); }
.key-swatch--amber    { background: var(--amber-ink); }
/* A line sample rather than a dot, for reference curves. */
.key-swatch--line { width: 16px; height: 2px; border-radius: 0; }
.key-swatch--dash {
  width: 16px; height: 0; border-radius: 0; background: none;
  border-top: 1.5px dashed var(--ink-3);
}


/* =====================================================================
   9  THE DISCLOSURE PANEL
   ---------------------------------------------------------------------
   A <details> that opens onto real source. The summary must read as a
   CONTROL -- a thing you operate -- not as a link. It gets a boxed +/-
   glyph and sans label typography, and the default triangle marker is
   removed on both engines.
   ===================================================================== */

.panel {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  margin: var(--gap-fig) 0;
}
.panel + .panel { border-top: 0; margin-top: 0; }

.panel > summary {
  display: flex;
  align-items: center;
  gap: 12px;
  list-style: none;
  cursor: pointer;
  padding: 14px 0;
  font-family: var(--sans);
  font-size: var(--t-13);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-2);
  transition: color 120ms linear;
}
.panel > summary::-webkit-details-marker { display: none; }
.panel > summary::marker { content: ""; }

/* The boxed glyph: this is what makes it read as a switch. */
.panel > summary::before {
  content: "+";
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  display: inline-grid;
  place-items: center;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 400;
  line-height: 1;
  color: var(--ink-2);
  transition: border-color 120ms linear, color 120ms linear;
}
.panel[open] > summary::before { content: "\2212"; }  /* true minus sign */

.panel > summary:hover,
.panel > summary:focus-visible { color: var(--pen); }
.panel > summary:hover::before,
.panel > summary:focus-visible::before { border-color: var(--pen); color: var(--pen); }
.panel > summary:focus-visible { outline: 2px solid var(--pen); outline-offset: 2px; }

/* A right-aligned hint on the summary, e.g. the file name. */
.panel > summary .summary-note {
  margin-left: auto;
  font-family: var(--mono);
  font-size: var(--t-13);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-3);
}

/* The body: inset on --paper so the source sits BELOW the card surface,
   the opposite of the figure's luminance lift. Reads as a well. */
.panel-body {
  background: var(--paper);
  border-left: 2px solid var(--rule);
  padding: 20px 22px 22px;
  margin: 0 0 18px;
  border-radius: 0 var(--radius) var(--radius) 0;
}
.panel-body > * + * { margin-top: 14px; }

.panel-body pre,
pre.code {
  font-family: var(--mono);
  font-size: var(--t-13);
  line-height: 1.75;
  color: var(--ink-2);
  margin: 0;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  tab-size: 2;
  font-variant-ligatures: none;
  white-space: pre;
}
.panel-body pre code { font-size: inherit; color: inherit; background: none; padding: 0; }

/* A comment line inside a listing, if the author marks one up. */
.panel-body .c { color: var(--ink-3); }
/* A line the prose is pointing AT. */
.panel-body .hl { color: var(--ink); background: var(--pen-wash); }

/* Prose inside the panel keeps the serif register so an explanation
   still reads as a human talking. */
.panel-body p {
  font-family: var(--serif);
  font-size: var(--t-17);
  line-height: 1.6;
  color: var(--ink-2);
  max-width: var(--measure);
}


/* =====================================================================
   10  PROSE DETAILS
   ---------------------------------------------------------------------
   The .correction class is the hinge between the prose and the figures:
   the word "overconfident" in a sentence is set in the SAME red pencil as
   the mark that names it on the canvas beside it, so the reader connects
   them without a caption doing it for them.
   ===================================================================== */

a {
  color: var(--pen);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.12em;
  text-decoration-skip-ink: auto;
}
a:hover { text-decoration-thickness: 2px; }
a:visited { color: var(--pen); }

/* CORRECTIONS ONLY. Never decorative. */
.correction {
  color: var(--red-pencil);
  text-decoration: underline dotted var(--red-pencil);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.16em;
}

/* The two other in-prose marks, matched to their canvas roles. */
.ink   { color: var(--pen); }
.pencil { color: var(--graphite); }

strong, b { font-weight: 700; color: var(--ink); }
em, i { font-style: italic; }

/* Inline machine text in running prose. Slightly down-scaled because
   mono runs large next to a serif at the same nominal size. */
code, kbd, samp {
  font-family: var(--mono);
  font-size: 0.85em;
  font-variant-ligatures: none;
  color: var(--ink-2);
  word-break: break-word;
}
kbd {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 1px 5px;
  font-size: 0.8em;
  color: var(--ink-2);
}

blockquote {
  border-left: 2px solid var(--rule);
  padding-left: 20px;
  color: var(--ink-2);
  font-style: normal;
}
blockquote cite {
  display: block;
  margin-top: 10px;
  font-family: var(--sans);
  font-size: var(--t-13);
  font-style: normal;
  color: var(--ink-3);
}

ul, ol { padding-left: 1.15em; }
li + li { margin-top: 0.4em; }
li::marker { color: var(--ink-3); }

/* A definition list is the natural shape for a parameter table. */
dl.defs { display: grid; grid-template-columns: max-content 1fr; gap: 8px 20px; }
dl.defs dt {
  font-family: var(--mono);
  font-size: var(--t-13);
  color: var(--ink);
  font-variant-numeric: tabular-nums slashed-zero;
}
dl.defs dd {
  margin: 0;
  font-family: var(--sans);
  font-size: var(--t-15);
  color: var(--ink-2);
}

/* Footnote-ish superscripts that do not wreck the line box. */
sup, sub { font-size: 0.68em; line-height: 0; }


/* =====================================================================
   11  TABLES
   ===================================================================== */
.tbl {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--mono);
  font-size: var(--t-13);
  line-height: 1.5;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums slashed-zero;
}
.tbl th {
  font-family: var(--sans);
  font-size: var(--t-11);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-2);
  text-align: left;
  padding: 0 16px 8px 0;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}
.tbl td {
  padding: 7px 16px 7px 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: baseline;
}
.tbl tr:last-child td { border-bottom: 0; }
.tbl .num { text-align: right; padding-right: 0; }
.tbl-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* .verdict is display:inline-block, which would silently destroy a table
   row if an author put it straight onto a <td>. Prefer wrapping the word
   in a <span class="verdict"> inside the cell; this is the safety net for
   when someone doesn't. */
.tbl td.verdict, .tbl th.verdict { display: table-cell; }


/* =====================================================================
   12  UTILITIES
   ===================================================================== */
.muted { color: var(--ink-3); }
.ink-2 { color: var(--ink-2); }
.measure { max-width: var(--measure); }
.nowrap { white-space: nowrap; }
.stack-8  > * + * { margin-top: 8px; }
.stack-16 > * + * { margin-top: 16px; }
.stack-24 > * + * { margin-top: 24px; }
.mt-0 { margin-top: 0 !important; }
.mb-0 { margin-bottom: 0 !important; }

/* For a label that must exist for a screen reader and nowhere else. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

[hidden] { display: none !important; }


/* =====================================================================
   13  RESPONSIVE
   ---------------------------------------------------------------------
   Only two real breakpoints do structural work: 1180px opens the margin
   rail, and 640px is where the page becomes a phone. Everything between
   is handled by the measure tokens and the grid, not by more queries.
   ===================================================================== */

@media (max-width: 900px) {
  :root { --pad-card: 24px; }
}

@media (max-width: 640px) {
  :root {
    --gap-section: 56px;
    --gap-fig: 32px;
    --pad-card: 20px;
    --pad-x: 1.25rem;
  }
  body { font-size: var(--t-17); line-height: 1.6; }
  .lede { font-size: var(--t-19); }
  h2 { font-size: var(--t-27); }
  h3, .fig-title { font-size: var(--t-23); }
  h4 { font-size: var(--t-19); }
  .masthead { padding-top: 56px; }
  .fig canvas,
  canvas[data-demo] { aspect-ratio: 4 / 3; min-height: 13rem; }
  /* A 2:1 letterbox is unreadable on a phone; relax it to 3:2. */
  canvas.ar-2x1 { aspect-ratio: 3 / 2; }
  canvas.ar-4x5 { min-height: 17rem; }
}

/* 360px: the floor. Nothing may overflow horizontally here. */
@media (max-width: 400px) {
  :root { --pad-x: 1rem; --pad-card: 16px; }
  .fig-num { letter-spacing: 0.1em; }
  .panel-body { padding: 16px 14px 18px; }
  .btn, button { padding: 9px 11px; }
  .key { gap: 6px 14px; }
}

/* Nothing on this page is allowed to cause a horizontal scrollbar. */
body { overflow-x: hidden; }


/* =====================================================================
   14  PRINT
   ---------------------------------------------------------------------
   A notebook should photocopy. Ink goes black on white, the rail folds
   inline, interactive controls disappear (they are meaningless on paper)
   and canvases print whatever frame they are showing.
   ===================================================================== */
@media print {
  :root {
    --paper: #FFFFFF;
    --card: #FFFFFF;
    --rule: #CCCCCC;
    --ink: #000000;
    --ink-2: #333333;
    --ink-3: #555555;
    --pen: #1F4FA8;
    --pen-wash: #EEF2FB;
    --gap-section: 28pt;
    --gap-fig: 16pt;
    --pad-card: 0;
  }

  body {
    background: #FFFFFF;
    color: #000000;
    font-size: 10.5pt;
    line-height: 1.5;
  }

  /* The rail folds inline: the marginalia is the point, keep it. */
  .leaf {
    display: block !important;
    padding-inline: 0;
  }
  .rail {
    border-left: 1pt solid var(--rule);
    padding-left: 8pt;
    padding-top: 0;
    margin: 0 0 10pt;
    max-width: none;
    font-size: 8pt;
  }
  .col > p, .col > ul, .col > ol, .col > blockquote, .col > .lede,
  .col > h2, .col > h3, .col > h4,
  .col > .figure, .col > .panel { max-width: none; }

  .fig { padding: 0; }
  .figure, .panel, .fig, .tbl { break-inside: avoid; page-break-inside: avoid; }
  h1, h2, h3, .fig-title { break-after: avoid; page-break-after: avoid; }

  /* Instruments do nothing on paper. */
  .controls, .btns, .btn, button, input, .hint, .skip { display: none !important; }

  /* Canvases keep their last frame, but must not run off the page. */
  .fig canvas, canvas[data-demo] { max-height: 3.6in; }

  /* Print the destination of a real link; leave anchors alone. */
  a { color: #000; text-decoration: underline; }
  a[href^="http"]::after {
    content: " <" attr(href) ">";
    font-family: var(--mono);
    font-size: 8pt;
    color: #555;
    word-break: break-all;
  }

  /* An open panel prints its source; a closed one prints only its label. */
  .panel-body { border-left: 1pt solid var(--rule); padding: 8pt 10pt; background: #fff; }
  .panel > summary::before { display: none; }

  .site-footer { padding-bottom: 0; }
}


/* =====================================================================
   15  REDUCED MOTION
   ---------------------------------------------------------------------
   Kills every transition and animation in the stylesheet. boot.js handles
   the canvases separately -- it draws one honest frame and never starts
   the rAF loop -- so between the two there is no motion left on the page.
   ===================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   FIGURE-SPECIFIC
   --------------------------------------------------------------------------
   The four figure modules build DOM of their own, so their class names have
   to be honoured here. Everything below is layout and register only: the
   palette, the type scale and the control styling all come from above.
   ========================================================================== */

/* --- small bits the page uses inline ------------------------------------ */
/* Text, not decoration: --rule against --card is about 1.3:1. */
.sep            { color: var(--ink-3); padding: 0 .4ch; }
.unit           { color: var(--ink-3); font-weight: 400; text-transform: none;
                  letter-spacing: 0; }

/* The found artifact. Pulled a little left so the first letter sits on the
   text column edge rather than the glyph side bearing. */
.found          { margin: 2rem 0; margin-left: -1ch; color: var(--ink);
                  overflow-x: auto; }

.reserved       { margin: 2.5rem 0 0; padding-left: 1.25rem;
                  border-left: 2px solid var(--pen); }
.reserved p     { font-family: var(--serif); font-size: 1.4375rem; line-height: 1.5;
                  margin: 0; }
.reserved cite  { display: block; margin-top: .5rem; font-family: var(--sans);
                  font-size: .8125rem; font-style: normal; color: var(--ink-3);
                  letter-spacing: .06em; text-transform: uppercase; }

/* --- readout strips ------------------------------------------------------ */
/* One row of live numbers under a figure. These change many times a second,
   so the strip is aria-hidden and a throttled live region carries a sentence
   instead. */
.fig-readout    { display: flex; flex-wrap: wrap; gap: .5rem 1.75rem;
                  font-family: var(--mono); font-size: .8125rem;
                  font-variant-numeric: tabular-nums slashed-zero;
                  color: var(--ink-2); }
.ro             { white-space: nowrap; }
.ro b           { font-weight: 400; color: var(--ink-3); }
.ro b::after    { content: " "; }
.ro--note       { color: var(--ink-3); white-space: normal; }
.ro--seed       { color: var(--ink-3); margin-left: auto; }

/* The modules emit these states and set no colour themselves, so this is the
   only place the hues live. The word is always printed too, so colour is
   never the only signal. */
[data-state="ok"]      { color: var(--pen); }
[data-state="neutral"] { color: var(--ink-2); }
[data-state="warn"]    { color: var(--amber-ink); }
[data-state="bad"],
[data-state="reject"]  { color: var(--red-pencil); }
[data-state="survive"] { color: var(--ink); }
[data-state="nosearch"]{ color: var(--amber-ink); }

/* --- FIG 1, calibration --------------------------------------------------- */
.calib-card     { margin-top: 1.5rem; }

.calib-meta     { display: flex; flex-wrap: wrap; gap: 1rem; margin: 0 0 .75rem;
                  font-family: var(--sans); font-size: .6875rem; font-weight: 600;
                  letter-spacing: .08em; text-transform: uppercase;
                  color: var(--ink-3); }
#calib-tag      { color: var(--pen); }
#calib-repeat   { color: var(--amber-ink); }

.calib-claim    { font-family: var(--serif); font-size: 1.4375rem; line-height: 1.5;
                  margin: 0 0 1.25rem; max-width: 34rem; min-height: 3.5rem; }

.calib-resolution { margin: 0 0 1.25rem; padding: 1rem 1.25rem;
                  background: var(--paper); border-left: 2px solid var(--rule); }
#calib-outcome  { font-family: var(--mono); font-size: .8125rem; font-weight: 700;
                  letter-spacing: .06em; }
#calib-truth    { font-family: var(--mono); font-size: .8125rem; color: var(--ink-3);
                  margin-left: 1ch; }
#calib-why      { font-family: var(--serif); font-size: .9375rem; line-height: 1.55;
                  color: var(--ink-2); margin: .6rem 0 0; }

.calib-answer   { display: flex; gap: .5rem; margin-bottom: 1rem; }
.calib-answer button { font-family: var(--mono); font-size: .8125rem;
                  letter-spacing: .08em; min-width: 6.5rem; }
.calib-answer button[aria-pressed="true"] { border-color: var(--pen);
                  color: var(--pen); background: var(--pen-wash); }

.calib-conf     { max-width: 26rem; }

.calib-readouts { display: flex; flex-wrap: wrap; gap: .75rem 2rem;
                  margin: 1.5rem 0 0; padding-top: 1.25rem;
                  border-top: 1px solid var(--rule); }
.calib-readouts > div { display: flex; flex-direction: column; gap: .15rem; }
.calib-readouts dt { font-family: var(--sans); font-size: .6875rem; font-weight: 600;
                  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); }
.calib-readouts dd { margin: 0; font-family: var(--mono); font-size: 1.0625rem;
                  font-variant-numeric: tabular-nums slashed-zero; color: var(--ink); }
#calib-verdict  { font-size: .9375rem; letter-spacing: .04em; }

@media (min-width: 700px) {
  #calib-brier, #calib-bss { font-size: 1.6875rem; }
}

/* --- FIG 2, the linter ---------------------------------------------------- */
canvas.lint-gauge { aspect-ratio: auto; height: 104px; min-height: 84px; }

.lint           { margin-top: 1.25rem; }
.lint-label     { display: block; margin-bottom: .5rem; }

.lint-input     { display: block; width: 100%; resize: vertical;
                  font-family: var(--mono); font-size: 1.0625rem; line-height: 1.55;
                  color: var(--ink); background: var(--paper);
                  border: 1px solid var(--rule); border-radius: 2px;
                  padding: .75rem .875rem; }
.lint-input:focus-visible { outline: 2px solid var(--pen); outline-offset: 1px;
                  border-color: var(--pen); }

.lint-egs       { margin-top: .75rem; }
.lint-eg        { font-family: var(--serif); font-size: .9375rem; text-transform: none;
                  letter-spacing: 0; text-align: left; max-width: 22rem; }
.lint-eg--clear { font-family: var(--sans); font-size: .8125rem; color: var(--ink-3); }

/* The sentence echoed back with its offending spans marked. */
.lint-echo      { margin: 1.25rem 0 .75rem; font-family: var(--mono);
                  font-size: 1.0625rem; line-height: 1.7; color: var(--ink-2);
                  word-break: break-word; }
.lint-echo[data-empty] { color: var(--ink-3); font-style: italic; }
.lint-hit       { color: var(--red-pencil);
                  text-decoration: underline wavy var(--red-pencil);
                  text-underline-offset: .22em; }
.lint-ok        { color: var(--pen);
                  text-decoration: underline solid var(--pen);
                  text-underline-offset: .22em; }

/* The violation list, laid out like real linter output. */
.lint-out       { font-family: var(--mono); font-size: .8125rem; line-height: 1.5;
                  font-variant-numeric: tabular-nums slashed-zero; }
.lint-row       { display: grid; grid-template-columns: 5.5rem 4rem 10rem 1fr;
                  gap: .25rem .75rem; padding: .3rem 0;
                  border-top: 1px solid var(--rule); align-items: baseline; }
.lint-row:first-child { border-top: 0; }
.lint-verdict   { color: var(--red-pencil); font-weight: 700; letter-spacing: .04em; }
.lint-loc       { color: var(--ink-3); }
.lint-code      { color: var(--ink-2); }
.lint-row--hard .lint-code { color: var(--red-pencil); }
.lint-msg       { display: flex; flex-wrap: wrap; gap: .5ch; color: var(--ink-2); }
.lint-quote     { color: var(--ink); }
.lint-note      { color: var(--ink-3); }
.lint-row--ok   { border-top-color: var(--pen-wash); }
.lint-row--ok .lint-verdict { color: var(--pen); }
.lint-row--idle .lint-verdict { color: var(--ink-3); }
.lint-row--more { display: block; color: var(--ink-3); }

@media (max-width: 620px) {
  .lint-row     { grid-template-columns: 1fr; gap: .1rem; padding: .5rem 0; }
  .lint-loc     { display: none; }
}

/* --- shared figure controls ----------------------------------------------- */
/* The seed is a number field wearing the readout's clothes. */
input.readout[type="number"] { width: 6rem; padding: .2rem .4rem;
                  background: var(--paper); border: 1px solid var(--rule);
                  border-radius: 2px; color: var(--ink); }
input.readout[type="number"]:focus-visible { outline: 2px solid var(--pen);
                  outline-offset: 1px; }

/* --- print ---------------------------------------------------------------- */
/* Controls are useless on paper; the figures themselves still print. */
@media print {
  .lint-input, .lint-egs, .calib-answer, .calib-actions { display: none; }
}

/* The idle row repeats what the gauge canvas already prints, so it is kept
   for assistive tech and hidden from sight rather than shown twice. */
.lint-row--idle { position: absolute; width: 1px; height: 1px; padding: 0;
                  margin: -1px; overflow: hidden; clip-path: inset(50%);
                  white-space: nowrap; border: 0; }
