/* ==========================================================================
   Opportunity FLOPs — shared foundation
   Direction: "Working Paper No. 01" — an operable offprint. A research
   institute's working paper: running head, folio, section marks, numbered
   plates, hanging footnotes, spot inks printed on warm stock. Except the
   figures are live and the reader can operate them.

   Loaded by BOTH /index.html and /methodology.html, ahead of each page's own
   stylesheet. Everything that must look identical across the two pages lives
   here: fonts, tokens, the page shell, the sheet grid, and every shared
   primitive. Page-specific rules belong in styles.css (calculator) or
   methodology.css (the paper).

   Structure carried by rules and typography. There is no elevation in this
   system and there never will be.
   ========================================================================== */

/* --- Fonts ---------------------------------------------------------------
   Newsreader (display) + Fira Sans (body, controls, the whole small-caps
   apparatus) + Fira Mono (readouts) + STIX Two (mathematics).

   Fira replaced Barlow because the apparatus this paper needs is not
   stylistic — it is machinery, and Barlow does not have it. Fira Sans ships
   real `smcp`/`c2sc` small caps, `onum` old-style figures for prose,
   `lnum`+`tnum` tabular lining figures for readouts, and a full Greek
   lowercase, in all four cuts. Every face here was verified to carry those
   features before it was subset. See DESIGN.md § Typography for the route
   comparison and why Switzer, Karrik and Public Sans were each rejected.

   Self-hosted, subset to Latin + Latin-Ext-lite + Greek + the punctuation and
   maths relations the copy actually sets. No CDN, no @import, no build step:
   the woff2 files are committed, and the OFL text sits beside them.

   Newsreader ships no Greek and no maths operators, which is why --font-math
   points at STIX Two rather than at --font-display — see the note above the
   STIX faces below. Body-set Greek (β, γ, δ, λ in the parameter labels)
   resolves natively in Fira Sans and does not fall back mid-line.
   ------------------------------------------------------------------------ */

@font-face {
  font-family: "Fira Sans";
  src: url("/assets/fonts/FiraSans-Regular.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Fira Sans";
  src: url("/assets/fonts/FiraSans-Italic.woff2") format("woff2");
  font-style: italic;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Fira Sans";
  src: url("/assets/fonts/FiraSans-Medium.woff2") format("woff2");
  font-style: normal;
  font-weight: 500;
  font-display: swap;
}

@font-face {
  font-family: "Fira Sans";
  src: url("/assets/fonts/FiraSans-Bold.woff2") format("woff2");
  font-style: normal;
  font-weight: 700;
  font-display: swap;
}

@font-face {
  font-family: "Fira Mono";
  src: url("/assets/fonts/FiraMono-Regular.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Fira Mono";
  src: url("/assets/fonts/FiraMono-Medium.woff2") format("woff2");
  font-style: normal;
  font-weight: 500;
  font-display: swap;
}

/* Mathematics sets in STIX Two, not in Newsreader.

   Newsreader ships no Greek at all and no maths operators, so every β, γ, δ,
   λ and ∈ in the paper was resolving through --font-math's *fallback* — a
   second serif, mid-equation, in the most prominent typography on the page,
   and a different second serif on every reader's machine. Ṡ, ∈ and ∼ reached
   no shipped face whatsoever. That is a rendering fallback working correctly,
   not a typographic result.

   Setting mathematics in a face built for it is also just what scientific
   publishing does; display and mathematics being different faces is normal,
   not a compromise. STIX splits its own coverage — Text carries the alphabet,
   Greek and accented Latin, Math carries the operators — so both are here.
   The operator face is subset to U+2190–22FF alone and declares that range, so
   a page with no operators on it never fetches it. */

@font-face {
  font-family: "STIX Two Text";
  src: url("/assets/fonts/STIXTwoText-Regular.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "STIX Two Text";
  src: url("/assets/fonts/STIXTwoText-Italic.woff2") format("woff2");
  font-style: italic;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "STIX Two Math";
  src: url("/assets/fonts/STIXTwoMath-ops.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  unicode-range: U+2190-21FF, U+2200-22FF;
}

@font-face {
  font-family: "Newsreader";
  src: url("/assets/fonts/Newsreader-latin.woff2") format("woff2");
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Newsreader";
  src: url("/assets/fonts/Newsreader-latin-ext.woff2") format("woff2");
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113;
}

@font-face {
  font-family: "Newsreader";
  src: url("/assets/fonts/Newsreader-Italic-latin.woff2") format("woff2");
  font-style: italic;
  font-weight: 200 800;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Newsreader";
  src: url("/assets/fonts/Newsreader-Italic-latin-ext.woff2") format("woff2");
  font-style: italic;
  font-weight: 200 800;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113;
}

/* --- Tokens --------------------------------------------------------------
   The six chart plates are frozen, and their strokes read straight from
   --ink, --orange, --blue, --orange-soft, --panel and --rule-strong. Those
   seven keep their exact legacy values so the plots render unchanged.

   The register is risograph / two-colour offset: warm stock, a handful of
   saturated spot inks, visible overprint where they overlap, halftone screens
   instead of smooth fills. Every ink carries one meaning from the model
   everywhere it appears — see the Semantic Ink Rule in DESIGN.md.
   ------------------------------------------------------------------------ */

:root {
  color-scheme: light;

  /* ── FROZEN ────────────────────────────────────────────────────────────
     Referenced directly by the chart rules in styles.css. Retuning any of
     these silently alters all six plots. Add a token instead. */
  --ink: #101b2b;
  --panel: #fbfcfb;
  --rule: #c8d0d4;
  --rule-strong: #86919a;
  --orange: #c84e2f;
  --orange-soft: #f0d5ca;
  --blue: #245b96;
  /* ── END FROZEN ─────────────────────────────────────────────────────── */

  /* Ground. --paper resolves to #f5f1e9; --plate is the cool-white stock the
     figures print on, and nothing may tint, screen or grain it. */
  --paper: oklch(96% 0.012 88);
  --plate: var(--panel);
  --muted: #556070;

  /* ── The ink system ────────────────────────────────────────────────────
     Six inks, each with one fixed semantic job. Contrast ratios below are
     measured against --paper (#f5f1e9); the `-text` variants exist because
     the mark colour and the text colour of an ink cannot always be the same
     value and still clear WCAG 1.4.3.

       Ink        #101b2b  15.4:1  text, structural rules, baseline trajectory
       Orange     #c84e2f   4.1:1  CAPABILITY COST — Firm A, figure numbers
       Cobalt     #245b96   6.2:1  COORDINATION — Firm B, controls, focus
       Verdigris  #2e7253   5.1:1  SAFETY BENEFIT — the alignment channel
       Ochre      #a8791f   3.4:1  UNCERTAINTY — assumption bands, hatching
       Fluoro     #ff48b0   2.7:1  ART ONLY — masthead plate, devices, colophon

     Fluoro cannot clear 3:1 against either ground. That is not a defect to
     work around: it is the Loud Ink Rule enforced by the token itself. An ink
     that cannot legibly carry a value can never accidentally become one. */
  --orange-text: #9f381f;   /* 6.1:1 — cost, as text */
  --verdigris: #2e7253;     /* clears 4.5:1 as text unmodified */
  --verdigris-text: #215940;
  --ochre: #a8791f;         /* marks and hatching only */
  --ochre-text: #7a5610;    /* 5.9:1 — uncertainty, as text */
  --fluoro: #ff48b0;        /* drawn imagery only. Never a mark, never a control. */

  /* --ready was the old name for this ink and is deliberately NOT aliased.
     It was being read by the methodology page's status stamps, which made
     verdigris mean "safety benefit" in the figures and "implemented" in the
     margins — two meanings for one ink, which is exactly what the Semantic Ink
     Rule exists to prevent. Build status is --ink, and the one caveat stamp is
     --ochre-text, because that one really is marking uncertainty. */

  /* Type */
  --font-body: "Fira Sans", "Trebuchet MS", sans-serif;
  --font-display: "Newsreader", "Iowan Old Style", Charter, Georgia, serif;
  --font-math: "STIX Two Text", "STIX Two Math", "Times New Roman", Georgia, serif;
  --font-mono: "Fira Mono", ui-monospace, SFMono-Regular, Consolas, monospace;

  --t-display: clamp(2.6rem, 5.2vw, 4.75rem);
  --t-headline: clamp(1.9rem, 3.2vw, 2.9rem);
  --t-title: 1.3rem;
  --t-lead: clamp(1.05rem, 1.3vw, 1.2rem);
  --t-body: 1rem;
  --t-small: 0.875rem;
  --t-label: 0.7rem;
  --t-micro: 0.62rem;

  --track-caps: 0.09em;
  --track-wide: 0.16em;

  /* 62–72 characters. Never wider — brief §3.1 makes the cap hard.

     These are NOT 68 and 62. A `ch` is the advance width of "0", and in every
     face here the digits are appreciably wider than the average lowercase
     letter, so `68ch` of Fira Sans prose renders about 83 characters — over
     the cap on both pages, measured on the built page rather than assumed.
     The ratio is roughly 1.23 characters per ch for Fira Sans and differs per
     face, which is why the display-serif surfaces carry their own caps rather
     than sharing one token that cannot fit them all. */
  --measure: 56ch;        /* ≈69 characters of Fira Sans prose */
  --measure-tight: 50ch;  /* ≈61 characters, for the display serif */

  /* Space — multiples of the 24px body baseline */
  --s1: 8px;
  --s2: 12px;
  --s3: 16px;
  --s4: 24px;
  --s5: 32px;
  --s6: 48px;
  --s7: 72px;
  --s8: 96px;
  --s9: 144px;

  /* Shell */
  --shell: 1460px;
  --gutter: 24px;
  --margin-col: 200px;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* ── Print artifacts ───────────────────────────────────────────────────
     Paper grain: one fractal-noise tile, alpha baked into the SVG so it can
     be used as a plain background-image with no opacity or blend juggling.
     Applied once, to <body>, fixed to the viewport. It must never sit on a
     plate — plates carry an opaque --plate background, which is what keeps
     the White Plate Rule true structurally rather than by discipline. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)' opacity='0.055'/%3E%3C/svg%3E");

  /* Registration mark: a press target, rotationally symmetric so one drawing
     serves all four corners. Hairline, small enough to read as structure. */
  --reg-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%2386919a' stroke-width='0.7'%3E%3Cpath d='M6 0v3.4M6 8.6v3.4M0 6h3.4M8.6 6h3.4'/%3E%3Ccircle cx='6' cy='6' r='2.4'/%3E%3C/svg%3E");

  /* Halftone screens — see the .screen primitive below. A 45° staggered dot
     grid on an 8px tile, coarse enough to read as a printed screen at 100%.
     Masks, not images: one drawing tints to any ink. */
  --screen-10: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Ccircle cx='2' cy='2' r='1.05'/%3E%3Ccircle cx='6' cy='6' r='1.05'/%3E%3C/svg%3E");
  --screen-25: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Ccircle cx='2' cy='2' r='1.6'/%3E%3Ccircle cx='6' cy='6' r='1.6'/%3E%3C/svg%3E");
  --screen-45: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Ccircle cx='2' cy='2' r='2.15'/%3E%3Ccircle cx='6' cy='6' r='2.15'/%3E%3C/svg%3E");
  --screen-70: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Ccircle cx='2' cy='2' r='2.7'/%3E%3Ccircle cx='6' cy='6' r='2.7'/%3E%3C/svg%3E");

  /* Diagonal hatch — uncertainty. Ochre's job, per the Semantic Ink Rule. */
  --hatch: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7'%3E%3Cpath d='M-1 1L1 -1M0 7L7 0M6 8L8 6' stroke='%23000' stroke-width='1.4'/%3E%3C/svg%3E");

  font-family: var(--font-body);
  font-synthesis: none;
  color: var(--ink);

  /* The grain rides on the root, not on <body>, and the reason is paint order.
     A stacking context paints its own background first, then its negative
     z-index descendants, and only then the backgrounds of its in-flow block
     descendants. With the grain on <body> it painted *over* .reg-marks — which
     sits at z-index -1 so it can never land on a plate — and the registration
     marks silently disappeared. On the root it becomes the canvas background,
     below everything, and the marks sit between it and the content. */
  background-color: var(--paper);
  background-image: var(--grain);
  background-attachment: fixed;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-width: 320px;
  background: transparent;
  font-size: var(--t-body);
  line-height: 1.5;
}

button,
input,
select {
  font: inherit;
}

a {
  color: inherit;
}

/* --- Figure style --------------------------------------------------------
   Old-style figures in prose, tabular lining figures in readouts. This is
   the single most visible piece of typographic apparatus on the page and it
   is why the body face was changed: Fira Sans has both, Barlow had neither.
   ------------------------------------------------------------------------ */

p,
li,
dd,
blockquote,
figcaption,
.prose {
  font-variant-numeric: oldstyle-nums proportional-nums;
}

/* Small caps sit at cap height with a large x-height; lining figures beside
   them stand a head taller and break the line. Old-style figures match. */
.label,
.running-head,
.folio,
.section-mark {
  font-variant-numeric: oldstyle-nums;
}

/* Anything that is a value rather than a sentence. Values must align in a
   column and must not vary in width as they change. */
.numeric,
.readout,
output,
td,
th,
.tabular {
  font-variant-numeric: tabular-nums lining-nums;
}

/* Mathematics and single-letter variables set in the serif italic, the way
   they would be in print. Greek falls through to --font-math's fallback. */
i,
var {
  font-family: var(--font-math);
  font-style: italic;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 400;
  text-wrap: balance;
}

/* --- Shared typographic primitives --------------------------------------- */

/* Descriptive labels. Real small caps — Fira carries smcp and c2sc, so this
   is drawn, not synthesised, and font-synthesis: none stays honest. */
.label {
  color: var(--muted);
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 600;
  font-variant-caps: all-small-caps;
  letter-spacing: var(--track-caps);
}

.label-ink {
  color: var(--ink);
}

/* Values and readouts only. Monospace used as shorthand for "technical" is
   the tell of a design that has not chosen a voice. */
.readout {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--t-label);
  letter-spacing: 0.055em;
  text-transform: uppercase;
}

.lede {
  max-width: var(--measure-tight);
  color: var(--muted);
  font-size: var(--t-lead);
  line-height: 1.6;
}

/* The abstract, on both pages. It was the one surface where the two diverged:
   the paper set it in the display serif at 1.4rem and the calculator set it in
   the body sans at lead size, so the same kind of text looked like it came
   from two different publications. Set in the serif, larger than body, full
   ink — the printed convention, and B's treatment, lifted here so there is one
   definition rather than two.

   Both markup shapes are served: a <p class="abstract"> and an .abstract
   container with paragraphs inside. */
.abstract,
.abstract p {
  max-width: var(--measure-tight);
  font-family: var(--font-display);
  font-size: clamp(1.14rem, 1.7vw, 1.4rem);
  line-height: 1.42;
  text-wrap: pretty;
}

.abstract p {
  margin: 0 0 var(--s4);
}

.abstract p:last-child {
  margin-bottom: 0;
}

.measure {
  max-width: var(--measure);
}

/* --- Naming the inks in prose --------------------------------------------
   THE SEMANTIC INK RULE, used as a teaching device rather than as decoration.
   The two quantities the whole model produces are named in the first sentence
   of each page, and each is set in the ink it carries everywhere else. A
   reader who meets "capability cost" in oxidized orange in the headline finds
   the same orange on Firm A's trace, on the figure numbers, and on § 3 of the
   paper — the legend is learned before it is needed.

   Applied to the *term*, never to a whole sentence: an ink that spreads to
   the surrounding prose stops being a legend and becomes a mood. Both clear
   4.5:1 on the ivory ground as text; the brighter chart --orange never
   carries type, which is what --orange-text exists for.
   ------------------------------------------------------------------------ */

.ink-cost {
  color: var(--orange-text);
  font-style: inherit;
}

.ink-safety {
  color: var(--verdigris-text);
  font-style: inherit;
}

.ink-uncertainty {
  color: var(--ochre-text);
  font-style: inherit;
}

/* --- Rules ---------------------------------------------------------------
   Three weights, three jobs. Hairline --rule divides within a block; 1px ink
   bounds a structure; 2px ink is reserved for the masthead and the colophon.
   Reach for a rule, a weight change, or a surface change — never a shadow.
   ------------------------------------------------------------------------ */

.rule-hair {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: 0;
}

.rule-structural {
  border: 0;
  border-top: 1px solid var(--ink);
  margin: 0;
}

.rule-major {
  border: 0;
  border-top: 2px solid var(--ink);
  margin: 0;
}

/* --- Print artifacts: screens and overprint ------------------------------

   THE HALFTONE RULE. Tints are dot screens, never opacity fades and never
   gradients. `.screen` masks a solid ink through a 45° dot grid, so one
   drawing serves every ink and every density:

       <div class="screen screen-25" style="--screen-ink: var(--blue)">

   It works on an HTML box and on an SVG <g> alike, which is what lets drawn
   imagery take a printed tint without a second mechanism.

   THE OVERPRINT RULE. Where two inks overlap they multiply, producing a
   genuine third colour the way real spot inks do. Measured results on this
   palette: fluoro over cobalt gives #241a68, a deep violet; fluoro over
   oxidized orange gives #c81620; the soft orange wash over cobalt gives
   #224c77. Two saturated *solids* multiply to near-black (orange × cobalt is
   #1c1c1c), so overprint is applied to screens and light tints — which is
   also how the real process behaves, the inks being semi-transparent. Never
   fake it with a gradient.

   Neither may touch a figure plate. The frozen traces were chosen against
   near-white, and the White Plate Rule outranks both.
   ------------------------------------------------------------------------ */

.screen {
  --screen-ink: var(--ink);
  background-color: var(--screen-ink);
  -webkit-mask-image: var(--screen-25);
  mask-image: var(--screen-25);
  -webkit-mask-size: 8px 8px;
  mask-size: 8px 8px;
  mask-repeat: repeat;
}

.screen-10 { -webkit-mask-image: var(--screen-10); mask-image: var(--screen-10); }
.screen-25 { -webkit-mask-image: var(--screen-25); mask-image: var(--screen-25); }
.screen-45 { -webkit-mask-image: var(--screen-45); mask-image: var(--screen-45); }
.screen-70 { -webkit-mask-image: var(--screen-70); mask-image: var(--screen-70); }

/* Uncertainty hatching. Ochre's job — a band drawn in anything else is
   claiming a precision the model does not have. */
.hatch {
  --hatch-ink: var(--ochre);
  background-color: var(--hatch-ink);
  -webkit-mask-image: var(--hatch);
  mask-image: var(--hatch);
  -webkit-mask-size: 7px 7px;
  mask-size: 7px 7px;
}

.overprint {
  mix-blend-mode: multiply;
}

/* Blending escapes its stacking context and will pick up whatever happens to
   be behind it, including a plate. Wrap overprinted art in this. */
.ink-field {
  isolation: isolate;
}

/* --- Shell: running head -------------------------------------------------
   Every page carries the paper's running head above a hairline rule, in
   tracked small caps. It is the first thing that says "this is a document,
   not a landing page".

   The separators are generated rather than typed, so the four parts stay
   four parts: they wrap as units at narrow widths and a stray middot never
   ends up orphaned at the head of a line.
   ------------------------------------------------------------------------ */

.running-head {
  width: min(var(--shell), calc(100% - (var(--gutter) * 2)));
  margin: 0 auto;
  padding: var(--s2) 0 5px;
  display: flex;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--rule);
  color: var(--muted);
  font-size: var(--t-micro);
  font-weight: 500;
  font-variant-caps: all-small-caps;
  letter-spacing: var(--track-wide);
  line-height: 1.4;
}

.running-head span + span::before {
  content: "\00B7";
  margin: 0 0.6em 0 0.45em;
  color: var(--rule-strong);
}

/* The paper's own title carries the ink. */
.running-head span:first-child {
  color: var(--ink);
  font-weight: 600;
}

/* --- Shell: masthead ----------------------------------------------------- */

.topbar {
  width: min(var(--shell), calc(100% - (var(--gutter) * 2)));
  min-height: 68px;
  margin: 0 auto;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  padding-top: var(--s3);
  border-bottom: 2px solid var(--ink);
}

.wordmark {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3em;
  padding-block: 8px;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 1.22rem;
  text-decoration: none;
}

.wordmark span {
  font-weight: 400;
  font-style: italic;
}

.wordmark strong {
  font-weight: 600;
  font-variant-caps: all-small-caps;
  letter-spacing: 0.06em;
}

.topbar nav {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s4);
  padding-bottom: var(--s2);
}

/* Small caps at 0.7rem gives a ~17px tap height on its own. The padding is
   what makes these reachable on a touch screen; the negative inline margin
   keeps the optical alignment of the row unchanged. */
.topbar nav a {
  display: inline-block;
  padding: 14px 6px;
  margin-inline: -6px;
  color: var(--muted);
  font-size: var(--t-label);
  font-weight: 600;
  font-variant-caps: all-small-caps;
  letter-spacing: var(--track-caps);
  text-decoration: none;
}

.topbar nav a[aria-current="page"] {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 5px;
}

.topbar nav a:hover,
.topbar nav a:focus-visible {
  color: var(--orange-text);
  text-decoration: underline;
  text-underline-offset: 5px;
}

/* --- Shell: registration marks -------------------------------------------
   Press targets at the four corners of the sheet, in the trim margin outside
   the type area. Hairline, 16px, aria-hidden, purely structural — if they
   read as decoration they are too big.

   z-index: -1 is load-bearing, not tidiness. It puts the marks behind all
   content, which is what makes the White Plate Rule structural: a mark
   cannot print on top of a plate, because every plate carries an opaque
   --plate background. Removed below 1020px, where the gutter is no longer a
   margin and there is nowhere honest to put them.
   ------------------------------------------------------------------------ */

main {
  position: relative;
  width: min(var(--shell), calc(100% - (var(--gutter) * 2)));
  margin-inline: auto;
}

/* 6px in, 12px across, so the mark occupies x 6–18 and clears the 24px gutter
   the type starts at. At 16px it touched the first letter of the running head,
   which is a mark in the type area rather than in the trim margin. */
.reg-marks {
  position: fixed;
  inset: 6px;
  z-index: -1;
  pointer-events: none;
}

.reg-mark {
  position: absolute;
  width: 12px;
  height: 12px;
  background-image: var(--reg-mark);
  background-repeat: no-repeat;
}

.reg-mark-tl { left: 0; top: 0; }
.reg-mark-tr { right: 0; top: 0; }
.reg-mark-bl { left: 0; bottom: 0; }
.reg-mark-br { right: 0; bottom: 0; }

/* --- Shell: the sheet grid -----------------------------------------------
   One grid, used identically on both pages: a persistent 200px margin column
   carrying section marks, figure numbers, footnotes and status, beside the
   text column. Asymmetry is the point. It collapses below 1020px, where
   marginalia become inline asides rather than disappearing.
   ------------------------------------------------------------------------ */

.sheet {
  display: grid;
  grid-template-columns: var(--margin-col) minmax(0, 1fr);
  column-gap: var(--s6);
}

.sheet > * {
  grid-column: 2;
  min-width: 0;
}

/* Spans both columns — for a figure or diagram that needs the full sheet. */
.sheet > .bleed {
  grid-column: 1 / -1;
}

/* Section mark. Set in the margin column, never inline in the heading:
   `§ 1` beside the title is a printed convention, `§ 1 Overview` as a run-in
   heading is a web one. */
.section-mark {
  grid-column: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--s1);
  margin: 0;
  padding-top: 6px;
  color: var(--muted);
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 500;
  font-variant-caps: all-small-caps;
  letter-spacing: var(--track-caps);
  text-align: right;
}

.section-no {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-weight: 400;
  font-variant-numeric: lining-nums;
  letter-spacing: 0;
}

/* Hung in the margin, aligned to the first line of the block it annotates. */
.marginalia {
  grid-column: 1;
  margin: 0;
  padding-top: 3px;
  border-top: 1px solid var(--ink);
  color: var(--muted);
  font-size: var(--t-label);
  line-height: 1.45;
  hanging-punctuation: first;
}

.marginalia strong {
  display: block;
  margin-bottom: var(--s1);
  color: var(--ink);
  font-weight: 600;
  font-variant-caps: all-small-caps;
  letter-spacing: 0.08em;
}

/* --- Footnote apparatus --------------------------------------------------
   Superscript markers in prose, notes hung in the margin column at ≥1020px,
   collapsing to inline asides below that. Never dropped.

   `.fn-ref` is set on a <sup>, which already carries vertical-align: super
   and font-size: smaller. These declarations replace those values rather
   than compounding with them — a marker raised twice clears the ascender and
   collides with the line above.
   ------------------------------------------------------------------------ */

.fn-ref {
  font-family: var(--font-body);
  font-size: 0.64em;
  font-variant-numeric: lining-nums;
  font-weight: 600;
  line-height: 0;
  vertical-align: super;
}

.fn-ref a {
  padding: 0 0.15em;
  color: var(--orange-text);
  text-decoration: none;
}

.fn-ref a:hover,
.fn-ref a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* The note's own number, hung to the left of its first line so the note text
   aligns as a block — the printed convention, and the reason the marker is
   findable when scanning the margin. */
.fn-no {
  color: var(--orange-text);
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-variant-numeric: lining-nums;
  font-weight: 700;
}

.fn-back {
  float: left;
  margin-right: 0.45em;
  text-decoration: none;
}

.fn-back:hover .fn-no {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* --- Figure plates -------------------------------------------------------
   A plate is cool-white stock inset into the ivory page, numbered, with the
   live readout on a rule above the image and the caption below in the
   printed convention: figure number, then title, then the plotted quantity.
   A plate is never a card — the border is a printed boundary, and there is
   no fill behind it, no shadow under it, and no grid of them.
   ------------------------------------------------------------------------ */

.plate {
  margin: 0;
  padding: var(--s3) var(--s3) var(--s2);
  background: var(--plate);
  border: 1px solid var(--rule);
}

.plate-readout {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s2) var(--s4);
  padding-bottom: 6px;
  border-bottom: 1px solid var(--rule);
}

.plate figcaption {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--s3);
  align-items: baseline;
  padding-top: var(--s2);
  border-top: 1px solid var(--ink);
}

/* Figure numbers are oxidized orange because oxidized orange means capability
   cost, and the figures are the cost accounting. */
.fig-no {
  margin: 0;
  color: var(--orange-text);
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 700;
  font-variant-caps: all-small-caps;
  font-variant-numeric: lining-nums;
  letter-spacing: var(--track-caps);
  white-space: nowrap;
}

.plate figcaption h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.12rem;
  font-weight: 500;
}

.fig-sub {
  grid-column: 2;
  max-width: var(--measure-tight);
  margin: 4px 0 0;
  color: var(--muted);
  font-size: var(--t-small);
  line-height: 1.45;
}

/* --- Controls ------------------------------------------------------------
   Square-cornered, 34px minimum target, small-caps label, ink rule at rest,
   inverting to paper-on-ink. No radius, no fill, no shadow.
   ------------------------------------------------------------------------ */

.button {
  min-width: 54px;
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid var(--ink);
  color: var(--ink);
  background: transparent;
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 600;
  font-variant-caps: all-small-caps;
  letter-spacing: 0.08em;
  cursor: pointer;
  transition: background 140ms var(--ease-out), color 140ms var(--ease-out);
}

.button:hover {
  background: var(--ink);
  color: var(--paper);
}

/* --- Pull quote ---------------------------------------------------------- */

.pull {
  max-width: var(--measure-tight);
  margin: var(--s5) 0;
  padding-left: var(--s3);
  border-left: 2px solid var(--ink);
  font-family: var(--font-display);
  font-size: 1.3rem;
  line-height: 1.3;
  hanging-punctuation: first last;
}

/* --- Channel diagram -----------------------------------------------------
   Reallocation runs down two parallel channels. Drawn in CSS rather than
   shipped as an image so it scales with the type stack, reflows at narrow
   widths, and stays selectable text. Shared — usable on either page.
   ------------------------------------------------------------------------ */

.channels {
  margin: 0;
}

.channels-source {
  position: relative;
  width: fit-content;
  margin: 0 auto var(--s6);
  padding: var(--s2) var(--s4);
  border: 1px solid var(--ink);
  font-family: var(--font-display);
  font-size: 1.15rem;
  text-align: center;
}

.channels-source::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: 1px;
  height: var(--s6);
  background: var(--ink);
}

.channels-split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s6);
}

.channel {
  min-width: 0;
  padding-top: var(--s4);
  border-top: 1px solid var(--ink);
  text-align: center;
}

.channel h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 500;
}

.channel ol {
  margin: var(--s3) 0 0;
  padding: 0;
  list-style: none;
}

.channel li {
  font-family: var(--font-display);
  font-size: 1.02rem;
  line-height: 1.3;
}

.channel li + li {
  margin-top: var(--s2);
}

.channel li + li::before {
  content: "\2193" / "";
  display: block;
  margin-bottom: var(--s2);
  color: var(--rule-strong);
  font-family: var(--font-body);
  font-size: 0.9rem;
  line-height: 1;
}

.channel-status {
  margin: var(--s4) 0 0;
  padding-top: var(--s2);
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 600;
  font-variant-caps: all-small-caps;
  letter-spacing: var(--track-caps);
}

/* A channel resting on a working prior rather than a supplied specification
   is marked in ochre — the uncertainty ink — and says so in words, because
   colour alone must not carry this. */
.channel-status[data-state="pending"],
.channel-status[data-state="prior"] {
  color: var(--ochre-text);
}

@media (max-width: 680px) {
  .channels-split {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s5);
  }
}

/* --- Shell: folio --------------------------------------------------------
   Section mark, short title, and the ordinal in the printed convention. A
   scrolling document's section is the analogue of a printed page, so the
   paper sets one at the foot of every section; the calculator sets one at
   the foot of the sheet.
   ------------------------------------------------------------------------ */

.folio {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s2) var(--s4);
  margin: var(--s5) 0 0;
  padding-top: 5px;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-size: var(--t-micro);
  font-weight: 500;
  font-variant-caps: all-small-caps;
  letter-spacing: var(--track-wide);
}

.folio-mark {
  color: var(--ink);
  font-weight: 600;
}

/* Pushed to the trailing edge, the way a page number sits at the outer
   corner of the folio line. */
.folio-no {
  margin-left: auto;
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: 0.08em;
}

/* The sheet's own folio, at the foot of the page above the colophon. */
.folio-sheet {
  width: min(var(--shell), calc(100% - (var(--gutter) * 2)));
  margin: var(--s7) auto 0;
  border-top-color: var(--ink);
}

/* --- Shell: colophon ----------------------------------------------------- */

footer {
  width: min(var(--shell), calc(100% - (var(--gutter) * 2)));
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s3) var(--s6);
  align-items: baseline;
  margin-top: var(--s4);
  padding: var(--s4) 0 var(--s6);
  border-top: 2px solid var(--ink);
  color: var(--muted);
  font-size: var(--t-small);
}

footer p {
  margin: 0;
}

footer p:first-child {
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--ink);
}

.footer-links {
  justify-self: end;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s3);
}

.footer-links a {
  display: inline-block;
  padding: 14px 6px;
  margin-inline: -6px;
  color: var(--muted);
  font-size: var(--t-label);
  font-variant-caps: all-small-caps;
  letter-spacing: var(--track-caps);
  text-decoration: none;
}

.footer-links a:hover,
.footer-links a:focus-visible {
  color: var(--orange-text);
  text-decoration: underline;
  text-underline-offset: 5px;
}

/* --- Focus --------------------------------------------------------------- */

:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 2px;
}

/* Bypass block (WCAG 2.4.1). Hidden until focused, then sets as a plain
   ink-on-paper label at the top-left of the sheet. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  padding: var(--s2) var(--s3);
  border: 1px solid var(--ink);
  background: var(--plate);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 600;
  font-variant-caps: all-small-caps;
  letter-spacing: var(--track-caps);
  text-decoration: none;
}

.skip-link:focus {
  left: var(--gutter);
  top: var(--s2);
}

/* Visually hidden but announced. For the live text equivalents that sit
   beside decorative drawn imagery. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Entrance ------------------------------------------------------------
   One orchestrated page-load reveal in reading order, not scattered
   micro-interactions. Only opacity and transform are animated. Each page
   stages its own sequence from these keyframes; the reduced-motion block at
   the bottom of this file neutralises all of it.

   THE CONTAINING BLOCK RULE. The apparatus fades but never translates. Any
   transform value creates a containing block, identity matrices included,
   which breaks the sticky control margin inside it.
   ------------------------------------------------------------------------ */

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.running-head {
  animation: fade 520ms var(--ease-out) both;
}

.topbar {
  animation: fade 520ms var(--ease-out) 60ms both;
}

/* --- Responsive ---------------------------------------------------------- */

@media (max-width: 1020px) {
  :root {
    --gutter: 16px;
  }

  main {
    max-width: 900px;
  }

  .sheet {
    grid-template-columns: minmax(0, 1fr);
  }

  .sheet > *,
  .sheet > .bleed {
    grid-column: 1;
  }

  /* Off the margin, a hanging note becomes an inline aside rather than
     disappearing — the caveat is the point, not the placement. */
  .marginalia {
    max-width: var(--measure);
    margin-block: var(--s3) var(--s4);
    padding-left: var(--s3);
    padding-top: 0;
    border-top: 0;
    border-left: 2px solid var(--ink);
  }

  .note {
    max-width: var(--measure);
    margin-block: var(--s2) var(--s3);
    padding-left: var(--s3);
    padding-top: 0;
    border-top: 0;
    border-left: 1px solid var(--rule);
  }

  /* The section mark runs in above its heading rather than beside it. It has
     to un-stack as well as re-align: at full width it is a column with the
     status stamp under the mark, and leaving flex-direction alone here left
     the mark, the stamp and the heading as three cramped lines at 360px. */
  .section-mark {
    display: flex;
    flex-direction: row;
    align-items: baseline;
    gap: var(--s2);
    padding-top: 0;
    text-align: left;
  }

  .section-mark b {
    font-size: 1.1rem;
  }

  /* No trim margin left to print them in. */
  .reg-marks {
    display: none;
  }
}

@media (max-width: 760px) {
  html {
    scroll-behavior: auto;
  }

  .topbar {
    flex-wrap: wrap;
    gap: var(--s2) var(--s4);
  }

  .running-head {
    font-size: 0.58rem;
    letter-spacing: 0.12em;
  }

  footer {
    grid-template-columns: minmax(0, 1fr);
  }

  .footer-links {
    grid-column: 1;
    grid-row: auto;
  }
}

/* Killing duration alone is not enough. The entrance staggers with delays of
   up to 280ms and animation-fill-mode: both, so with duration near zero but
   the delay intact, those elements still sat on their opening keyframe —
   invisible and offset — for the length of the delay before snapping into
   place. Someone who asked for no motion got a flash of blank page instead.
   The delays have to go to zero too. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }
}
