/* ————————————————————————————————————————————————————————————
   Metergraph landing page — "annual report" system.

   Self-contained on purpose. index.html no longer loads styles.css,
   because this design removes the card/shadow/dot-grid language that
   lives there and overriding it piecemeal was worse than carrying the
   ~90 lines of nav/footer/button primitives across. styles.css stays
   untouched for docs.html and the three legal pages.

   dev.html now loads this file too. It is the same system with one
   substitution: where index.html makes numerals the loudest element,
   dev.html makes the code specimens loudest, set in inverse.

   The organising idea: this page is a report on the reader's LLM
   spend, not a product page. Three consequences run through
   everything below —
     1. Numerals are the loudest element; headlines are quieter.
     2. Hairline rules replace every card, shadow and rounded box.
     3. Emerald means "money recovered" and amber means "money
        leaking". Neither is ever used decoratively.
   ———————————————————————————————————————————————————————————— */

:root {
  --paper: #e8edf3;
  --paper-deep: #dfe6ee;
  --ink: #0b1220;
  /* Alphas are set by the contrast they have to clear on --paper, not
     by eye. Against #e8edf3: 0.72 → 6.99:1, 0.60 → 4.64:1 (AA body),
     0.46 → 3.03:1 (AA for UI components and large text). Lowering any
     of these drops real text below AA — the earlier 0.45/0.25 pair
     measured 2.94:1 and 1.73:1. */
  --ink-70: rgba(11, 18, 32, 0.72);
  --ink-muted: rgba(11, 18, 32, 0.60);  /* all small real text */
  --ink-mark: rgba(11, 18, 32, 0.46);   /* markers, icons, placeholders */
  --ink-12: rgba(11, 18, 32, 0.12);
  --ink-08: rgba(11, 18, 32, 0.08);

  /* Semantic only. See note 3 above.
     Two greens because one can't serve both: --recovered clears 3:1 on
     paper, which AA allows for large text only, so it is restricted to
     the display figures. Everything smaller uses --recovered-text at
     4.54:1. --leaking is darkened from #b87503 (3.19:1) for the same
     reason — every one of its uses is small type. */
  --recovered: #0b9a6d;                 /* ≥24px display figures only */
  --recovered-text: #097a56;            /* 4.54:1 — all small text */
  --recovered-bright: #10b981;          /* graphic marks and the wordmark */
  --recovered-soft: rgba(16, 185, 129, 0.16);
  --leaking: #945e02;                   /* 4.62:1 */
  --leaking-soft: rgba(245, 158, 11, 0.55);
  --leaking-fill: rgba(245, 158, 11, 0.20);  /* area fills only, never type */
  /* Two more amber roles the plates need. Type set on --leaking-fill is
     reading against #ebddc5, not against paper, and --leaking measures
     only 4.06:1 there — so the label inside Figure 1's wedge gets its
     own darker value at 4.55:1. --leaking-bar is the lightest amber
     that still clears 3:1 on paper as a graphic. */
  --leaking-on-fill: #8a5702;           /* 4.55:1 on the wedge */
  --leaking-bar: #b87503;               /* 3.19:1 on paper */

  --display: 'ABC Diatype', ui-sans-serif, system-ui, sans-serif;
  --mono: 'ABC Diatype Mono', ui-monospace, 'SF Mono', monospace;

  --measure: 1240px;
  --gutter: 32px;
  --rail: 168px;      /* margin column: section numbers + telemetry */
  --col-gap: 56px;
  --body-col: 62ch;   /* reading measure, narrower than the old full-width paras */
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--display);
  font-size: 17px;
  line-height: 1.65;
  font-feature-settings: "kern" 1;
  -webkit-font-smoothing: antialiased;
}

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

h1, h2, h3, h4 {
  margin: 0 0 0.5em;
  font-weight: 400;
  letter-spacing: -0.025em;
  line-height: 1.12;
  text-wrap: balance;
}
p { margin: 0 0 1em; }
a { color: inherit; }
code { font-family: var(--mono); font-size: 0.9em; }

.mono { font-family: var(--mono); }
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* Focus rings are ink, not emerald — emerald is reserved for money.
   Ink on paper clears AA comfortably at 2px. */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, summary:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* ————— The document grid —————
   rail | body | marginalia. The rail carries section numbers, running
   heads and repo telemetry; marginalia carries pull quotes, caveats
   and attributions. This asymmetry is what breaks the page out of the
   centered-stack look — everything else follows from it. */
.doc {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.sect {
  display: grid;
  grid-template-columns:
    var(--rail)
    minmax(0, var(--body-col))
    minmax(0, 1fr);
  column-gap: var(--col-gap);
  padding: 84px 0;
  border-top: 1px solid var(--ink-12);
}
/* Everything defaults to the reading column. */
.sect > * { grid-column: 2; }
.sect > .rail { grid-column: 1; grid-row: 1 / span 40; }
.sect > .marg { grid-column: 3; }
/* Schedules, the scorecard and anything tabular want the full width
   from the reading column rightwards. */
.sect > .wide { grid-column: 2 / -1; }

/* ————— Rail —————
   Sticky so the section number stays with you through a long section,
   the way a running head does on a printed page. */
.rail {
  position: sticky;
  top: 28px;
  align-self: start;
}
.rail-num {
  font-family: var(--display);
  font-size: 30px;
  letter-spacing: -0.03em;
  line-height: 1;
  margin: 0 0 8px;
  font-variant-numeric: tabular-nums;
}
.rail-name {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0;
  padding-top: 9px;
  border-top: 2px solid var(--ink);
}


/* ————— Type scale —————
   The inversion: figures are the largest thing on the page, set at
   weight 400. Heavy weights at display size read as SaaS marketing;
   400 reads as a document. */
/* Sized so the two hero figures sit on one line side by side without
   either wrapping: at the 1240 measure the pair has ~950px, and
   "15–40%" + "$90k–$240k / year" fit inside it at these caps. Raising
   either clamp re-introduces the collision. */
.fig {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(64px, 9vw, 136px);
  line-height: 0.84;
  letter-spacing: -0.045em;
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
  margin: 0;
}
.fig-mid {
  font-size: clamp(36px, 4.2vw, 54px);
  line-height: 0.95;
  letter-spacing: -0.035em;
  font-variant-numeric: tabular-nums lining-nums;
  font-weight: 400;
  white-space: nowrap;
  margin: 0;
}
/* Units stay in the figure's own colour — a grey "%" reads as a
   separate object rather than part of the number. */
.fig small {
  font-size: 0.34em;
  letter-spacing: -0.01em;
  color: inherit;
  opacity: 0.5;
}
.fig-mid small { font-size: 0.36em; letter-spacing: 0; color: var(--ink-muted); }
.fig-label {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 14px;
}
/* Accessible green by default; the display figures opt back into the
   brighter one because at ≥24px AA only asks for 3:1. */
.recovered { color: var(--recovered-text); }
.fig.recovered, .fig-mid.recovered { color: var(--recovered); }
.leaking { color: var(--leaking); }

h1 { font-size: clamp(31px, 3.5vw, 46px); letter-spacing: -0.03em; }
h2 { font-size: clamp(28px, 3.2vw, 40px); margin-bottom: 0.6em; }
h3 { font-size: 19px; font-weight: 500; letter-spacing: -0.015em; margin-bottom: 0.35em; }

.lede {
  font-size: 19px;
  color: var(--ink-70);
  margin-bottom: 1.4em;
  text-wrap: pretty;
}
.body-sm { font-size: 15.5px; color: var(--ink-70); }

/* ————— Footnotes —————
   Every hedge in the copy becomes a numbered note. A company whose
   pitch is "we prove it in writing" should annotate its own marketing
   claims; nobody else in the category does. Numbers are hand-authored
   rather than counter-generated so the marker and the note can never
   drift out of sync silently. */
.fn {
  font-family: var(--mono);
  font-size: 0.62em;
  font-weight: 500;
  vertical-align: super;
  line-height: 0;
  color: var(--recovered-text);
  text-decoration: none;
  padding: 0 1px 0 2px;
}
.fn:hover { text-decoration: underline; }
.notes {
  grid-column: 2 / -1;
  margin: 44px 0 0;
  padding: 12px 0 0;
  list-style: none;
  border-top: 1px solid var(--ink-12);
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-muted);
  /* auto-fit rather than `columns: 2`, so a section with a single note
     runs full width instead of sitting in a half-width stub. */
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  column-gap: var(--col-gap);
}
.notes li {
  padding: 5px 0 5px 26px;
  position: relative;
}
.notes li::before {
  content: attr(data-n);
  position: absolute;
  left: 0; top: 5px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-muted);
}
.notes a { color: var(--ink-70); }

/* ————— Marginalia ————— */
.marg {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink-muted);
  border-left: 1px solid var(--ink-12);
  padding-left: 20px;
  align-self: start;
}
/* Pins marginalia beside the heading instead of letting it auto-place
   after a `.wide` element. A wide table occupies columns 2–3, so any
   marginalia following it gets pushed to a row of its own below —
   which stranded §04's caution at the bottom-right, reading as debris
   rather than as a note on the prices above it. */
.marg-top { grid-row: 1 / span 2; }
.marg-quote {
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.65;
  color: var(--ink-70);
  margin: 0 0 0.9em;
}
.marg-quote::before { content: "\201C"; }
.marg-quote::after { content: "\201D"; }
.marg-src {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0;
}

/* ————— Masthead ————— */
.masthead {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr) auto;
  column-gap: var(--col-gap);
  align-items: baseline;
  padding: 26px 0 20px;
  border-bottom: 2px solid var(--ink);
}
.wordmark {
  font-family: var(--display);
  font-size: 25px;
  letter-spacing: -0.03em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
/* The wordmark keeps its emerald and amber. It is identity, not data,
   and it is the one place the colour rule doesn't apply. */
.wm-graph { color: var(--recovered-bright); }
.wm-spike { color: #f59e0b; font-size: 0.55em; vertical-align: super; margin-left: 1px; }
.wordmark-sm { font-size: 19px; }

.runhead {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0;
}
.masthead-nav {
  display: flex;
  gap: 22px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.masthead-nav a { color: var(--ink-muted); text-decoration: none; }
.masthead-nav a:hover { color: var(--ink); }

/* ————— Buttons —————
   Square. A radius here would reintroduce the SaaS-card language the
   rest of the page spends its time removing. */
.btn {
  display: inline-block;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 14px 22px;
  border: 1px solid var(--ink);
  border-radius: 0;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}
.btn-solid { background: var(--ink); color: var(--paper); }
.btn-solid:hover { background: transparent; color: var(--ink); }
.btn-ghost { background: transparent; color: var(--ink); }
.btn-ghost:hover { background: var(--ink); color: var(--paper); }
.btn-sm { font-size: 10.5px; padding: 9px 15px; }

/* ————— §01 The finding (hero) ————— */
.finding { border-top: none; padding-top: 68px; }
.finding h1 { margin-bottom: 0.5em; }
.finding h1 em { font-style: normal; }

.finding-figs {
  grid-column: 2 / -1;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: end;
  column-gap: var(--col-gap);
  row-gap: 26px;
  margin: 12px 0 0;
  padding-top: 30px;
  border-top: 1px solid var(--ink-12);
}
.finding-rule {
  grid-column: 1 / -1;
  height: 2px;
  background: var(--ink);
  margin: 22px 0 0;
}
.finding-second { padding-bottom: 6px; }

.claims {
  list-style: none;
  margin: 30px 0 0;
  padding: 0;
  display: grid;
  gap: 0;
}
.claims li {
  font-size: 15.5px;
  color: var(--ink-70);
  padding: 14px 0 14px 34px;
  border-top: 1px solid var(--ink-08);
  position: relative;
}
.claims li::before {
  content: attr(data-n);
  position: absolute;
  left: 0; top: 15px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-mark);
}
.claims b { color: var(--ink); font-weight: 500; }

/* Hero capture: a field on a rule plus a solid CTA. No card, no select —
   the API accepts an empty spend band, so the qualifying question can
   wait for the colophon form. The button is solid rather than a bare
   text submit because this is the page's primary action and has to
   read as one at a glance. */
.capture {
  display: flex;
  align-items: stretch;
  gap: 14px;
  margin: 30px 0 0;
  max-width: 520px;
}
.capture input {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--display);
  font-size: 16px;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--ink);
  padding: 12px 2px;
}
.capture input::placeholder { color: var(--ink-mark); }
.capture input:focus-visible { outline-offset: 1px; }
.capture button {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper);
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 0;
  padding: 12px 20px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.capture button:hover { background: transparent; color: var(--ink); }
.capture button:disabled {
  background: var(--ink-mark);
  border-color: var(--ink-mark);
  color: var(--paper);
  cursor: default;
}

/* The lede reads as follow-up once the form sits above it. */
.lede-after { margin-top: 30px; }

/* Scope note. "Your LLM bill" is genuinely ambiguous now that the FinOps
   tools bucket coding-assistant seats under the same heading, so the
   page says which bill it means, once, in plain words. Set on a rule so
   it reads as a definition rather than as more body copy. */
.scope {
  font-size: 15.5px;
  color: var(--ink-70);
  border-left: 2px solid var(--ink);
  padding: 2px 0 2px 18px;
  margin: 0 0 1em;
}
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; }

.form-msg {
  font-family: var(--mono);
  font-size: 12px;
  margin: 10px 0 0;
  min-height: 1.2em;
  color: var(--ink-muted);
}
.form-msg.ok { color: var(--recovered-text); }
.form-msg.err { color: #c0392b; }
.form-fine {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-muted);
  margin: 8px 0 0;
}
form.done .capture, form.done .calc-fields { display: none; }

/* ————— Schedules (the two tables) ————— */
.sched { width: 100%; border-collapse: collapse; }
/* Schedule captions live OUTSIDE .scroll-x. A <caption> inherits the
   table's 560px min-width, so inside the scroller it was clipped at the
   viewport edge on mobile and could not wrap. The tables carry
   aria-label instead. */
.sched-cap, .plate-cap {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 14px;
}
.sched th {
  text-align: left;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding: 0 14px 10px 0;
  border-bottom: 2px solid var(--ink);
  vertical-align: bottom;
}
.sched td {
  font-size: 15px;
  padding: 14px 14px 14px 0;
  border-bottom: 1px solid var(--ink-08);
  vertical-align: top;
  color: var(--ink-70);
}
.sched td:first-child { color: var(--ink); }
.sched th:last-child, .sched td:last-child { padding-right: 0; }
.sched td.num {
  text-align: right;
  font-family: var(--mono);
  font-size: 13.5px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Header cells keep the header scale — .num must not leak its body size
   up into <th>, which is what made "TYPICAL REDUCTION" outrank its
   neighbours. */
.sched th.num { text-align: right; }
.sched .why { font-size: 14px; color: var(--ink-muted); }
/* `.sched td` sets a colour, so it outranks the bare .recovered /
   .idx-* classes (0,1,1 vs 0,1,0) and was silently swallowing every
   semantic colour in both schedules — the whole cost-index column read
   as neutral grey. Re-assert them at cell specificity. */
.sched td.recovered { color: var(--recovered-text); }
.sched td.leaking { color: var(--leaking); }
.sched td.idx-hi { color: var(--leaking); }
.sched td.idx-lo { color: var(--recovered-text); }
.sched-band td {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding: 26px 0 8px;
  border-bottom: 1px solid var(--ink-mark);
}
.sched-band:first-child td { padding-top: 4px; }
.idx-hi { color: var(--leaking); }
.idx-lo { color: var(--recovered-text); }

/* Specimen rows — what the marquee used to scroll past, held still so
   it can actually be read. */
.specimen {
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-muted);
  border-top: 2px solid var(--ink);
}
/* Grid rather than space-between: the middle column has to start on a
   common left edge or the four findings read as ragged prose instead
   of as a schedule. */
.specimen li {
  padding: 11px 0;
  border-bottom: 1px solid var(--ink-08);
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 4px 20px;
}
.specimen li > :last-child { text-align: right; }
.specimen .route { color: var(--ink); }
.specimen b { font-weight: 500; font-variant-numeric: tabular-nums; }

/* ————— §05 Scorecard —————
   The differentiator, promoted to the widest and most designed moment
   on the page. Chrome removed; it now reads as a table of findings. */
.sc { margin: 0; }
.sc-title {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px 20px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding-bottom: 12px;
  border-bottom: 2px solid var(--ink);
}
.sc-title b { color: var(--ink); font-weight: 500; }
.sc-verdict {
  font-weight: 500;
  color: var(--recovered-text);
  font-variant-numeric: tabular-nums;
}
.sc-axis {
  display: flex;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--ink-muted);
  padding: 10px 0 2px;
  margin-left: calc(190px + 20px);
  margin-right: calc(64px + 20px);
}
.sc-row {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr) 64px;
  align-items: center;
  column-gap: 20px;
  padding: 11px 0;
  border-bottom: 1px solid var(--ink-08);
}
.sc-label { font-size: 15px; color: var(--ink-70); }
.sc-val {
  text-align: right;
  font-family: var(--mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
}
.sc-val.recovered { color: var(--recovered-text); }
.sc-track { position: relative; height: 24px; }
/* The non-inferiority margin the interval has to clear. */
.sc-margin {
  position: absolute;
  left: 25%; top: 0; bottom: 0;
  border-left: 1px dashed var(--leaking-soft);
}
.sc-zero {
  position: absolute;
  left: 50%; top: 0; bottom: 0;
  border-left: 1px solid var(--ink-mark);
}
.sc-ci {
  position: absolute;
  top: 9px;
  height: 6px;
  background: var(--recovered-soft);
}
.sc-ci i {
  position: absolute;
  top: -3px;
  width: 12px; height: 12px;
  margin-left: -6px;
  background: var(--recovered-bright);
  border-radius: 50%;
}
.sc-foot, .plate-foot {
  font-family: var(--mono);
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--ink-muted);
  margin: 14px 0 0;
}

/* ————————————————————————————————————————————————————————————
   Plates — the page's data graphics.

   Every plate draws a figure the copy already states; none of them is
   decoration, and none introduces a number that isn't in the text.
   Same rules as everywhere else: hairlines, no fill except the two
   semantic colours, square corners.

   One structural rule worth stating, because it drives the markup:
   labels are HTML positioned over the drawing, never <text> inside a
   viewBox. A 900-unit viewBox rendered into a 335px phone column
   scales a 12px label to about 4px. So the SVG carries geometry only
   and the words sit on top of it at their real size.
   ———————————————————————————————————————————————————————————— */
.plate { margin: 0; }

/* ————— Figure 1 · price drift (§02) —————
   The flat line is what you pay after choosing once; the staircase is
   what the market floor does underneath it. The amber wedge between
   them is the whole argument of the section, so it is the only filled
   area on the page. */
.drift {
  border-top: 2px solid var(--ink);
  padding-top: 14px;
}
.drift-plot { position: relative; }
/* preserveAspectRatio is none so the plot fills whatever column it
   gets; non-scaling-stroke is what keeps the lines a hairline instead
   of stretching with it. */
.drift-svg {
  display: block;
  width: 100%;
  height: clamp(190px, 24vw, 300px);
}
/* Scoped to .drift-svg so it outranks the blanket `fill: none` above.
   As a bare `.drift-wedge` it lost that fight (0,1,0 against 0,1,1) and
   the wedge — the only thing the figure exists to show — rendered
   empty. */
.drift-svg path { fill: none; vector-effect: non-scaling-stroke; }
.drift-svg .drift-wedge { fill: var(--leaking-fill); stroke: none; }
.drift-paid { stroke: var(--ink); stroke-width: 2; }
.drift-floor { stroke: var(--recovered); stroke-width: 2; }
/* Positions are set here rather than inline so the breakpoint can move
   them; at 375px the desktop placement puts the drift label straight
   through the staircase. */
.drift-note {
  position: absolute;
  margin: 0;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.4;
  color: var(--ink-70);
  max-width: 22ch;
  pointer-events: none;
}
.drift-note b { font-weight: 500; color: var(--ink); }
.drift-note.is-paid { top: 15%; left: 0.5%; max-width: 16ch; }
.drift-note.is-drift { top: 33%; left: 34%; color: var(--leaking-on-fill); }
.drift-note.is-floor { top: 72%; left: 54%; color: var(--recovered-text); }
.drift-x {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding-top: 9px;
  margin-top: 4px;
  border-top: 1px solid var(--ink-12);
}

/* ————— §02 problem blocks —————
   Three-up rather than stacked. Stacked, the three read as one long
   passage; side by side they read as three findings, which is what
   they are. */
.probs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--col-gap);
  margin-top: 48px;
}
.probs > div {
  padding-top: 16px;
  border-top: 1px solid var(--ink-12);
}
.probs h3 { margin-bottom: 0.4em; }
.probs p { margin: 0; }

/* ————— Range bars (Schedule A) —————
   The reduction column becomes a chart in place: the cell keeps its
   figure and grows a bar underneath it, scaled 0–100%. A lever worth
   10–25% should not look the same size on the page as one worth
   40–70%, which is exactly what a column of text ranges does. */
.rb-cell { width: 168px; }
.rb {
  display: block;
  position: relative;
  height: 4px;
  margin-top: 8px;
  background: var(--ink-08);
}
/* min-width so a tight range like "~30%" still draws as a mark rather
   than as a two-pixel artefact that reads like a rendering fault. */
.rb::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  left: calc(var(--lo) * 1%);
  right: calc(100% - var(--hi) * 1%);
  min-width: 8px;
  background: var(--recovered);
}
/* "varies" has no interval to draw, so the track stays empty rather
   than guessing at one. */
.rb-none { background: repeating-linear-gradient(90deg, var(--ink-12) 0 3px, transparent 3px 7px); }
.rb-none::after { content: none; }

/* ————— Log bars (Schedule B) —————
   The index column spans 200 down to 1. On a linear scale everything
   below the frontier tier collapses into the baseline and the table
   shows nothing; log10 keeps the cheap tier legible while still
   reading as a collapse. Widths are computed in the markup. */
.lb-cell { width: 150px; }
/* Bars hang from the same right rule as the figures they belong to, so
   the column reads as one falling stack. */
.lb {
  display: block;
  height: 4px;
  margin-top: 8px;
  margin-left: auto;
  min-width: 6px;
  width: calc(var(--w) * 1%);
  background: var(--recovered);
}
.lb-hi { background: var(--leaking-bar); }
.lb-base { background: var(--ink-mark); }

/* ————— Figure 2 · where Metergraph sits (§06) —————
   Built from boxes and rules rather than as one SVG so it reflows: at
   720px the rows turn vertical and the connector glyphs rotate with
   them. The point it has to make in one glance is that the request
   path at the top never enters the box below it. */
.flow { border-top: 2px solid var(--ink); padding-top: 22px; }
.flow-lane {
  border: 1px solid var(--ink-12);
  padding: 16px 20px 18px;
}
.flow-lane.is-mg { border-color: var(--ink); }
.flow-lane-cap {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 14px;
}
.flow-lane.is-mg .flow-lane-cap { color: var(--ink); }
.flow-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
}
/* A row with no arrows in it is a set of things, not a sequence. It
   keeps its gap when the diagram stacks, because at gap 0 the boxes
   share borders and read as a chain with the arrows missing. */
.flow-row.is-set { gap: 10px 14px; }
.flow-node {
  font-size: 15px;
  padding: 7px 14px;
  border: 1px solid var(--ink-mark);
  white-space: nowrap;
}
.flow-node.is-stage {
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: 0.04em;
}
.flow-node .n {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink-mark);
  margin-right: 8px;
}
/* Connectors are glyphs, not geometry — that is what lets the whole
   diagram turn from a row into a column at one breakpoint. */
.flow-arr {
  flex: 1 1 20px;
  min-width: 20px;
  height: 1px;
  background: var(--ink-mark);
  position: relative;
}
.flow-arr::after {
  content: "";
  position: absolute;
  right: -1px; top: -3px;
  border-left: 6px solid var(--ink-mark);
  border-top: 3.5px solid transparent;
  border-bottom: 3.5px solid transparent;
}
.flow-drop {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 0 14px 40px;
  position: relative;
}
.flow-drop::before {
  content: "";
  position: absolute;
  left: 40px; top: 0; bottom: 14px;
  border-left: 1px dashed var(--ink-mark);
}
.flow-drop::after {
  content: "";
  position: absolute;
  left: 36px; bottom: 8px;
  border-top: 6px solid var(--ink-mark);
  border-left: 4.5px solid transparent;
  border-right: 4.5px solid transparent;
}
.flow-drop span {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink-muted);
  padding-left: 18px;
}
.flow-out { margin-top: 0; }

/* ————————————————————————————————————————————————————————————
   Code specimens (dev.html).

   The developer page needs a loudest element the way the exec page has
   its numerals, and on a page about an SDK that element is the code.
   So specimens are set in inverse: paper on ink, the same ground as
   the statement band. That also fixes a contrast problem rather than
   creating one. A tinted code block on --paper-deep puts the comment
   grey at 4.2:1 and --recovered-text at 4.25:1, both under AA; on
   #0b1220 the same roles measure 5.42:1 and 7.38:1.
   ———————————————————————————————————————————————————————————— */
.code { margin: 0; }
/* Same scroll-shadow trick the schedules use, inverted for the dark
   ground: `local` layers travel with the content and `scroll` ones
   don't, so the shadow shows only on a side that still has code
   off-screen. Without it a line cut at the right edge reads as a
   rendering fault rather than as something you can swipe. */
.code pre,
.tabs-panel > pre {
  margin: 0;
  color: rgba(232, 237, 243, 0.9);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.75;
  padding: 22px 24px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  background:
    linear-gradient(to right, var(--ink) 40%, rgba(11, 18, 32, 0)) left center,
    linear-gradient(to left, var(--ink) 40%, rgba(11, 18, 32, 0)) right center,
    radial-gradient(farthest-side at 0 50%, rgba(232, 237, 243, 0.25), transparent) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(232, 237, 243, 0.25), transparent) right center,
    var(--ink);
  background-repeat: no-repeat;
  background-size: 30px 100%, 30px 100%, 12px 100%, 12px 100%, auto;
  background-attachment: local, local, scroll, scroll, scroll;
}
.code code { font-size: inherit; font-family: inherit; }
/* Inside a specimen, colour is syntax highlighting, which is its own
   long-standing convention and not the page's money semantics. The one
   place that distinction matters is a figure that shows a dollar
   amount: those stay uncoloured, because emerald there would read as a
   saving. */
.cmt { color: rgba(232, 237, 243, 0.55); }   /* 5.42:1 */
.hl { color: var(--recovered-bright); }      /* 7.38:1 on ink */
.str { color: #fbbf24; }                     /* 11.22:1 on ink */
/* Python and TypeScript side by side. They say the same thing, so
   stacking them makes the second look like a second step. */
/* 24px, not the old 2px seam: with a copy button pinned to the right of
   each head, a hairline gutter put column one's button hard against
   column two's caption and the row read as one jumbled line. */
.code-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
}
/* A media query adds no specificity, so this has to sit after the rule
   it overrides, not before it. Two 13px code columns need more room
   than a tablet gives them: at 768px each column came to 351px and both
   samples overflowed it by ~120px. */
@media (max-width: 900px) {
  .code-2 { grid-template-columns: minmax(0, 1fr); gap: 22px; }
}
.code-cap {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 10px;
}

/* The anatomy of one captured row, labelled. */
.anat {
  font-family: var(--mono);
  background: var(--ink);
  color: rgba(232, 237, 243, 0.9);
  padding: 20px 24px;
  font-size: 14px;
  overflow-x: auto;
}
.anat-legend {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px var(--col-gap);
  font-size: 13px;
  color: var(--ink-70);
}
.anat-legend b {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 2px;
}

/* ————— The hero specimen —————
   The whole product as one object: the line you write, the row that
   comes back, and the finding it turns into. This is the page's
   equivalent of §01's figures on index.html, and it is the reason the
   dev hero carries no giant numeral. A "1" for one line of code is not
   a finding, it is a spec, and every SDK on earth claims it. The
   number worth setting large is the one at the end of the chain.

   Reading order down the block is deliberate: write, measure, save. */
.spec { margin: 0; }
.spec-block {
  background: var(--ink);
  color: rgba(232, 237, 243, 0.9);
  font-family: var(--mono);
}
.spec-cap {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(232, 237, 243, 0.5);
  margin: 0 0 12px;
}
.spec-zone { padding: 20px 24px 22px; }
.spec-zone + .spec-zone { border-top: 1px solid rgba(232, 237, 243, 0.14); }
.spec-code { font-size: 15px; line-height: 1.6; margin: 0; overflow-x: auto; }
.spec-route { font-size: 12.5px; color: rgba(232, 237, 243, 0.55); margin: 0 0 10px; }
/* The measured cost and the recovered figure sit at the same size, on
   the same baseline, on opposite sides of the block. That pairing is
   the argument. */
.spec-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px 24px;
  flex-wrap: wrap;
}
.spec-what { font-size: 13.5px; color: rgba(232, 237, 243, 0.72); }
.spec-fig {
  font-family: var(--display);
  font-size: clamp(26px, 3.4vw, 40px);
  letter-spacing: -0.03em;
  line-height: 1;
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
  color: #fff;
}
/* Emerald here is legitimate and load-bearing: this one is money
   recovered, which is the only thing it ever means. The measured spend
   above it stays white, because a bill is not a saving. */
.spec-fig.is-saved { color: var(--recovered-bright); }   /* 7.38:1 on ink */
/* The unit is dimmed so the figure reads first, but not by a flat
   value: at 0.5 the white unit measures 5.30:1 and the green one only
   2.69:1, under the 3:1 floor that even display-size text has to
   clear. Each gets the alpha its own colour needs. */
.spec-unit { opacity: 0.5; }
.spec-fig.is-saved .spec-unit { opacity: 0.7; }   /* 4.16:1 */
/* The closing zone carries two figures at the same size, not one. Both
   loud numbers used to be money, which read as a page about money that
   mentions quality. The parity result is the reason the saving is
   takeable, so it gets equal weight and comes first. */
.spec-pair { align-items: flex-end; gap: 20px 48px; }
.spec-pair > span { display: block; }
.spec-sub {
  display: block;
  font-size: 12px;
  line-height: 1.45;
  color: rgba(232, 237, 243, 0.62);   /* 6.59:1 on ink */
  margin-top: 7px;
  max-width: 30ch;
}
.spec-note {
  font-size: 11.5px;
  color: rgba(232, 237, 243, 0.5);
  margin: 10px 0 0;
}

/* Spec line under the headline. These facts are real and worth stating;
   they are just not findings, so they are set at the size of what they
   are. */
.spec-facts {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-muted);
  margin: 0 0 26px;
}

/* Two real buttons rather than a button beside a sentence. The old row
   put "Or skip the account entirely" next to the primary action, which
   argued against pressing it. */
.cta-row {
  display: flex;
  align-items: stretch;
  flex-wrap: wrap;
  gap: 14px;
  margin: 26px 0 0;
}
.cta-fine + .spec { margin-top: 34px; }
.cta-fine {
  font-family: var(--mono);
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--ink-muted);
  margin: 14px 0 0;
}
.cta-fine code { color: var(--ink); background: var(--paper-deep); padding: 3px 7px; }

/* Editions read as a comparison schedule, not as pricing cards. */
.sched td.mark, .sched th.mark { text-align: center; }
.sched td.mark {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--ink-muted);
}
.sched td.mark-y { color: var(--recovered-text); }

/* ————————————————————————————————————————————————————————————
   Docs (docs.html).

   Same system again, third variant. The rail that carries section
   numbers on the two landing pages carries the table of contents here,
   because a reference page is the one place where knowing where you
   are matters more than the section number.
   ———————————————————————————————————————————————————————————— */
/* A page-level two-column shell rather than the per-section grid the
   landing pages use: the contents list has to stay put across the whole
   document, not restart at every section. */
.docs-shell {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  column-gap: var(--col-gap);
  align-items: start;
}
.docs-main { min-width: 0; }
.docs-main > .sect {
  display: block;
  padding: 52px 0;
  scroll-margin-top: 24px;
}
.docs-main > .sect:first-child { border-top: none; padding-top: 30px; }
.docs-main .body { max-width: var(--body-col); }
.docs-main p, .docs-main ul, .docs-main ol, .docs-main .warn { max-width: var(--body-col); }
.docs-main .wide, .docs-main .code, .docs-main table,
.docs-main .scroll-x, .docs-main .steps-n { max-width: none; }

.toc { position: sticky; top: 28px; align-self: start; }
.toc > summary { list-style: none; cursor: default; }
.toc > summary::-webkit-details-marker { display: none; }
.toc-cap {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 12px;
  padding-bottom: 9px;
  border-bottom: 2px solid var(--ink);
}
.toc ol { list-style: none; margin: 0; padding: 0; }
.toc li { margin: 0 0 7px; }
.toc a {
  font-family: var(--mono);
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--ink-muted);
  text-decoration: none;
  display: block;
}
.toc a:hover { color: var(--ink); }
.toc a.on { color: var(--ink); font-weight: 500; }
.toc .toc-n { color: var(--ink-mark); margin-right: 7px; }

/* Numbered step, used for the onboarding path. The number is the only
   thing a reader scanning for "where was I" can lock onto. */
.steps-n { list-style: none; margin: 0; padding: 0; counter-reset: sn; }
.steps-n > li {
  counter-increment: sn;
  position: relative;
  padding: 0 0 30px 52px;
  border-left: 1px solid var(--ink-12);
  margin-left: 13px;
}
.steps-n > li:last-child { border-left-color: transparent; padding-bottom: 0; }
.steps-n > li::before {
  content: counter(sn, decimal-leading-zero);
  position: absolute;
  left: -13px; top: 0;
  width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
}
.steps-n h3 { margin: 2px 0 0.4em; }
.steps-n p { font-size: 15.5px; color: var(--ink-70); }
.steps-n .code { margin: 14px 0 0; }

/* Copy button on a specimen. */
.code-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.copy {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  background: transparent;
  border: 1px solid var(--ink-12);
  border-radius: 0;
  padding: 4px 9px;
  cursor: pointer;
}
.copy:hover { color: var(--ink); border-color: var(--ink-mark); }
.copy.done { color: var(--recovered-text); border-color: var(--recovered-text); }

/* ————— Language tabs —————
   Every bilingual example is one object with a Python and a TypeScript
   panel rather than two blocks side by side. Two reasons: at 350px the
   side-by-side pair had to stack anyway, and a reader is almost always
   in one language, so showing them both doubles the page for no gain.
   Choosing a tab sets it for the whole page and is remembered, which is
   the only reason tabs beat two blocks.

   The panels are real content with a `hidden` attribute, not CSS-only
   toggles, so a reader without JavaScript gets the Python panel and the
   <noscript> block below reveals the rest. */
.tabs { margin: 0; }
/* Hairline, not the 2px ink rule the schedules use: the selected tab
   marks itself with a 2px ink underline, and against a bar that is
   already 2px ink that indicator is invisible. */
.tabs-bar {
  display: flex;
  align-items: stretch;
  gap: 0;
  border-bottom: 1px solid var(--ink-12);
}
.tabs-bar [role="tab"] {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  margin-bottom: -1px;
  padding: 9px 16px 8px;
  cursor: pointer;
}
.tabs-bar [role="tab"]:first-child { padding-left: 0; }
.tabs-bar [role="tab"]:hover { color: var(--ink); }
.tabs-bar [role="tab"][aria-selected="true"] {
  color: var(--ink);
  border-bottom-color: var(--ink);
}
.tabs-bar .copy { margin-left: auto; align-self: center; }
.tabs-panel { margin: 0; }
.tabs-panel > pre { margin-top: 0; }
.tabs-cap {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 8px;
  display: none;
}

/* The variable schedule needs fixed columns: nowrap on the names makes
   the browser hand column one most of the table otherwise. */
.sched-vars { table-layout: fixed; }
.sched-vars th:nth-child(1), .sched-vars td:nth-child(1) { width: 34%; }
.sched-vars th:nth-child(2), .sched-vars td:nth-child(2) { width: 13%; }
/* The names stay intact and the table scrolls, which is the pattern
   every other schedule on the site uses. Letting them wrap broke
   METERGRAPH_APP_TOKEN across two lines mid-word. */
.scroll-x .sched-vars { min-width: 780px; }

/* Inline API term. */
.api {
  font-family: var(--mono);
  font-size: 0.92em;
  background: var(--paper-deep);
  padding: 2px 6px;
  white-space: nowrap;
}
h3 .api, h4 .api { font-size: 0.95em; background: none; padding: 0; }

/* Callout for the things that will silently cost someone an afternoon. */
.warn {
  border-left: 2px solid var(--leaking-bar);
  padding: 2px 0 2px 18px;
  margin: 20px 0;
  font-size: 15px;
  color: var(--ink-70);
}
.warn b { color: var(--ink); font-weight: 500; }

/* ————— §06 Exposure ————— */
.exposure {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--col-gap);
}
.exposure li {
  padding: 20px 0;
  border-top: 1px solid var(--ink-12);
}
.exposure h3::before {
  content: attr(data-n) "\00a0\00a0";
  font-family: var(--mono);
  font-size: 0.62em;
  color: var(--ink-mark);
  letter-spacing: 0.1em;
}
.exposure p { font-size: 15px; color: var(--ink-70); margin: 0; }

/* ————— Steps ————— */
.steps { list-style: none; margin: 0; padding: 0; }
.steps li {
  display: grid;
  grid-template-columns: 78px minmax(0, 1fr);
  column-gap: 24px;
  padding: 22px 0;
  border-top: 1px solid var(--ink-12);
}
.steps li:last-child { border-bottom: 1px solid var(--ink-12); }
.step-n {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding-top: 5px;
}
.steps p { font-size: 15.5px; color: var(--ink-70); margin: 0; }

/* ————— Statement band —————
   Full-bleed dark. In an otherwise light document this is the one
   change of pace, and it carries the positioning line. */
/* The one image on the page: a guilloche engraving, the lathe-work
   printed on share certificates and banknotes. It sits in the band's
   empty right half at low opacity, so the page's single full-bleed
   moment gets the texture of a financial instrument rather than a flat
   navy rectangle. Screen blend means the near-black ground of the
   source drops out and only the linework survives, which saves cutting
   an alpha channel. */
.band {
  position: relative;
  overflow: hidden;
  background: var(--ink);
  color: rgba(232, 237, 243, 0.7);
  padding: 72px 0;
  margin: 0;
}
.band::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("/plate-guilloche.webp") right center / auto 150% no-repeat;
  mix-blend-mode: screen;
  opacity: 0.3;
  pointer-events: none;
}
.band .doc { max-width: var(--measure); position: relative; }
.band p {
  max-width: 30ch;
  margin: 0;
  font-size: clamp(24px, 3.4vw, 40px);
  line-height: 1.22;
  letter-spacing: -0.03em;
  text-wrap: pretty;
}
.band-wide { max-width: 42ch !important; }
.band b { color: #fff; font-weight: 400; }
.band-label {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(232, 237, 243, 0.35);
  margin: 0 0 26px;
}

/* ————— §07 Questions ————— */
.faq { margin: 0; }
.faq details { border-top: 1px solid var(--ink-12); }
.faq details:last-of-type { border-bottom: 1px solid var(--ink-12); }
.faq summary {
  font-size: 17.5px;
  padding: 18px 34px 18px 0;
  cursor: pointer;
  list-style: none;
  position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+";
  position: absolute;
  right: 2px; top: 17px;
  font-family: var(--mono);
  font-size: 18px;
  color: var(--ink-mark);
  transition: transform 0.2s ease;
}
.faq details[open] summary::after { transform: rotate(45deg); color: var(--ink); }
.faq details p {
  font-size: 15.5px;
  color: var(--ink-70);
  margin: 0 0 20px;
}

/* ————— Colophon (closer + full form) ————— */
.colophon { padding-bottom: 96px; }
.calc-label {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
  display: block;
  margin: 0 0 12px;
}
.calc-spend {
  font-family: var(--display);
  font-size: clamp(34px, 4.4vw, 56px);
  font-weight: 400;
  letter-spacing: -0.038em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  margin: 0 0 4px;
}
.calc-spend span:last-child { font-size: 0.32em; color: var(--ink-muted); letter-spacing: 0; }

/* Hairline track. Native range styling is replaced rather than themed
   so it matches the rules used everywhere else. */
.calc-range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  max-width: 420px;
  height: 24px;
  background: transparent;
  margin: 14px 0 0;
  display: block;
  cursor: pointer;
}
.calc-range::-webkit-slider-runnable-track {
  height: 1px;
  background: var(--ink-mark);
}
.calc-range::-moz-range-track { height: 1px; background: var(--ink-mark); }
.calc-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 3px; height: 22px;
  margin-top: -10px;
  background: var(--ink);
  border: 0;
  border-radius: 0;
}
.calc-range::-moz-range-thumb {
  width: 3px; height: 22px;
  background: var(--ink);
  border: 0;
  border-radius: 0;
}
.calc-scale {
  display: flex;
  justify-content: space-between;
  max-width: 420px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--ink-muted);
  margin: 2px 0 0;
}
.calc-out {
  margin: 34px 0 0;
  padding-top: 24px;
  border-top: 2px solid var(--ink);
}
.calc-fields {
  display: flex;
  align-items: stretch;
  margin: 28px 0 0;
  max-width: 480px;
  border-bottom: 2px solid var(--ink);
}
.calc-fields input {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--display);
  font-size: 16px;
  color: var(--ink);
  background: transparent;
  border: 0;
  padding: 11px 2px;
}
.calc-fields input::placeholder { color: var(--ink-mark); }
.calc-fields button {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  background: transparent;
  border: 0;
  padding: 11px 0 11px 18px;
  cursor: pointer;
}
.calc-fields button:hover { color: var(--recovered-text); }
.calc-fields button:disabled { color: var(--ink-mark); cursor: default; }

.alt-path {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-muted);
  margin: 32px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--ink-12);
}
.alt-path a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }

/* ————— Video ————— */
.film { margin: 0; }
.film video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--ink);
  border: 1px solid var(--ink-12);
}
.film figcaption {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--ink-12);
  margin-bottom: 14px;
}

/* ————— Footer ————— */
.footer {
  border-top: 2px solid var(--ink);
  padding: 26px 0 56px;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.footer p {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
  margin: 0;
}
.footer a { color: var(--ink-70); text-decoration: none; }
.footer a:hover { color: var(--ink); text-decoration: underline; }

/* ————————————————————————————————————————————————————————————
   Motion. Two signature moments and one quiet fade — nothing else.
   Only transform / opacity / clip-path are animated.
   ———————————————————————————————————————————————————————————— */

/* Quiet fade, carried over from app.js's .reveal contract. */
.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.55s ease, transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.reveal.in { opacity: 1; transform: none; }

/* Moment 1 — §01. The figure is typeset rather than animated: it wipes
   up from its own baseline as if being printed, and the rule beneath
   draws left to right. Nothing else on the first screen moves. */
/* The figure sets at line-height 0.84, so its glyphs overflow the
   content box by ~29px per side at the desktop size. Any residual clip
   therefore shaves the tops off the digits. The end inset clears the
   ascenders on its own, and report.js drops the clip entirely once the
   wipe finishes so nothing can crop the number at rest. */
.print {
  clip-path: inset(105% 0 0 0);
  transition: clip-path 0.85s cubic-bezier(0.22, 1, 0.36, 1);
}
.print.in { clip-path: inset(-32% 0 0 0); }
.print.done { clip-path: none; }
.draw {
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.75s cubic-bezier(0.22, 1, 0.36, 1) 0.18s;
}
.draw.in { transform: scaleX(1); }

/* Moment 2 — §05. Each confidence interval grows outward from the zero
   line, staggered. transform-origin is set per-row in report.js from
   the bar's own geometry so it really does originate at zero rather
   than at its left edge. The verdict is gated on the last interval
   settling: the animation enacts "nothing is recommended until the
   interval clears the margin". */
.sc-ci {
  transform: scaleX(0);
  transition: transform 0.62s cubic-bezier(0.22, 1, 0.36, 1);
}
.sc.drawn .sc-ci { transform: scaleX(1); }
.sc-ci i {
  opacity: 0;
  transition: opacity 0.3s ease;
}
.sc.settled .sc-ci i { opacity: 1; }
.sc-verdict {
  opacity: 0;
  transition: opacity 0.45s ease;
}
.sc.settled .sc-verdict { opacity: 1; }

/* ————— Responsive ————— */
@media (max-width: 1080px) {
  /* Rail collapses: the section number moves inline above its heading
     and marginalia flows underneath the body column. */
  .sect {
    grid-template-columns: minmax(0, 1fr);
    column-gap: 0;
    padding: 64px 0;
  }
  .sect > *, .sect > .rail, .sect > .marg, .sect > .wide, .notes { grid-column: 1; }
  .rail { position: static; display: flex; align-items: baseline; gap: 14px; margin-bottom: 24px; }
  .rail-num { font-size: 22px; margin: 0; }
  .rail-name { border-top: none; padding-top: 0; }
  .marg {
    border-left: none;
    border-top: 1px solid var(--ink-12);
    padding: 18px 0 0;
    margin-top: 30px;
    max-width: var(--body-col);
  }
  /* Single column here, so the row pin has to be released or the note
     would collide with the heading. */
  .marg-top { grid-row: auto; }
  .finding-figs { grid-template-columns: minmax(0, 1fr); }
  .notes { grid-template-columns: minmax(0, 1fr); }
  .masthead { grid-template-columns: auto minmax(0, 1fr) auto; column-gap: 28px; }
  .exposure { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
  .docs-shell { grid-template-columns: minmax(0, 1fr); }
  .toc {
    position: static;
    margin-bottom: 30px;
    padding-bottom: 22px;
    border-bottom: 1px solid var(--ink-12);
  }
  .toc ol {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 4px 24px;
    margin-top: 14px;
  }
  /* Tappable once it can collapse, with the same +/- affordance the FAQ
     accordions use on the landing pages. */
  .toc > summary {
    cursor: pointer;
    position: relative;
    padding-right: 24px;
    margin-bottom: 0;
    border-bottom: none;
    padding-bottom: 0;
  }
  .toc > summary::after {
    content: "+";
    position: absolute;
    right: 2px; top: -3px;
    font-family: var(--mono);
    font-size: 15px;
    color: var(--ink);
  }
  .toc[open] > summary::after { content: "\2212"; }
  /* The plates keep the full content width once the rail collapses,
     which is the one place they gain from the narrower layout. */
  .probs { column-gap: 32px; margin-top: 36px; }
}

@media (max-width: 720px) {
  :root { --gutter: 20px; }
  body { font-size: 16px; }
  .masthead-nav, .runhead { display: none; }
  .masthead { grid-template-columns: minmax(0, 1fr) auto; }
  .sect { padding: 52px 0; }
  /* Stacked: at 390px the three-column row left the interval about
     150px to live in, which is too narrow to read a confidence bound
     off. Label and value share a line above, and the track gets the
     full column — roughly double the width. */
  .sc-row {
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 12px;
    row-gap: 6px;
    padding: 14px 0;
  }
  /* Explicit placement: DOM order is label → track → value, so
     auto-placement pushed the value onto a third row of its own once
     the track spanned both columns. */
  .sc-label { font-size: 14px; grid-column: 1; grid-row: 1; }
  .sc-val { grid-column: 2; grid-row: 1; }
  .sc-track { grid-column: 1 / -1; grid-row: 2; }
  .sc-axis { margin-left: 0; margin-right: 0; }
  .steps li { grid-template-columns: minmax(0, 1fr); row-gap: 4px; }
  .step-n { padding-top: 0; }
  /* Field and CTA stack: side by side at 350px the button's uppercase
     label eats most of the row and leaves the input unusable. */
  .capture { flex-direction: column; gap: 16px; margin-top: 24px; }
  .capture button { width: 100%; padding: 14px 20px; }
  /* §01 tightens so the headline, the figure and the CTA all clear a
     667px viewport (iPhone SE). At the section's desktop rhythm the
     form landed 55px under the fold there. */
  .finding { padding-top: 30px; }
  .finding-figs { margin-top: 4px; padding-top: 20px; row-gap: 18px; }
  .band { padding: 52px 0; }
  /* Schedules can't reflow below this width without becoming unreadable,
     so they scroll in their own container rather than the page. The
     paired linear/radial gradients are the standard scroll-shadow
     trick: `local` backgrounds travel with the content and `scroll`
     ones don't, so the shadow appears only on the side that still has
     content off-screen. Without it the cut-off third column reads as a
     layout bug rather than as something you can swipe. */
  .scroll-x {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    background:
      linear-gradient(to right, var(--paper) 30%, rgba(232, 237, 243, 0)) left center,
      linear-gradient(to left, var(--paper) 30%, rgba(232, 237, 243, 0)) right center,
      radial-gradient(farthest-side at 0 50%, rgba(11, 18, 32, 0.14), transparent) left center,
      radial-gradient(farthest-side at 100% 50%, rgba(11, 18, 32, 0.14), transparent) right center;
    background-repeat: no-repeat;
    background-size: 34px 100%, 34px 100%, 13px 100%, 13px 100%;
    background-attachment: local, local, scroll, scroll;
  }
  .scroll-x .sched { min-width: 560px; }
  /* Schedule B is tightened rather than left at 560px because its
     rightmost column — the cost index — is the entire point of the
     table, and at 560px it sat two columns off-screen. Narrower cells
     and smaller figures bring it to the edge of the viewport, so it
     reads as one short swipe rather than as hidden data. */
  .scroll-x .sched-prices { min-width: 430px; }
  .sched-prices td { font-size: 14px; padding-right: 10px; }
  .sched-prices th { padding-right: 10px; }
  .sched-prices td.num { font-size: 12.5px; }

  /* Specimen rows stack: 150px + copy + delta does not survive a 335px
     content column. */
  .specimen li { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .specimen li > :last-child { text-align: left; }

  /* ————— Plates on a phone ————— */
  /* Once the rail collapses, a plate follows whatever marginalia was
     pushed above it, and two mono lines running together read as one
     broken paragraph. */
  .sect > .plate { margin-top: 34px; }
  .probs { grid-template-columns: minmax(0, 1fr); row-gap: 22px; margin-top: 30px; }

  /* Figure 1's labels come out of the drawing and become a legend under
     it. A 350px plot cannot hold three annotations without running them
     through the staircase they are annotating.
     `display: contents` on the plot wrapper is what lets the axis row
     stay directly beneath the chart with the legend after it, even
     though the notes are the wrapper's own children. */
  .drift { display: flex; flex-direction: column; }
  .drift-plot { display: contents; }
  .drift-svg { order: 1; }
  .drift-x { order: 2; }
  .drift-note {
    order: 3;
    position: relative;
    max-width: none;
    margin-top: 10px;
    padding-left: 21px;
  }
  /* The three placements are set at (0,2,0) outside this query, so they
     have to be unset at the same weight or they keep displacing the
     legend items. */
  .drift-note.is-paid, .drift-note.is-drift, .drift-note.is-floor {
    top: auto;
    left: auto;
  }
  .drift-note::before {
    content: "";
    position: absolute;
    left: 0; top: 0.5em;
    width: 14px; height: 2px;
    background: currentColor;
  }
  .drift-note.is-paid::before { background: var(--ink); }
  .drift-note.is-drift::before { height: 10px; top: 0.25em; background: var(--leaking-fill); }
  .drift-note.is-floor::before { background: var(--recovered); }
  .drift-x span:nth-child(2) { display: none; }
  /* The bar columns give up width first: the figure beside them is the
     value that has to survive, the bar is the comparison. */
  .rb-cell { width: 118px; }
  .lb-cell { width: 92px; }
  /* The diagram turns from a row into a column. Only the connectors
     need real work — everything else is already flex. */
  .flow-lane { padding: 14px 16px 16px; }
  .flow-row { flex-direction: column; align-items: stretch; gap: 0; }
  .flow-node { white-space: normal; text-align: center; }
  .flow-arr {
    flex: 0 0 auto;
    width: 1px;
    height: 20px;
    min-width: 0;
    margin: 0 auto;
  }
  .flow-arr::after {
    right: auto; top: auto;
    left: -3.5px; bottom: -1px;
    border-left: 4.5px solid transparent;
    border-right: 4.5px solid transparent;
    border-top: 6px solid var(--ink-mark);
    border-bottom: 0;
  }
  .flow-drop { padding-left: 24px; }
  .flow-drop::before { left: 24px; }
  .flow-drop::after { left: 20px; }
  .flow-drop span { padding-left: 14px; font-size: 11px; }

  .code-2 { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .code pre { font-size: 12px; padding: 18px 16px; }
  .anat { font-size: 12px; padding: 16px; }
  .cta-row { gap: 12px; }
  .cta-row .btn { width: 100%; text-align: center; }
  .spec-zone { padding: 16px 16px 18px; }
  .spec-code { font-size: 13px; }
  /* Stacked, the figure has to lead its own line rather than sit at the
     far right of a wrapped flex row, where it reads as orphaned. */
  .spec-line { flex-direction: column-reverse; align-items: flex-start; gap: 6px; }
  /* The pair keeps its reading order when it stacks: quality, then cost. */
  .spec-pair { flex-direction: column; gap: 20px; }
  .spec-facts { font-size: 11px; margin-bottom: 22px; }
}

/* Small phones. The emulated viewport is the full screen, but Safari's
   URL bar takes 60-100px of it on first paint, so §01 needs real slack
   rather than the 7px it had at 375x667. Everything above the CTA
   gives a little: masthead, section padding, headline floor, and the
   spacing inside the figure block. */
@media (max-width: 430px) {
  .masthead { padding: 16px 0 14px; }
  .wordmark { font-size: 22px; }
  .finding { padding-top: 20px; }
  h1 { font-size: clamp(27px, 7.4vw, 46px); }
  .fig { font-size: clamp(54px, 14vw, 136px); }
  .fig-mid { font-size: clamp(30px, 8vw, 54px); }
  .fig-label { margin-bottom: 8px; }
  .finding-figs { margin-top: 0; padding-top: 14px; row-gap: 12px; }
  .capture { margin-top: 20px; gap: 12px; }
  .form-fine { margin-top: 10px; }
}

/* ————— Reduced motion —————
   Both signature moments land in their final state, immediately. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  /* .print.in has to be named here too, or it outranks this rule and the
     figure stays clipped for reduced-motion users. */
  .print, .print.in { clip-path: none; transition: none; }
  .draw { transform: none; transition: none; }
  .sc-ci { transform: scaleX(1); transition: none; }
  .sc-ci i, .sc-verdict { opacity: 1; transition: none; }
  .btn, .faq summary::after { transition: none; }
}

.wordmark img { display: block; height: 25px; width: auto; }
