/* ── SPECIMEN ──────────────────────────────────────────────────────────────
   One variable face, every axis live. Recursive carries the whole page: its
   MONO axis is the small-label voice, its wght/slnt/CASL axes are driven by the
   actual readings rather than chosen by hand.

   Hierarchy comes from scale alone — no cards, no borders, no shadows, no rules
   except hairline baselines. One acid field owns the markets whole; everything
   else is ink on near-white.
   ──────────────────────────────────────────────────────────────────────── */

@font-face {
  font-family: 'Recursive';
  src: url(../fonts/recursive.woff2) format('woff2');
  font-weight: 300 1000;
  font-style: oblique 0deg 15deg;
  font-display: swap;
}

/* ── themes ────────────────────────────────────────────────────────────────
   Two selected themes, not one theme and its inversion. Dark takes its own
   steps against its own surface and every one was measured there: the live
   sweep holds 7.08:1 at its worst (light holds 7.1), the lab trio clears the
   full all-pairs validator on the dark surface, and the acid steps down from
   #d8f000 to #c2d900 so a full-bleed field does not sear at night while still
   carrying ink at 11.9:1.

   Scoping follows the three-block rule so a stated preference always wins:
   bare :root is light, the media query covers the OS setting but yields to an
   explicit light stamp, and [data-theme] beats both. Only VALUES are restated
   below — never a rule, never a component. */

:root {
  color-scheme: light;

  --paper: #faf9f7;
  --ink:   #0d0d0d;
  --ink-2: #55534e;
  --ink-3: #63605a;   /* 5.96:1 on the paper — the small mono labels are real text */
  --acid:  #d8f000;
  --hair:  rgba(13, 13, 13, 0.13);
  --wash:  rgba(13, 13, 13, 0.07);
  --rule-soft: rgba(13, 13, 13, 0.22);
  --rule-firm: rgba(13, 13, 13, 0.45);

  /* the live hue's lightness and chroma are theme steps; the hue itself is data */
  --live-l: 0.45;
  --live-c: 0.14;

  /* ── colour ────────────────────────────────────────────────────────────
     Three roles, no loose swatches.

     1. LIVE — the temperature's own hue, written by the page. It extends the
        Live Axis Rule from the letterform to its colour: cold reads deep blue,
        temperate purple, hot ember red. Diverging blue→magenta→red rather than
        a thermal rainbow, so it never passes through green or yellow. Lightness
        and chroma are held constant, which keeps every step of the sweep between
        7.1:1 and 7.7:1 on this paper — the figure cannot fade in daylight
        whatever the weather does.
     2. LAB — categorical identity in the chart, three slots plus neutral.
     3. ACID — unchanged; still exactly one field.
     Market direction is deliberately NOT a colour: it lives on the acid ground
     where neither red nor green survives, so it is carried by a drawn arrow. */
  --live-h: 300;
  --live:       oklch(var(--live-l) var(--live-c) var(--live-h));
  /* the same hue, thinned — the coming day is ground, the current reading is
     figure, and alpha is what keeps that order without a second colour */
  --live-ghost: oklch(var(--live-l) var(--live-c) var(--live-h) / var(--ghost-a));
  --live-peak:  oklch(var(--live-l) var(--live-c) var(--live-h) / var(--peak-a));
  --ghost-a: 0.12;
  --peak-a:  0.30;

  /* Slots 1–3 of the validated categorical palette. The bars are ranked by
     score, so any two labs can end up adjacent — that is the all-pairs case,
     which the full eight-slot set cannot clear. Three can: worst all-pairs CVD
     ΔE 8.4, normal-vision 21.6, every slot over 3:1 on this paper. Past three,
     labs fold into neutral rather than inventing a fourth hue. */
  --lab-1: #2a78d6;
  --lab-2: #eb6834;
  --lab-3: #199e70;
  --lab-0: #8f8c85;   /* Other — recessive by design */

  /* The neutral instance the page holds until a live reading arrives. Starting
     at a plain regular is what makes the tuning visible: the move to the real
     values is the one authored moment on the surface. */
  --w: 400;
  --s: 0;
  --c: 0;

  /* the spread the glyph grid maps scores onto */
  --wlo: 300;
  --whi: 1000;

  --face: 'Recursive', ui-sans-serif, system-ui, sans-serif;
  --gut: clamp(18px, 4vw, 56px);
  --max: 1180px;
}

/* dark, as a set of steps measured on #12110f */
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --paper: #12110f;
    --ink:   #f2f0ea;   /* 16.6:1 */
    --ink-2: #b8b5ac;   /*  9.2:1 */
    --ink-3: #928f86;   /*  5.8:1 */
    --acid:  #c2d900;   /* ink on it: 11.9:1 */
    --hair:  rgba(242, 240, 234, 0.16);
    --wash:  rgba(242, 240, 234, 0.09);
    --rule-soft: rgba(242, 240, 234, 0.26);
    --rule-firm: rgba(242, 240, 234, 0.50);
    --live-l: 0.72;     /* worst step of the sweep: 7.08:1 */
    --live-c: 0.15;
    --ghost-a: 0.16;    /* a thinned light hue needs more alpha to read as ground */
    --peak-a:  0.38;
    --lab-1: #3987e5;   /* 5.2:1 · all-pairs validated on this surface */
    --lab-2: #d95926;   /* 4.9:1 */
    --lab-3: #199e70;   /* 5.5:1 */
    --lab-0: #8a8780;   /* 5.3:1 */
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --paper: #12110f;
  --ink:   #f2f0ea;
  --ink-2: #b8b5ac;
  --ink-3: #928f86;
  --acid:  #c2d900;
  --hair:  rgba(242, 240, 234, 0.16);
  --wash:  rgba(242, 240, 234, 0.09);
  --rule-soft: rgba(242, 240, 234, 0.26);
  --rule-firm: rgba(242, 240, 234, 0.50);
  --live-l: 0.72;
  --live-c: 0.15;
  --ghost-a: 0.16;
  --peak-a:  0.38;
  --lab-1: #3987e5;
  --lab-2: #d95926;
  --lab-3: #199e70;
  --lab-0: #8a8780;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scrollbar-color: var(--ink) var(--paper); }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  transition: background-color 0.28s linear, color 0.28s linear;
  font-family: var(--face);
  font-variation-settings: 'MONO' 0, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--acid); color: var(--ink); }
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--paper); }
::-webkit-scrollbar-thumb { background: var(--ink); }
:focus-visible { outline: 2px solid var(--live); outline-offset: 3px; }
.field :focus-visible { outline-color: #12110f; }

a { color: inherit; text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { text-decoration-thickness: 2px; }

.in { max-width: var(--max); margin: 0 auto; padding-inline: var(--gut); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── icons ────────────────────────────────────────────────────────────────
   One hand: 24px box, 1.75 stroke, round caps and joins, never filled, always
   currentColor. Sized in em so a glyph always matches the text it sits beside. */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.ic {
  width: 1em; height: 1em; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.75;
  stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.ic--wx  { width: 1.5em; height: 1.5em; color: var(--live); }
.ic--dir { width: 0.95em; height: 0.95em; stroke-width: 2.1; }

/* the small voice: Recursive's own monospace axis, never a second family */
.mono, .head-meta, .axes, .field-meta, .grid-meta, .q-meta, .q-range-cap,
.tool-l, .tool-v, .g-score, .g-lab, .g-n, .grid-foot, .colo, .field-note {
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
  font-feature-settings: 'ss01';
  letter-spacing: 0.02em;
}

/* ── head ─────────────────────────────────────────────────────────────── */

.head .in {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px 16px; flex-wrap: wrap;
  padding-block: 22px 0;
}
.head-meta { align-self: baseline; }

/* the theme control speaks the page's existing switch language rather than
   inventing a second one; icon-only, with the label carried for screen readers */
.theme { display: flex; gap: 0; margin-left: auto; }
.tog--ic { padding: 6px 9px; }
.tog--ic .ic { width: 14px; height: 14px; }
.theme .tog--ic + .tog--ic { margin-left: -1px; }
.mark {
  font-size: 15px; font-weight: 800; letter-spacing: -0.02em;
  text-decoration: none;
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0;
}
.mark:hover { color: var(--ink-2); }
.head-meta {
  display: flex; gap: 16px; margin: 0;
  font-size: 11px; color: var(--ink-3); text-transform: uppercase;
  letter-spacing: 0.14em;
}
.head-meta time { color: var(--ink); font-variant-numeric: tabular-nums; }

/* ── the specimen ─────────────────────────────────────────────────────── */

.spec .in { padding-block: clamp(18px, 3vw, 40px) clamp(28px, 5vw, 64px); }

/* ── the hour field ───────────────────────────────────────────────────────
   The forecast standing behind the reading. It fills the space the figure was
   already leaving empty and answers the question the number cannot: where the
   day is going. Columns are the live hue at 12% — light enough that the figure
   keeps its full contrast over them, present enough to read as a shape.
   The peak takes 30% and is the only emphasis; no second colour is introduced. */

.spec-stage { position: relative; }
/* the island is clipped by its own stage: a scaled node with nothing to clip it
   spills over the condition line and the whole section beneath */
.spec-stage { overflow: hidden; }
.spec-zoom { position: relative; }

.hours {
  position: absolute; inset: 0; z-index: 0;
  display: grid; grid-template-columns: repeat(24, minmax(0, 1fr));
  align-items: end; gap: clamp(2px, 0.5vw, 5px);
  padding-bottom: 0.1em;
}
.hours i {
  display: block; min-height: 2px; height: var(--h, 10%);
  background: var(--live-ghost);
  transform-origin: bottom center;
  transition: background-color 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}
.hours i[data-peak] { background: var(--live-peak); }
.hours i[data-on]   { background: var(--live-peak); }

/* the figure sits over the field but must not shield it: it is a reading, not a
   control, so pointer events belong to the hours behind it */
.spec-glyph { position: relative; z-index: 1; pointer-events: none; }

.hours-cap {
  margin: 6px 0 0; min-height: 1.4em;
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-3);
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
}
.hours-cap b { font-weight: 700; color: var(--ink-2); }

/* The world's grammar is enormous display against tiny labels, so this
   deliberately passes the usual display ceiling: at 6rem the axes stop being
   legible as letterform and the direction has nothing left to show. */
.spec-glyph {
  margin: 0;
  font-size: clamp(7rem, 34vw, 26rem);
  line-height: 0.78;
  letter-spacing: -0.055em;
  font-variant-numeric: lining-nums tabular-nums;
  font-variation-settings: 'MONO' 0, 'CASL' var(--c), 'slnt' var(--s), 'CRSV' 0;
  font-weight: var(--w);
  color: var(--live);
  /* the focal moment — long enough to read as a tuning, not a jump. The hue
     travels with the axes, so the figure warms and thickens as one act. */
  transition: font-weight 0.9s cubic-bezier(0.16, 1, 0.3, 1),
              font-variation-settings 0.9s cubic-bezier(0.16, 1, 0.3, 1),
              color 0.9s cubic-bezier(0.16, 1, 0.3, 1);
  will-change: font-variation-settings;
}
/* the readout is part of the same act, so it holds its own width while it counts */
.axes dd { font-variant-numeric: tabular-nums lining-nums; }
/* A degree sign, not a second glyph: same ink, set on the cap line of the
   figure so the reading stays one object. Lifted higher or tinted grey it
   detaches and reads as a stray mark floating beside the number. */
.spec-deg {
  font-size: 0.3em; font-weight: 500; color: var(--live);
  letter-spacing: 0; vertical-align: 1.35em; margin-left: 0.04em;
}

.spec-foot {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: clamp(20px, 4vw, 60px); flex-wrap: wrap;
  margin-top: clamp(14px, 2.4vw, 26px);
  padding-top: 14px;
  border-top: 1px solid var(--hair);
}
.spec-cond {
  display: flex; align-items: baseline; gap: 0.5em;
  margin: 0; max-width: 32ch;
  font-size: clamp(1rem, 2.1vw, 1.45rem); font-weight: 500;
  line-height: 1.25; letter-spacing: -0.015em;
}
.axes { display: flex; gap: clamp(16px, 3vw, 40px); margin: 0; flex-wrap: wrap; }
.axes > div { display: flex; flex-direction: column; gap: 1px; }
.axes dt {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.18em; color: var(--ink-3);
}
.axes dd {
  margin: 0; font-size: 15px; font-weight: 700; color: var(--live);
  transition: color 0.9s cubic-bezier(0.16, 1, 0.3, 1);
  font-variant-numeric: tabular-nums;
}
.axes dd.from {
  font-size: 10px; font-weight: 400; color: var(--ink-3);
  text-transform: lowercase; letter-spacing: 0.06em;
}

/* ── the acid field ───────────────────────────────────────────────────── */

/* The field pins its own foreground: the acid ground carries near-black ink in
   BOTH themes, because a dark theme that let --ink go pale here would put
   #f2f0ea on #c2d900 at 1.4:1. A ground with a fixed relationship to its ink is
   the whole reason this world has exactly one field. */
.field { background: var(--acid); color: #12110f; }
.field .ic { color: #12110f; }
.field .in { padding-block: clamp(26px, 4vw, 52px); }
.field ::selection { background: #12110f; color: var(--acid); }

.field-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: clamp(18px, 3vw, 34px);
}
.field-h {
  margin: 0; font-size: 11px; font-weight: 800;
  letter-spacing: 0.28em; text-transform: uppercase;
}
.field-meta { margin: 0; font-size: 11px; letter-spacing: 0.06em; }

.quotes {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3.4vw, 44px);
}
.quote { min-width: 0; }
.q-price {
  margin: 0;
  font-size: clamp(1.9rem, 4.4vw, 3.3rem);
  line-height: 0.95; letter-spacing: -0.045em;
  font-variant-numeric: lining-nums tabular-nums;
  font-weight: var(--qw, 400);
  transition: font-weight 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}
.q-meta {
  display: flex; gap: 10px; align-items: baseline;
  margin: 6px 0 0; font-size: 11px; letter-spacing: 0.06em;
}
.q-sym { font-weight: 700; letter-spacing: 0.14em; }
.q-dir { display: inline-flex; align-items: center; gap: 0.4em; }
/* direction is shape, not colour: this sits on the acid ground, where neither a
   red nor a green would survive, so the arrow is the carrier and the sign backs it */
.quote[data-up="0"] .q-dir > [data-delta] {
  text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px;
}
.q-range {
  position: relative; height: 3px; margin-top: 14px;
  background: rgba(18, 17, 15, 0.22);
}
/* scaled rather than resized: animating width relayouts the quote */
.q-range i {
  position: absolute; inset-block: 0; left: 0; width: 100%;
  background: #12110f;
  transform: scaleX(var(--lvl, 0)); transform-origin: left center;
  transition: transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}
.q-range-cap { margin: 6px 0 0; font-size: 11px; letter-spacing: 0.04em; min-height: 1em; }
.quotes[data-empty="1"] .q-price { opacity: 0.4; font-weight: 300; }
.quotes[data-empty="1"] .q-range i { transform: scaleX(0); }
.field-note {
  margin: clamp(20px, 3vw, 32px) 0 0; max-width: 74ch;
  font-size: 11px; line-height: 1.6;
}

/* ── the glyph grid ───────────────────────────────────────────────────── */

.grid-sec .in { padding-block: clamp(40px, 6vw, 84px) 0; }

.grid-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
}
.grid-h {
  margin: 0; font-size: 11px; font-weight: 800;
  letter-spacing: 0.28em; text-transform: uppercase;
}
.grid-meta { margin: 0; font-size: 11px; color: var(--ink-3); letter-spacing: 0.06em; }
.grid-lede {
  margin: 18px 0 0; max-width: 62ch;
  font-size: clamp(1rem, 1.7vw, 1.22rem); line-height: 1.45;
  letter-spacing: -0.012em; color: var(--ink-2);
}

.tools {
  display: flex; align-items: flex-end; gap: clamp(14px, 2.2vw, 28px);
  flex-wrap: wrap;
  margin-top: clamp(24px, 3.4vw, 40px);
  padding-bottom: 18px;
  border-bottom: 1px solid var(--hair);
}
.tool { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.tool--spread { flex: 0 0 clamp(190px, 24vw, 280px); }
.tool--spread[hidden] { display: none; }
.tool--view { flex: 0 0 auto; }
.tool--view .tool-t { gap: 0; }
/* the two views are one control, so they read as one object rather than two
   unrelated switches sharing a row */
.tool--view .tog + .tog { margin-left: -1px; box-shadow: inset 0 -2px 0 var(--ink), inset 1px 0 0 var(--hair); }
.tool--grow { flex: 1 1 160px; }
.tool-l {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.2em; color: var(--ink-3);
}
.tool-v { font-size: 11px; color: var(--ink-2); font-variant-numeric: tabular-nums; }

/* The axis slider, drawn rather than left to the platform. The thumb keeps the
   hairline look of a specimen's axis marker but carries a 26px transparent
   target around it, so the page's one control is findable by thumb. */
#spread {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 34px; background: none; cursor: ew-resize;
}
#spread::-webkit-slider-runnable-track { height: 2px; background: var(--ink); }
#spread::-moz-range-track { height: 2px; background: var(--ink); }

#spread::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 26px; height: 26px; border: 0; border-radius: 0;
  margin-top: -12px;
  background:
    linear-gradient(to right, transparent 0 44%, var(--ink) 44% 56%, transparent 56% 100%)
    center / 100% 22px no-repeat;
}
#spread::-moz-range-thumb {
  width: 26px; height: 26px; border: 0; border-radius: 0;
  background:
    linear-gradient(to right, transparent 0 44%, var(--ink) 44% 56%, transparent 56% 100%)
    center / 100% 22px no-repeat;
}
#spread:hover::-webkit-slider-thumb,
#spread:active::-webkit-slider-thumb {
  background:
    linear-gradient(to right, transparent 0 44%, var(--ink) 44% 56%, transparent 56% 100%)
      center / 100% 26px no-repeat,
    var(--acid);
}
#spread:hover::-moz-range-thumb, #spread:active::-moz-range-thumb {
  background:
    linear-gradient(to right, transparent 0 44%, var(--ink) 44% 56%, transparent 56% 100%)
      center / 100% 26px no-repeat,
    var(--acid);
}
#spread:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

.tool select, .tool input[type="search"] {
  font-family: var(--face);
  font-variation-settings: 'MONO' 1;
  font-size: 13px; color: var(--ink);
  background: none; border: 0; border-bottom: 1px solid var(--ink);
  border-radius: 0; padding: 5px 0; width: 100%;
  caret-color: var(--ink);
}
.tool select { appearance: none; padding-right: 20px;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink) 50%),
                    linear-gradient(135deg, var(--ink) 50%, transparent 50%);
  background-position: calc(100% - 10px) 13px, calc(100% - 6px) 13px;
  background-size: 4px 4px, 4px 4px; background-repeat: no-repeat;
}
.tool input::placeholder { color: var(--ink-3); }
.tool-t { display: flex; gap: 8px; flex-wrap: wrap; align-self: flex-end; }
/* the specimen has no boxes, so a switch reads as a pressed field of the acid,
   not as a bordered button — but unpressed it still has to look pressable */
.tog {
  display: inline-flex; align-items: center; gap: 0.5em;
  font-family: var(--face); font-variation-settings: 'MONO' 1;
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.16em;
  color: var(--ink); background: none;
  border: 0; box-shadow: inset 0 -2px 0 var(--ink);
  padding: 7px 9px; cursor: pointer;
  transition: background-color 0.14s linear, color 0.14s linear;
}
.tog:hover { background: var(--wash); }
.tog[aria-pressed="true"] {
  color: var(--ink); background: var(--acid);
  box-shadow: inset 0 -2px 0 var(--ink);
}
.tool-clear {
  font-family: var(--face); font-variation-settings: 'MONO' 1;
  font-size: 11px; color: var(--ink-2); background: none;
  border: 0; border-bottom: 1px solid var(--ink-3); padding: 5px 0; cursor: pointer;
}
.tool-clear:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* ── the chart ────────────────────────────────────────────────────────────
   Bars are ink on paper, never acid: chartreuse on near-white measures about
   1.2:1 and a bar nobody can see is not a chart. The acid stays what it already
   is on this page — the colour of a field and of an active state. */

.chart { margin-top: 4px; }

.chart-axis {
  position: relative;
  display: grid;
  grid-template-columns: 40px minmax(0, 1.45fr) 96px minmax(0, 2fr) 62px;
  gap: 18px;
  padding: 14px 0 6px;
}
.ca-lo, .ca-hi, .ca-med {
  grid-column: 4; grid-row: 1;
  font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
  white-space: nowrap;
}
.ca-lo  { justify-self: start; }
.ca-hi  { justify-self: end; }
.ca-med {
  justify-self: start; margin-left: var(--med, 50%);
  transform: translateX(-50%); color: var(--ink-3);
}

/* Identity is never colour-alone: the legend names every slot, and the lab name
   is printed on every row besides. */
.legend {
  list-style: none; display: flex; flex-wrap: wrap; gap: 6px 20px;
  margin: 0; padding: 18px 0 2px;
}
.legend li {
  display: flex; align-items: center; gap: 7px;
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--ink-2);                     /* text wears text tokens, never the series colour */
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
}
.legend i { width: 10px; height: 10px; flex: none; background: var(--lab, var(--lab-0)); }

.bars { list-style: none; margin: 0; padding: 0; }
.bars-empty {
  padding: 40px 0; font-size: 13px; color: var(--ink-3);
  font-variation-settings: 'MONO' 1;
}

.c {
  display: grid;
  grid-template-columns: 40px minmax(0, 1.45fr) 96px minmax(0, 2fr) 62px;
  align-items: center; gap: 18px;
  padding: 6px 0;
}
.c:hover { background: var(--acid); }
.c-n {
  font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums;
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
}
/* In the chart the bar carries the rank, so the name is a label and sits at the
   body step. In the list the name IS the measure — weight is the encoding — so it
   takes the larger title step. Same content, two jobs, both on the ramp. */
.c-name {
  font-size: 16px; letter-spacing: -0.012em; font-weight: 450;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.c-name a { text-decoration: none; }
.c-name a:hover { text-decoration: underline; text-decoration-thickness: 2px; }
.c-lab {
  font-size: 10px; color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.11em;
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.c-bar { position: relative; height: 15px; }
.c-bar i {
  display: block; height: 100%; width: var(--w, 0%);
  background: var(--lab, var(--lab-0));
  clip-path: inset(0 0 0 0);
}
/* the origin. At the bottom of a scaled range the rule is zero length, and this
   is the only thing left marking where it starts. */
.c-bar::before {
  content: ''; position: absolute; left: 0; top: -3px; bottom: -3px;
  width: 1px; background: var(--ink-3);
}
/* the median, drawn through every row so the rows stack it into one line */
.c-bar::after {
  content: ''; position: absolute; left: var(--med, 50%); top: -6px; bottom: -6px;
  width: 1px; background: var(--rule-soft);
}
.c:hover .c-bar::after { background: var(--rule-firm); }

.c-score {
  font-size: 13px; font-weight: 700; text-align: right;
  font-variant-numeric: tabular-nums;
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
}
.c-flags {
  font-size: 10px; color: var(--ink-3); letter-spacing: 0.1em; text-transform: uppercase;
  font-variation-settings: 'MONO' 1;
}

.glyphs { list-style: none; margin: 0; padding: 0; }
.glyphs-empty {
  padding: 40px 0; font-size: 13px; color: var(--ink-3);
  font-variation-settings: 'MONO' 1;
}
.g {
  display: grid;
  /* wide enough for the longest lab plus both flags spelled out; truncating them
     would hide the only place the list states reasoning and open weights */
  grid-template-columns: 40px minmax(0, 1fr) 176px 62px;
  align-items: baseline; gap: 18px;
  padding: 9px 0;
  border-bottom: 1px solid var(--hair);
}
.g:hover { background: var(--acid); }
.g-n { font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* one custom property, thirty rows: the spread slider drives every weight at
   once through CSS, with no per-row scripting */
.g-name {
  font-size: clamp(1rem, 2.1vw, 1.45rem);
  line-height: 1.1; letter-spacing: -0.022em;
  font-weight: calc(var(--wlo) + var(--k, 0) * (var(--whi) - var(--wlo)));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.g-name a { text-decoration: none; }
.g-name a:hover { text-decoration: underline; text-decoration-thickness: 2px; }
/* the list carries the same lab identity as the chart, so switching views never
   changes what a colour means */
.g-lab {
  display: flex; align-items: baseline; gap: 7px;
  font-size: 11px; color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.12em;
  overflow: hidden; white-space: nowrap;
}
.g-lab::before {
  content: ''; flex: none; width: 8px; height: 8px;
  background: var(--lab, var(--lab-0)); transform: translateY(-1px);
}
.g-lab > span { overflow: hidden; text-overflow: ellipsis; }
.g-lab i {
  font-style: normal; color: var(--ink-3); letter-spacing: 0.08em;
}
.g-score {
  font-size: 13px; font-weight: 700; text-align: right;
  font-variant-numeric: tabular-nums;
}
.g-flags { font-size: 10px; color: var(--ink-3); letter-spacing: 0.1em; text-transform: uppercase; }

.grid-foot {
  margin: 22px 0 0; max-width: 74ch;
  font-size: 11px; line-height: 1.7; color: var(--ink-3);
}

/* ── colophon ─────────────────────────────────────────────────────────── */

.colo { margin-top: clamp(48px, 7vw, 96px); border-top: 1px solid var(--hair); }
.colo .in {
  display: flex; justify-content: space-between; gap: 18px; flex-wrap: wrap;
  padding-block: 20px 56px;
  font-size: 11px; color: var(--ink-3); line-height: 1.7;
}
.colo p { margin: 0; max-width: 72ch; }

/* ── narrow ───────────────────────────────────────────────────────────── */

@media (max-width: 860px) {
  .quotes { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .quote { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 4px 14px; }
  .q-price { grid-column: 1; }
  .q-meta  { grid-column: 2; margin: 0; justify-content: flex-end; }
  .q-range, .q-range-cap { grid-column: 1 / -1; }
  .q-range { margin-top: 8px; }

  .spec-foot { flex-direction: column; align-items: flex-start; gap: 16px; }

  .g {
    grid-template-columns: 30px minmax(0, 1fr) 54px;
    grid-template-areas: 'n name score' '. lab lab';
    row-gap: 2px; gap: 12px;
  }
  .g-n     { grid-area: n; }
  .g-name  { grid-area: name; white-space: normal; }
  .g-lab   { grid-area: lab; }
  .g-score { grid-area: score; }

  /* the chart stacks: name and score read first, the bar sits under them at
     full width where its length is actually comparable row to row */
  .c {
    grid-template-columns: 30px minmax(0, 1fr) 54px;
    grid-template-areas: 'n name score' 'n bar bar';
    row-gap: 5px; gap: 12px; padding: 9px 0;
    align-items: center;
  }
  .c-n     { grid-area: n; align-self: start; padding-top: 2px; }
  .c-name  { grid-area: name; white-space: normal; }
  .c-score { grid-area: score; align-self: start; }
  .c-bar   { grid-area: bar; height: 12px; }
  .c-lab, .c-flags { display: none; }

  .chart-axis {
    grid-template-columns: 30px minmax(0, 1fr) 54px;
    gap: 12px; padding-bottom: 8px;
  }
  .ca-lo, .ca-hi, .ca-med { grid-column: 2 / -1; }
  .ca-med { display: none; }
}

/* ── motion ───────────────────────────────────────────────────────────────
   One authored moment: the specimen tunes itself. The page holds a neutral
   instance until the first live reading lands, then interpolates the figure onto
   the real axis values while the readout counts up in step. A weather refresh
   replays the same act, so an update reads as a re-tuning rather than a swap.

   Everything else is acknowledgment: bars wipe when the set changes, weights
   settle when the spread moves, controls answer immediately. No scroll reveals.
   ──────────────────────────────────────────────────────────────────────── */

/* the bars wipe rather than scale, so the bar's end stays a hard edge */
@media (prefers-reduced-motion: no-preference) {
  /* the day rises behind the reading as the reading tunes itself — the same
     curve, so the two land together rather than competing */
  .hours.is-entering i {
    animation: hour-rise 0.55s cubic-bezier(0.16, 1, 0.3, 1) backwards;
    animation-delay: var(--d, 0ms);
  }
  @keyframes hour-rise { from { transform: scaleY(0.04); } }

  .bars.is-entering .c-bar i {
    animation: wipe 0.44s cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: var(--d, 0ms);
  }
  @keyframes wipe {
    from { clip-path: inset(0 100% 0 0); }
    to   { clip-path: inset(0 0 0 0); }
  }

  /* weight settles when the spread axis is remapped, but never mid-drag —
     a transition during a drag reads as lag, not as motion */
  .glyphs:not(.is-dragging) .g-name {
    transition: font-weight 0.55s cubic-bezier(0.16, 1, 0.3, 1);
  }

  .tog, .tool-clear { transition: background-color 0.14s linear, color 0.14s linear; }
}

@media (prefers-reduced-motion: reduce) {
  .spec-glyph, .q-price, .q-range i { transition: none; }
  .bars.is-entering .c-bar i { animation: none; }
}

/* ── the fold ─────────────────────────────────────────────────────────────
   SciCode is thirty rows of detail behind one headline fact. Collapsed by
   default, the section states its own summary — leader, field size, span — so
   closing it costs no information, only room. The daily glance gets a clean
   page; the answer is one tap away and the choice is remembered. */

.fold { border-top: 1px solid var(--hair); }
.fold-head {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 22px 0; cursor: pointer; list-style: none;
}
.fold-head::-webkit-details-marker { display: none; }
.fold-head:hover .grid-h { color: var(--live); }
.fold-head:focus-visible { outline: 2px solid var(--live); outline-offset: 4px; }

/* the summary carries the headline so a closed section is still informative */
.fold-sum {
  flex: 1 1 auto; margin: 0; min-width: 0;
  font-size: 13px; color: var(--ink-2);
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
  letter-spacing: 0.02em;
}
.fold-sum b { font-weight: 700; color: var(--ink); }
.fold .grid-meta { margin: 0; }

.fold-mark {
  width: 16px; height: 16px; color: var(--ink-2);
  transition: transform 0.24s cubic-bezier(0.16, 1, 0.3, 1);
}
.fold[open] .fold-mark { transform: rotate(180deg); }
.fold-body { padding-bottom: 4px; }

@media (prefers-reduced-motion: reduce) {
  .fold-mark { transition: none; }
}

@media (max-width: 860px) {
  .fold-head { padding: 18px 0; gap: 6px 12px; }
  .fold-sum { flex: 1 1 100%; order: 3; }
  .fold-mark { margin-left: auto; }
}

/* a closed fold leaves a long run of nothing before the colophon, so the page
   closes up behind it rather than ending in a void */
main:has(.fold:not([open])) + .colo { margin-top: clamp(20px, 2.5vw, 34px); }

/* ── ask ──────────────────────────────────────────────────────────────────
   A chat, in a world with a No-Box Rule. So: no bubbles, no panel, no avatar,
   no floating widget. One input line on a hairline baseline, and the answer
   sets into the page as prose at reading size — the page answering in its own
   voice rather than a support widget bolted to the corner. */

.ask .in { padding-block: 0; }
.fold--ask { border-top: 1px solid var(--hair); }

.ask-form {
  display: flex; align-items: center; gap: 12px;
  border-bottom: 2px solid var(--ink);
  padding-bottom: 8px;
}
.ask-input {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--face); font-size: clamp(1rem, 2.1vw, 1.45rem);
  letter-spacing: -0.015em; font-weight: 500;
  color: var(--ink); background: none; border: 0; padding: 6px 0;
  caret-color: var(--live);
}
.ask-input::placeholder { color: var(--ink-3); font-weight: 400; }
.ask-input:focus { outline: none; }
.ask-form:focus-within { border-bottom-color: var(--live); }

.ask-count {
  font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums;
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
  white-space: nowrap;
}
/* the count turns to the live hue as the cap approaches, and the form refuses
   past it — the browser half of a limit the service also enforces */
.ask-count[data-over] { color: var(--live); font-weight: 700; }

.ask-go {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; cursor: pointer;
  color: var(--ink); background: none; border: 0;
}
.ask-go .ic { width: 20px; height: 20px; }
.ask-go:hover { background: var(--acid); color: #12110f; }
.ask-go[disabled] { color: var(--ink-3); cursor: not-allowed; background: none; }

.ask-note {
  margin: 10px 0 0; min-height: 1.3em;
  font-size: 11px; letter-spacing: 0.02em; color: var(--ink-2);
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
}
.ask-note[data-tone="stop"] { color: var(--live); font-weight: 700; }

.ask-out { margin-top: clamp(18px, 2.6vw, 30px); }
.ask-q {
  margin: 0 0 8px;
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.14em; color: var(--ink-3);
  font-variation-settings: 'MONO' 1, 'CASL' 0, 'slnt' 0, 'CRSV' 0;
}
/* the answer is page prose, not a chat message */
.ask-a {
  margin: 0; max-width: 62ch;
  font-size: clamp(1rem, 2.1vw, 1.45rem); line-height: 1.4;
  letter-spacing: -0.015em; font-weight: 450; color: var(--ink);
  min-height: 1.4em;
}
.ask-a[data-state="thinking"] { color: var(--ink-3); font-weight: 400; }
.ask-a[data-state="error"] { color: var(--live); font-weight: 500; }

/* the caret that trails the answer while it streams, reusing the live hue */
.ask-a[data-state="streaming"]::after {
  content: ''; display: inline-block; vertical-align: -0.08em;
  width: 0.5em; height: 1em; margin-left: 0.08em; background: var(--live);
}
@media (prefers-reduced-motion: no-preference) {
  .ask-a[data-state="streaming"]::after { animation: caret 1s steps(1) infinite; }
  @keyframes caret { 50% { opacity: 0; } }
}

@media (max-width: 860px) {
  .ask-form { gap: 8px; }
}

/* ── zoom islands ─────────────────────────────────────────────────────────
   A zoomed island needs to escape its own box without pushing the page around,
   and needs to sit above the acid field while it is being read. */
[data-zoom-island] { will-change: transform; }
/* the affordance: stated once, only where a finger can act on it */
.zi-hint { display: none; }
@media (pointer: coarse) {
  .zi-hint {
    display: inline; margin-left: 10px; padding-left: 10px;
    border-left: 1px solid var(--hair); color: var(--ink-3);
  }
}

/* ── bidi ─────────────────────────────────────────────────────────────────
   The ask box is answered in whatever language it is asked in, so direction is
   content's to decide, not the page's. dir="auto" on the input, the question
   and the answer lets the browser read the first strong character and align
   accordingly; text-align:start then follows it in both directions. */
.ask-input, .ask-q, .ask-a { text-align: start; }
/* an RTL answer should still start under the question, so the block stays
   left-anchored while the text inside it flows right-to-left */
.ask-a:dir(rtl), .ask-q:dir(rtl) { margin-inline-start: 0; margin-inline-end: auto; }

/* the waiting word breathes along the weight axis, one letter behind the next */
.ask-a[data-state="thinking"] .tw {
  display: inline-block;
  font-weight: 300;
  animation: think 1.5s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  animation-delay: calc(var(--i, 0) * 55ms);
}
@keyframes think {
  0%, 70%, 100% { font-weight: 300; }
  35%           { font-weight: 850; }
}
@media (prefers-reduced-motion: reduce) {
  .ask-a[data-state="thinking"] .tw { animation: none; font-weight: 450; }
}
