/* metalign handbook — the second page, and the only one that scrolls.
   No build step and no dependencies, like everything else here.

   This sheet is loaded *after* css/site.css and does not replace it. site.css owns the
   five @font-face rules, the palette, the base type, .reader-only and the wordmark, so the
   handbook renders in the same ink as the landing page and there is exactly one copy of
   the brand in this repo. What follows undoes the one-screen poster layout — the svh grid,
   the centred main, the footer pinned to the bottom edge — and turns it into a document.

   Everything here is scoped to .handbook on <body>, so nothing in this file can reach
   index.html even by accident. Where a rule fights one of site.css's, it wins on
   specificity rather than on order: site.css's height-keyed media queries at the bottom of
   that file still tighten what they can, and a one-class selector there loses to a
   two-class selector here whatever the viewport is doing.

   ── The form ─────────────────────────────────────────────────────────────────────────────
   A published film datasheet. The discipline the form actually carries — and the reason it
   was chosen over an editorial page for a document that is looked *up* as much as read — is
   that **every fact has a coordinate**: a clause number, a table row, a figure number.
   Nothing floats. Three rules follow from that and are worth stating once here rather than
   defending at every declaration below:

   1. **One rail, one left edge.** Clause numbers hang left; every heading title and every
      line of body text on the page shares the same left edge, from §1 to the footer. That
      alignment is the whole composition — it is why there is no sidebar and no card. The
      margin index further down this file is not a counter-example: it is fixed out in the
      window's own margin at 90rem and up, and the sheet neither moves nor narrows for it.
   2. **Fields, not floating hairlines.** A table head is a filled ground; a notice is a
      bounded box. Ruled fields are what a datasheet uses to separate things, and they carry
      structure at page scale rather than whispering it.
   3. **Amber is a fault, and nothing else.** It marked two things until 24.08.2026 — what you
      can act on, and what is wrong — and the first half was the bigger half, drawing every link
      underline on the page. Then the landing page's monochrome finishes went site-wide, which
      left the working colour as the ink and links reading by the weight of the rule under them.
      So the amber narrowed to the half that had nowhere else to go and was renamed for it:
      `var(--fault)`, twice on this page — §4's major notice, and the rows Fig. 6's ladder
      raises a warning about. `var(--accent)` still exists and is the ink; anything below still
      naming it means "what you act on", and none of it is amber any more.

   The warmth is in the content and the type — XCharter titles, prose set at a real measure,
   the app's own voice — and the rigour is in the frame. A datasheet that is only rigour
   reads as a specification for a product nobody loves, which is the one risk this direction
   was chosen with its eyes open about. */

/* ---------- Tokens this page adds ---------- */

.handbook {
  /* The prose measure, and the rail the clause numbers hang in. --doc is the *text* column;
     the page box is --doc + --num, so a heading's number sits outside the measure and its
     title starts on the same edge as the paragraph below it.

     37rem lands Instrument Sans at about 68 characters, inside the 65–75 a long technical
     read wants. The old sheet used 44rem, which measured nearer 88 — comfortable for a
     magazine column and too wide for a page somebody scans for one tag name. */
  --doc: 37rem;
  --num: 3.5rem;

  /* Wider than the prose, for the three things that are not prose: a table, the route blocks,
     and the pairing ladder. A technical document has always done this — the text column is set
     for reading and the tables are set for the data — and measuring it showed why it is not
     optional here. Held to --doc, the three route blocks came out at 190px each and wrapped
     every bullet after three words, and the §6 matrix put four columns into 592px. The prose
     keeps --doc; only these break out, and they break out to the right from the same left edge,
     so the rail still runs unbroken down the page. */
  --wide: 50rem;

  /* The grounds (--band, --field) and the heavier rule (--rule-2) lived here while only
     this page set ruled fields. The landing page sets them too now, so all three moved to
     site.css's tokens (23.08.2026) — one copy of the palette, living with the rest of it. */

  /* How tall a phone screenshot is allowed to be. A screenshot is the one thing on this page
     that has to be sized by its *height*: a phone is about 0.46 as wide as it is tall, so a
     capture given the full measure would stand 80rem high and the reader would scroll four
     screens to get past one picture. Cap the height, let the width follow. */
  --shot: 26rem;

  /* One row of the pairing ladder (Fig. 6). Fixed, because the figure's whole mechanism is
     that the left column moves by exactly two rows — see .ladder. */
  --rung: 3.5rem;
}

/* ---------- Undoing the one screen ---------- */

.handbook {
  /* site.css sizes the body to the viewport and splits it into a 1fr row plus a footer.
     A document is as tall as it is. */
  display: block;
  min-height: 0;
  row-gap: 0;

  /* Every number on this page is either a coordinate (a clause, a figure, a row) or a
     measurement, and both want to line up in a column rather than set prettily. This is one
     of the browser surfaces that ships with no design system attached until somebody says
     so — the same reason site.css themes the selection. */
  font-variant-numeric: tabular-nums;
}

/* The page box: the widest thing on the page plus the rail its clause numbers hang in. Every
   top-level block gets the same box and therefore the same left edge — that shared edge is the
   composition, so the blocks are never centred independently of one another. What varies is
   how much of the box each thing *uses*, and that is set just below. */
.handbook .sheet-head,
.handbook .contents,
.handbook .sec,
.handbook footer {
  width: 100%;
  max-width: calc(var(--wide) + var(--num));
  margin-inline: auto;
}

/* Prose holds the measure. Everything here is left-aligned inside the wider box, so capping it
   pulls the right edge in and never moves the left one. */
.handbook .standfirst,
.handbook .sheet-id,
.handbook .sec > p,
.handbook .sec > ul,
.handbook .sec > ol:not(.procedure),
.handbook .step > p,
.handbook .step > ul,
.handbook .step > .gloss,
.handbook .notice,
.handbook .fig:not(.fig-wide),
.handbook footer p {
  max-width: var(--doc);
}

/* Headings start one rail to the left of the prose, so their cap includes it — otherwise a
   title would measure --num narrower than the paragraph under it. */
.handbook h2,
.handbook .step > h3,
.handbook .contents ol {
  max-width: calc(var(--doc) + var(--num));
}

/* Everything indents by the rail, so the whole sheet has one text edge and one margin — the
   masthead's title, the contents' entries, all seven sections and the footer.

   This used to apply to the numbered sections alone, on the reasoning that the document's own
   title should span the full box. That reasoning was wrong in a way only a wide screen shows:
   it left the top of the page sitting a whole rail to the left of every word below it, so the
   masthead and the body had two different left edges. At a 3.5rem rail that read as a masthead
   spanning wide; at 7rem it read as a mistake, which is what it always was. What hangs in the
   margin is the apparatus — clause numbers, figure numbers, the edition mark — and nothing
   else crosses into it. */
.handbook .sheet-head,
.handbook .contents,
.handbook .sec,
.handbook footer {
  padding-left: var(--num);
}

/* The edition mark, out in the margin beside the title. Hidden until the rail is wide enough to
   be a margin — see the wide-screen query near the end of this file. */
.handbook .sheet-head { position: relative; }

.handbook .sheet-mark {
  display: none;
  position: absolute;
  left: 0;
  top: 0.4rem;
  width: var(--num);
  margin: 0;
  padding-right: 0.75rem;
  font-family: var(--sans);
  /* 0.78rem, like every other muted-uppercase-letterspaced label on this page — the floor this
     file states for that combination in two other places and which this one was set below.
     Small type in a margin invites being made smaller; three things are already working against
     legibility here before the size does. */
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.5;
  color: var(--muted);
}

.handbook .sheet-mark span { display: block; font-weight: 400; letter-spacing: 0.02em; }

.handbook main {
  /* Not the centred grid of gaps site.css gives it: the sections set their own rhythm. */
  display: block;
  align-self: auto;
  max-width: none;
  margin: 0;
}

/* p { margin: 0 } is right for six blocks laid out on a grid and wrong for prose. */
.handbook p,
.handbook ul,
.handbook ol {
  margin: 0 0 1.1em;
}

.handbook li + li { margin-top: 0.5em; }
.handbook ul, .handbook ol { padding-left: 1.15em; }
.handbook li::marker { color: var(--muted); }

/* Every bulleted list on this page sits inside <li class="step"> or inside the procedure's
   <ol>, and the UA stylesheet's `ol ul, ul ul { list-style-type: circle }` therefore caught all
   of them: the steps and the three route blocks were drawn with hollow rings rather than the
   discs a first-level list gets. Nothing here is a nested list conceptually — they are all
   first-level lists that happen to live inside a numbered clause — so they are set that way. */
.handbook .step ul,
.handbook .route ul,
.handbook .sec > ul { list-style-type: disc; }

/* ---------- Skip link ----------
   The page opens on a masthead and a contents table; a keyboard reader who wants neither
   should not have to tab through eight links to reach the document. Visible only on focus,
   and drawn in the same field the notices use so it belongs to the page rather than to the
   browser. */

.handbook .skip {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -120%);
  z-index: 120;
  padding: 0.6rem 1.1rem;
  background: var(--field);
  color: var(--ink);
  border: 1px solid var(--rule-2);
  border-top: none;
  /* 0.3rem, the one radius the bounded fields on this page use. 0.4 was a value invented for
     this element alone. */
  border-radius: 0 0 0.3rem 0.3rem;
  font-size: 0.9rem;
  text-decoration: none;
}

.handbook .skip:focus-visible { transform: translate(-50%, 0); }

/* ---------- The running head ----------
   A datasheet prints its section at the head of every page. A scrolling document has no
   pages, so this band does that job — and with the margin index below it, this page's
   wayfinding is still the deliberate alternative to the sidebar-plus-anchor-rail that every
   documentation template ships. A contents table at the top says what is here; the running
   head says where you are; the margin index says where else there is. None of the three costs
   a permanent column of the screen, which is the whole test.

   [hidden] in the markup and un-hidden by the script, so a reader with no JavaScript is
   never shown an empty bar. */

.handbook .runhead {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  /* Full-bleed out of the padded body, so the band reaches both edges of the window the way
     a printed running head reaches its margins. */
  margin: calc(-1 * var(--pad)) calc(-1 * var(--pad)) 0;
  padding: 0.7rem var(--pad);
  background: var(--paper);
  border-bottom: 1px solid transparent;
  font-family: var(--sans);
  font-size: 0.82rem;
  color: var(--muted);
  /* Until a section owns it the band is invisible ink on the paper it sits on; it appears
     with the first section rather than hanging over the masthead saying nothing. */
  opacity: 0;
  transition: opacity 240ms cubic-bezier(0.16, 1, 0.3, 1);
}

.handbook .runhead.is-live {
  opacity: 1;
  border-bottom-color: var(--rule);
}

.handbook .runhead-doc { font-style: italic; font-family: var(--serif); }

.handbook .runhead-sec {
  color: var(--ink-2);
  font-weight: 600;
  /* The § and the title are separated by two spaces in the script's string; without this the
     browser collapses them to one. */
  white-space: pre;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- The margin index ----------
   The running head's other half, and the answer to a real gap in the one above: the head names
   the section you are in and takes you nowhere, so a reader who wanted §5 from inside §2 had to
   scroll back to the top of the document to find the contents table. Wayfinding that only
   reports is half a device on a page whose whole claim is that it is looked up as much as read.

   It is a margin index and not a sidebar, and that distinction is the only reason it can exist
   on this sheet. It is fixed out in the *window's* margin at 90rem and up, where the page box is
   63rem and there is 13.5rem of nothing on either side of it. The document does not move, does
   not narrow, and does not gain a second left edge: the apparatus simply reaches further out
   than the clause numbers already do, which is where a printed sheet has always kept its index.
   Below 90rem there is no margin to put one in, so there is none — and nothing down there is
   any worse than it was.

   Fixed rather than sticky: it is furniture of the window, not of the sheet. */

.handbook .doc-index { display: none; }

/* Only where the numbers actually work. 90rem is the first width at which the index (9rem) and
   its gap to the sheet (2rem) fit inside the 13.5rem the centred page box leaves, with 2.5rem
   still between the index and the window edge — measured at 1440, which is the width this is
   tightest at and the one a reader is most likely to be at. Wider, all of the slack goes to that
   outer margin and none of it to the gap, because the gap is the relationship that matters: a
   constant 2rem is what makes this read as the sheet's own margin rather than as a panel
   floating near it. That is also why the offset is measured from the centre line in the tokens
   rather than written as a literal.

   The height floor keeps it off a short landscape window, where seven entries would run into the
   bottom of the screen. */
@media (min-width: 90rem) and (min-height: 34rem) {
  .handbook .doc-index {
    display: block;
    position: fixed;
    /* Clear of the running head's band, which stands about 2.6rem tall where it sits. */
    top: 4.75rem;
    /* Measured from the centre line rather than from the window edge, so it tracks the sheet
       instead of the viewport: half the page box, then the gap. 100vw is deliberately not used
       — it counts the classic scrollbar on the browsers that draw one, and this column would
       drift left by its width on exactly those. */
    right: calc(50% + (var(--wide) + var(--num)) / 2 + 2rem);
    width: 9rem;
    z-index: 40;
  }
}

/* Visible with JavaScript off, faded until §1 with it on — and the test for which is the head's
   own [hidden], with no second flag to keep in step. The band ships hidden and is un-hidden only
   once the observer is running, so `:not([hidden])` means exactly "the script is doing this
   page's wayfinding". Under it, the index waits for the first section the way the head does,
   instead of standing beside the contents table repeating it. Without it, the index is simply
   there, which is what a printed index does anyway.

   visibility rides along so the links are not clickable while they are invisible: it flips to
   visible at once on the way in, and waits out the fade on the way back. */
.handbook .runhead:not([hidden]) ~ .doc-index {
  opacity: 0;
  visibility: hidden;
  transition: opacity 240ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s 240ms;
}

.handbook .runhead.is-live ~ .doc-index {
  opacity: 1;
  visibility: visible;
  transition: opacity 240ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s;
}

/* No heading of its own — a margin does not title itself, and "Contents" already names the
   table at the top of the page. The rule the contents table opens with comes along, because it
   is what says a list of rows is a bounded thing rather than seven links that happen to be near
   each other. */
.handbook .doc-index ol {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--rule-2);
}

.handbook .doc-index li { margin: 0; }

/* The same two-track row as the contents table, at apparatus scale: the number in a column of
   its own, the title on one edge beside it. Every list of sections on this page is built this
   way, which is what makes them read as the same list seen twice rather than as two lists. */
.handbook .doc-index a {
  display: grid;
  grid-template-columns: 1.4rem minmax(0, 1fr);
  align-items: baseline;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--rule);
  font-family: var(--sans);
  font-size: 0.82rem;
  line-height: 1.35;
  color: var(--ink-2);
  text-decoration: none;
  text-wrap: pretty;
}

.handbook .doc-index .ix-no { font-weight: 600; color: var(--muted); }

/* Where you are, drawn the way this page draws every other boundary: a filled ground, not a
   coloured mark. Nothing about the metrics changes with it — a bolder face here would rewrap
   the entry and shuffle the whole column as you scrolled past a section, which is the one thing
   a fixed margin element must never do. The state is named in the markup as well as in tone:
   aria-current is on the link, so a reader who cannot see the ground is not guessing. */
.handbook .doc-index [aria-current] {
  background: var(--band);
  color: var(--ink);
}

.handbook .doc-index [aria-current] .ix-no { color: var(--ink); }

/* Amber under the pointer rather than at rest. The contents table earns seven underlines at
   rest — it is the first viewport's only actionable thing, and a hover-only signal is no signal
   at all on a phone. This list is the opposite case on both counts: it exists only above 90rem,
   which is a pointer, and seven amber rules stacked in a 9rem column would have the margin
   shouting across the gap at the sheet. The row takes the same ground the contents row takes,
   so the two lists answer a cursor identically. */
.handbook .doc-index a:hover { background: var(--band); color: var(--accent); }
.handbook .doc-index a:hover .ix-no { color: var(--accent); }

/* ---------- The masthead ---------- */

.handbook .sheet-head {
  padding-bottom: 1.5rem;
  margin-bottom: 2.5rem;
  border-bottom: 2px solid var(--ink);
}

.handbook .home {
  display: inline-block;
  text-decoration: none;
}

/* Well below the landing page's 5rem cap: there the mark carries the whole screen, here it
   is a way back to it. The -0.4em that drops the pronunciation into the descender of the
   'g' has nothing to sit under on this page, so it goes. */
.handbook .wordmark {
  font-size: clamp(1.6rem, 5.5vw, 2rem);
  margin: 0;
}

.handbook h1 {
  font-family: var(--serif);
  font-weight: 700;
  font-size: clamp(2.25rem, 8vw, 3.25rem);
  line-height: 1.05;
  letter-spacing: -0.015em;
  margin: 0.5rem 0 0;
}

.handbook .standfirst {
  font-size: 1.1rem;
  color: var(--ink-2);
  margin: 0.9rem 0 0;
  max-width: var(--doc);
  text-wrap: pretty;
}

/* ---------- The identity block ----------
   What the sheet describes and when it was checked. A datasheet opens with this because a
   technical document that cannot be dated cannot be trusted, and every row of it was already
   somewhere in this page's prose — the block gives each one a coordinate instead.

   Set as a two-column ruled field rather than a paragraph: the label column is the point.
   Below the phone breakpoint it stacks, because a 7rem label column against a 20rem value
   column wraps every value to three lines. */

.handbook .sheet-id {
  margin: 1.75rem 0 0;
  border: 1px solid var(--rule-2);
  border-radius: 0.3rem;
  overflow: hidden;
  font-size: 0.9rem;
}

.handbook .sheet-id > div {
  display: grid;
  grid-template-columns: 9rem minmax(0, 1fr);
}

.handbook .sheet-id > div + div { border-top: 1px solid var(--rule); }

.handbook .sheet-id dt {
  padding: 0.55rem 0.9rem;
  background: var(--field);
  border-right: 1px solid var(--rule);
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  /* --ink-2, not --muted: on --field the muted tone measures 3.60:1 and owes 4.5. */
  color: var(--ink-2);
}

.handbook .sheet-id dd {
  margin: 0;
  padding: 0.55rem 0.9rem;
  color: var(--ink-2);
}

/* ---------- Contents ----------
   The old sheet had none, on seven sections holding a fourteen-row tag map and a three-way
   comparison, and said so in a comment: the argument was that a link belongs where the
   question is asked. It does — and those in-context links are all still here — but that is
   an argument for cross-references, not against ever telling the reader what is on the page.

   A table rather than a list, because each entry carries a second column saying what the
   section answers, and because the numbers want a column to line up in. */

.handbook .contents {
  margin-bottom: 3rem;
}

.handbook .contents h2 {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.6rem;
}

/* Pulled left by the rail it then re-establishes as each row's first track — the same move the
   headings and the figure captions make, so the contents' seven numbers hang in the margin
   with every other coordinate on the page and the entry titles land on the shared text edge.

   The negative margin belongs in this declaration and not in an earlier one: `margin: 0` here
   is a shorthand, and it silently reset a `margin-left` set anywhere above it. */
.handbook .contents ol {
  margin: 0 0 0 calc(-1 * var(--num));
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--rule-2);
}

.handbook .contents li { margin: 0; }

.handbook .contents a {
  display: grid;
  grid-template-columns: var(--num) minmax(0, 1fr);
  align-items: baseline;
  gap: 0 0;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--rule);
  text-decoration: none;
  color: var(--ink);
}

/* Underlined at rest, not on hover. These seven links are the only thing on the first viewport
   you can act on — signalling that only on hover meant the opening screen carried none of the
   page's own signal, and meant nothing at all on a touch screen, where there is no hover to
   reach it with.

   It arrives as an underline rather than as coloured text, which is exactly how site.css marks
   every link on the site: the word in ink, the statement in the rule under it. **The rule used
   to be amber and is ink now** (24.08.2026) — the finishes went site-wide and took the hue off
   links with them, so what distinguishes these seven from the surrounding prose is the underline
   itself rather than its colour, and what a pointer changes is its weight. The reasoning is
   unchanged and so is the mechanism; only the palette moved under it. */
.handbook .contents .c-title {
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

.handbook .contents a:hover { background: var(--band); color: var(--ink); }
.handbook .contents a:hover .c-title { color: var(--accent); }

.handbook .c-no {
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--muted);
}

.handbook .c-title {
  font-family: var(--serif);
  font-weight: 700;
  font-size: 1.05rem;
}

.handbook .c-note {
  grid-column: 2;
  font-size: 0.85rem;
  color: var(--muted);
  text-wrap: pretty;
}

/* ---------- Sections and their numbers ----------
   The rail. Every heading is a two-column grid whose first track is --num and whose second
   holds the title; the heading is then pulled left by --num out of the section's own
   padding. The effect is that a clause number hangs in the margin while its title lands on
   exactly the same left edge as the paragraphs under it — and because §1 and §1.4 share one
   track width, every title on the page lines up in one column.

   This is one mechanism at every width, which is why there is no separate mobile treatment
   for it and no nested rail: a number never ends up stranded on its own line above a
   heading, which would be an eyebrow rather than a coordinate. */

.handbook .sec + .sec {
  margin-top: 3.25rem;
  padding-top: 2.5rem;
  border-top: 2px solid var(--ink);
}

/* A link into the middle of a long page shouldn't land its target under the running head. */
.handbook .sec { scroll-margin-top: 3.5rem; }
.handbook .step,
.handbook .fig { scroll-margin-top: 4rem; }

.handbook h2,
.handbook .step > h3 {
  display: grid;
  grid-template-columns: var(--num) minmax(0, 1fr);
  align-items: baseline;
  margin-left: calc(-1 * var(--num));
  font-family: var(--serif);
  font-weight: 700;
  text-wrap: pretty;
}

.handbook h2 {
  font-size: clamp(1.5rem, 5vw, 1.9rem);
  line-height: 1.15;
  letter-spacing: -0.012em;
  margin-top: 0;
  margin-bottom: 1.1rem;
}

.handbook .step > h3 {
  font-size: 1.15rem;
  line-height: 1.3;
  margin-top: 0;
  margin-bottom: 0.7rem;
}

/* An ordinary subheading, with no number of its own — §3 uses three of them. Indented with
   the body text rather than hanging, because it is not a numbered clause and pretending
   otherwise would put an unnumbered thing in the number column. */
.handbook .sec h3:not(.step > h3) {
  font-family: var(--serif);
  font-weight: 700;
  font-size: 1.1rem;
  line-height: 1.3;
  margin: 1.9rem 0 0.6rem;
}

/* The numbers themselves. Set in the sans at a size that reads as a reference rather than as
   display type — the title is the loud half of the pair, and a clause number that shouts is
   the "01 / 02 / 03" decoration this form is often mistaken for. Here the sequence is load
   bearing: it is how §4 is linked to, how the contents addresses it, and how the running
   head names it. */
.handbook .sec-no,
.handbook .step-no {
  font-family: var(--sans);
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0;
}

.handbook .sec-no { font-size: 0.95rem; }
.handbook .step-no { font-size: 0.85rem; }

/* And the number is the link to its own clause. Every fact on this sheet carries a coordinate,
   so the coordinate is the handle you copy — which costs no ¶, no ⧉ and no second control
   appearing beside the title on hover, and puts the link exactly on the thing a reader would
   quote when they send it to somebody.

   justify-self, because a grid item fills its track by default and the rail is 7rem wide on a
   large screen: without it the link would be seven centimetres of clickable margin beside every
   heading, and a click on empty paper that navigates is a surprise, not an affordance.

   Muted at rest and amber under the pointer. A clause number's first job is to be read as a
   coordinate and only its second to be clicked, so the accent arrives when the cursor does —
   the focus ring, which comes from site.css, is what says the same thing to a keyboard. */
.handbook a.sec-no,
.handbook a.step-no {
  justify-self: start;
  text-decoration: none;
  /* A single digit is an 11 × 21 target, and this repo's floor is WCAG 2.5.8's 24 × 24. The
     padding buys the box and the matching negative margin gives back every pixel of layout it
     took, so the number does not move: what grows is only what a cursor has to hit. The
     baseline is unaffected — the heading aligns its two tracks on the first baseline, which
     padding above the text does not shift. */
  padding: 0.25rem 0.45rem;
  margin: -0.25rem -0.45rem;
}

.handbook a.sec-no:hover,
.handbook a.step-no:hover { color: var(--accent); }

.handbook .sec-lede {
  font-size: 1.05rem;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* ---------- The procedure ----------
   Four steps that used to be five slides in a fixed-height carousel. Every argument for the
   slider was about attention; every cost of it was paid by the reader who wanted to compare
   two steps, print the page, or find a phrase on a slide that was not showing. Stacked, the
   steps are all present, all findable, all printable, and the ~100 lines of measuring script
   the fixed-height box needed are gone with it. */

.handbook .procedure {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: none;
}

.handbook .step + .step {
  margin-top: 2.25rem;
  padding-top: 2rem;
  border-top: 1px solid var(--rule);
}

/* One fact per step, and only one. A step in a procedure has a single thing that goes wrong
   if you skip it, and that thing is set here — in the serif, ruled off, ahead of the
   ordinary bullets. Not amber and not boxed: the accent is reserved for what you act on and
   what is wrong, and a notice box would flatten the difference between "worth knowing" and
   §4's "this fails silently". */
.handbook .keyfact {
  font-family: var(--serif);
  font-size: 1.08rem;
  line-height: 1.45;
  color: var(--ink);
  padding: 0.55rem 0 0.75rem;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  margin-bottom: 1rem;
  text-wrap: pretty;
}

/* ---------- Step glossaries ----------
   The three lists in §1 are one construction twelve times over: a term, then what it is or
   where it lives. They were <ul>s carrying an em-dash between the two halves, which put a
   separator in the content layer and left a screen reader to read out one run-on line with a
   dash in it. As a definition list the halves are marked as what they are, and the separator
   moves here, where punctuation between a term and its gloss belongs. Not one word of the
   copy changed, and neither did what a reader sees.

   Run-in, not two columns. The terms run to "Set the dials, tap Log frame N", so a column
   wide enough for the longest would leave the gloss about a third of the measure and strand
   every short one in white space. Run-in also keeps the page's single text edge — a hanging
   indent would give the step a second one, which is the rule §1 is built on.

   The <div> wrapper is what carries the bullet: <dt> and <dd> are inline here, so neither can
   be the list item. It is the same wrapper .sheet-id and .route use, for the same reason —
   a dl's pairs have no element of their own until you give them one. */

.handbook .gloss {
  margin: 0 0 1.1em;
  padding-left: 1.15em;
}

.handbook .gloss > div {
  display: list-item;
  list-style-type: disc;
}

.handbook .gloss > div::marker { color: var(--muted); }
.handbook .gloss > div + div { margin-top: 0.5em; }

/* 700, because that is what the <strong> this replaced resolved to. The term is doing the
   same job it was; only its markup got honest. */
.handbook .gloss dt {
  display: inline;
  font-weight: 700;
}

.handbook .gloss dd {
  display: inline;
  margin: 0;
}

/* The separator. A non-breaking space ahead of the dash so it can never begin a line; the gap
   after it is a margin rather than a space, because a space typed into `content` collapses
   against the text that follows and the dash ends up jammed against the gloss. The margin
   also keeps the wrap: U+2014 already offers a line-break opportunity after itself, and a
   second nbsp would have taken it away.

   0.18em is Instrument Sans' word space, measured (3px at 17px) rather than picked — the gap
   has to match the nbsp on the other side of the dash or the separator sits off-centre. */
.handbook .gloss dd::before {
  content: '\00a0\2014';
  margin-right: 0.18em;
}

/* ---------- The three routes ----------
   Was a tab picker inside the last slide, so two of the three were always hidden. A reader at
   this point in the page is choosing between them, which is exactly the moment not to hide
   two. Three ruled blocks, all visible, all searchable, side by side once there is room. */

.handbook .routes {
  display: grid;
  gap: 0.9rem;
  margin: 0 0 1.1em;
}

/* Side by side, the three share their borders and close up into a single ruled field with one
   head band across it — the same object the §6 matrix is, which is the point: these are three
   columns of one comparison, not three cards. Stacked, they keep the gap and their own left
   edge, since a shared vertical border means nothing in one column. */
@media (min-width: 52rem) {
  .handbook .routes { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0; }
  .handbook .route + .route { border-left: none; }
}

/* Ruled fields with a filled head, exactly as the tables are — not three rounded boxes with a
   rule on top, which is the card grid this page's own world block refuses. The difference is
   not cosmetic: a card is a container that happens to hold three things, and these three are
   columns of one comparison, so they take the same head band the §6 matrix takes and read as
   one object rather than as three. No radius, for the same reason a datasheet's tables have
   none. */
.handbook .route {
  border: 1px solid var(--rule);
  border-top: none;
  display: flex;
  flex-direction: column;
}

.handbook .route dt {
  background: var(--field);
  border-bottom: 2px solid var(--rule-2);
  padding: 0.45rem 0.9rem;
  font-family: var(--serif);
  font-style: italic;
  /* The lede step, not a size of its own. At 1rem this was the only use of that value on the
     page — a step invented for one element, four tenths of a pixel from one that already
     existed. */
  font-size: 1.05rem;
  /* --ink-2 on --field, for the reason the table heads are: --muted measures 3.82:1 there. */
  color: var(--ink-2);
}

.handbook .route dd {
  margin: 0;
  padding: 0.75rem 0.9rem 0.85rem;
  flex: 1;
}

.handbook .route ul {
  margin: 0;
  padding-left: 1.05em;
  font-size: 0.94rem;
  color: var(--ink-2);
}

/* ---------- Notices ----------
   A bounded, ruled block with its label set into the top of it — the shape a datasheet uses
   for a caution. Deliberately not a fat coloured left border, which is the web's default
   callout and says "aside" rather than "read this".

   Two weights. The ordinary one is neutral: it separates without claiming urgency. The major
   one is the only place on this page a rule is drawn in the accent, and it is used exactly
   once, on the rule in §4 that fails silently if you ignore it. A second one would spend it. */

.handbook .notice {
  margin: 0 0 1.3em;
  border: 1px solid var(--rule-2);
  border-radius: 0.3rem;
  padding: 0.85rem 1rem 0.95rem;
  background: var(--band);
}

.handbook .notice p { margin: 0; }
.handbook .notice p + p { margin-top: 0.5rem; }

.handbook .notice-label {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 0.4rem;
}

/* --fault, not --accent, since 24.08.2026: this is one of the two places on the whole site the
   amber survived the move to the monochrome finishes, and it survived precisely because it is
   naming something that goes wrong rather than something you click. The token's new name is what
   makes that reading enforceable — a rule drawn in --accent here would now be drawn in ink. */
.handbook .notice-major {
  border-color: var(--fault);
  border-top-width: 3px;
  background: none;
  font-size: 1.08rem;
}

.handbook .notice-major .notice-label { color: var(--fault); }

/* ---------- Two kinds of literal ----------
   A control's name on screen, and a tag name in a file. They are different kinds of thing
   and are set differently: the label in the italic serif the landing page already uses for
   "metalign companion", the tag in mono, because that is what it is — the one use of a
   monospace face on this page that is about code and data rather than about looking
   technical.

   The size is 1em and not a shade larger. XCharter's italic already stands out of Instrument
   Sans plainly enough, and a handbook names a control every second sentence — set any
   louder, the labels stop marking anything because half the page is marked. Which is also
   why the prose reserves this for things you click or tap: a whole sentence the app puts on
   screen gets quotation marks instead, and a section referred to in passing gets neither.

   Not nowrapped, deliberately: some of these labels are five words long, and on a phone one
   of them would be wider than the column. site.css sets overflow-x: hidden on the body, so
   an over-wide inline would be clipped rather than scrolled to. */

.handbook .ui {
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink);
}

/* Emphasis goes to the same face, which is the landing page's own habit (.status em there)
   and here is also what keeps a fourth font off the page. Left alone, an <em> in a sans
   paragraph resolves to Instrument Sans Italic — 95 KB fetched to italicise about ten words,
   on a page that is otherwise three faces and one small script. XCharter Italic is already
   preloaded for .ui, so this costs nothing. That .ui and <em> then look alike is not a loss:
   both mean "set apart", and which kind it is reads off the sentence. */
.handbook em {
  font-family: var(--serif);
  font-style: italic;
}

/* No tinted box behind a tag, and no padding. The mono face is already unmistakable against
   Instrument Sans, and the padding cost something the tint never repaid: a padded inline sits
   a visible gap away from the punctuation after it, so "…never to ExposureTime." reads as
   "…never to ExposureTime ." — on a page that ends a lot of sentences on a tag name. */
.handbook code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 0.88em;
  color: var(--ink);
  /* A long tag list in a narrow column has to be allowed to break; the page never scrolls
     sideways. */
  overflow-wrap: anywhere;
}

/* ---------- Tables ----------
   The form's home ground, and the reason it was chosen: §5 is a fourteen-row map from a
   logged field to a set of tags, and §6 is a three-way specification matrix. Both are
   look-up furniture, and both were previously set as quiet editorial tables that gave a
   header row a hairline and hoped.

   A filled head instead — the ground doing structural work at page scale. Wide content
   scrolls inside its own box; the body never does, because site.css sets overflow-x: hidden
   on it and a table that made the whole page slide sideways on a phone would break the one
   promise this site's CSS makes about small screens. */

.handbook .scroller {
  overflow-x: auto;
  margin: 0 0 1.4em;
  /* Room for the scrollbar some platforms draw inside the box. */
  padding-bottom: 0.25rem;
  /* The scrollbar is one of the browser surfaces that ships with no design system attached.
     Themed from the palette here rather than left in the platform's grey. */
  scrollbar-width: thin;
  scrollbar-color: var(--rule-2) transparent;
}

.handbook .dtable {
  border-collapse: collapse;
  width: 100%;
  /* Below this the columns stop being columns — see the stacking query at the end of this
     file, which takes over before a table is ever this cramped. */
  min-width: 30rem;
  font-size: 0.94rem;
  line-height: 1.5;
  text-align: left;
}

.handbook .dtable thead th {
  background: var(--field);
  padding: 0.5rem 0.9rem;
  border-bottom: 2px solid var(--rule-2);
  font-family: var(--sans);
  /* 0.78rem, not the 0.72 this started at. This label and the data-label pseudo-headers in
     the phone query below are --ink-2, uppercased and letterspaced, which is two things
     working against legibility at once; they were failing on size alone at ~11.5px.

     The colour is --ink-2 rather than --muted because these sit on --field now, where muted
     measures 3.82:1 against the 4.5 it owes — see the token's own note above. */
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-2);
  vertical-align: bottom;
}

.handbook .dtable th:first-child { border-top-left-radius: 0.3rem; }
.handbook .dtable th:last-child { border-top-right-radius: 0.3rem; }

.handbook .dtable tbody th {
  font-weight: 600;
  color: var(--ink);
}

.handbook .dtable td,
.handbook .dtable tbody th {
  padding: 0.6rem 0.9rem;
  vertical-align: top;
  border-bottom: 1px solid var(--rule);
}

.handbook .dtable tbody tr:nth-child(even) > * { background: var(--band); }

.handbook .dtable td { color: var(--ink-2); }

/* The tag map's left column is the term and its right column the value, so the term carries
   the weight — it is the thing being looked up. */
.handbook .dtable-tags td:first-child { color: var(--ink); font-weight: 600; }

/* The matrix's first column is an empty corner cell by construction: its header row names the
   three apps and its rows name the properties. */
.handbook .matrix thead th:first-child { background: none; border-bottom-color: var(--rule); }

/* The one table head on this page that is not uppercased, because its three cells are the
   product's own names and the product is lowercase always — including when a text-transform
   would rather it weren't. Losing the caps costs the letterspacing too, which only ever
   existed to open up capitals. Set from the top rather than the bottom: these are the only
   heads long enough to wrap, and bottom-aligned they had no shared first baseline — "metalign
   web, in a browser" started a line below its neighbours. */
.handbook .matrix thead th {
  text-transform: none;
  letter-spacing: 0.005em;
  font-size: 0.82rem;
  vertical-align: top;
}

/* ---------- Figures ----------
   Numbered, because a numbered figure can be referred to from the prose — §4 sends the
   reader to Fig. 7 and Fig. 6 says which screen it is drawn from — and because a coordinate
   is what this whole form is for.

   Phone screens from metalign companion, illustrating the hand-offs this page describes in
   words. Two things shape every rule here.

   **Each shot is a light/dark pair, and the swap is markup, not CSS.** A <picture> with a
   <source media="(prefers-color-scheme: dark)"> in front of the light image picks one half at
   load and loads that one, where the CSS alternative — two images with one hidden — puts both
   on the wire to show one. Sixteen phone captures is the wrong page to pay that on. There is
   therefore nothing in this sheet that knows about the theme.

   **The cost of choosing it, measured rather than assumed:** <picture> resolves at load and
   does not re-run when the theme changes under an open page. Switched from light to dark in
   a live tab, currentSrc stayed on the light file until a reload. The CSS swap would follow
   instantly. That is the trade taken here — a reader who flips their system theme mid-page
   sees one stale screenshot until they reload, which is rarer than a reader on a metered
   connection, and costs them nothing but a moment's oddity. Revisit it if the page ever grows
   a theme toggle of its own, because then switching stops being rare and this becomes wrong.

   **A screenshot has to be separated from the page it sits on**, and the reason weakened on
   24.08.2026 without going away. Under warm paper the light captures were very nearly --paper
   (#f7f4ef) and the dark ones very nearly the dark --paper (#14120f), so an unbordered shot
   bled straight into the prose in both themes. The finishes are further from a phone screen at
   both ends — a near-white capture against the chrome plate (#cfcdc7), a near-black one against
   matte black (#191817) — so the hairline is doing less work than it was.

   **It is scheduled to go entirely**, because these figures are getting Apple's product bezels
   like the landing plates (owner's call, 24.08.2026): a bezel is the edge, and a border around
   it draws a second rectangle around a frame that already has rounded corners, with --band
   filling the transparent corners outside them. The border and the ground stay until the framed
   files land — SCREENSHOTS.md § 5 and § 6, and `tools/frame-plates.py` already lists all eight
   screens. Removing them before the frames exist would leave an unbordered bare capture, which
   is the one arrangement that reads worst.

   Alt text is the picture's job to carry, not this file's — a reader who cannot see the
   screenshot should get what the screen *says*, since that is the whole reason it is here. */

/* Flush left, not centred. A centred plate with a centred caption under it is the one place
   this page was still composing to an axis of its own instead of to the rail — and the caption
   was the loudest instance, five ragged-centre lines sitting under the signature figure. Both
   now start on the same left edge as every heading title and every paragraph. */
.handbook .fig {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.75rem;
  /* Browsers give <figure> a 40px inline margin, which would inset it from a measure that is
     already narrow. */
  margin: 1.4em 0;
}

.handbook .fig-frames {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: flex-start;
  gap: 1rem;
}

/* <picture> is an inline box that would otherwise add a descender's worth of space under the
   image and throw the hairline off by a pixel. */
.handbook .fig picture { display: block; }

/* Every screenshot is wrapped in an <a class="shot-zoom"> (see the lightbox script at the end
   of the body) — same reasoning as .fig picture above, block rather than the anchor's default
   inline, and no underline or colour: it wraps a picture, not a phrase. position: relative
   anchors the badge below, which needs .shot-zoom's own box to draw against. */
.handbook .shot-zoom {
  display: block;
  position: relative;
  color: inherit;
  text-decoration: none;
  cursor: zoom-in;
}

/* The badge is the only hint a screenshot opens larger — necessary because the affordance
   hover gives on desktop doesn't exist on the phones this feature matters most for. Sized for
   a thumb rather than for looking dainty. */
/* Positioned in per cent, not rem, since the plates were framed (24.08.2026) — because what it
   has to sit inside is no longer the image but the SCREEN within it. Apple's bezel insets the
   aperture by 72/1350 of the width and 69/2760 of the height, which at the rendered size is
   12.5px and 12px; at the old 0.35rem (5.6px) the badge overhung the rim by about 7px on both
   axes and straddled the phone's rounded corner. 7% and 3.5% clear the inset with roughly 4-5px
   of margin inside the screen, and being proportional they hold at every size the three
   height rules give a plate. */
.handbook .shot-zoom::after {
  content: "";
  position: absolute;
  right: 7%;
  bottom: 3.5%;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background-color: rgba(25, 23, 20, 0.72);
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg>');
  background-repeat: no-repeat;
  background-position: center;
  background-size: 0.95rem;
}

.handbook .fig img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: var(--shot);

  /* No border, no radius, no ground, since 24.08.2026 — the same three that came off the
     landing page's plates when they were framed, for the same reason. The bezel is the edge.
     A border draws a second rounded rectangle around one that already has corners, and a
     ground fills the transparent corners outside them. */

  /* 45:92 is the framed plate's own ratio — 540x1104 — so the intrinsic size and this rule
     agree, and the loaded geometry is stated rather than inferred.

     **It does NOT reserve the box before the image loads, and do not read it as doing so.**
     Measured 24.08.2026: an unloaded lazy image under this rule computes to 0x0, aspect-ratio
     and width/height attributes notwithstanding, because `width: auto` + `height: auto` on a
     replaced element with no intrinsic size resolves to zero and the ratio has nothing to
     scale. The landing page's plates have the identical rule and the identical behaviour —
     an earlier note here claimed they reserved correctly and the handbook's did not, which
     was measured wrong: a loaded plate on one page against an unloaded one on the other.

     So the figures still shift as they arrive, on both pages. The fix is a definite `height`
     rather than `max-height` — a definite side is what gives the ratio something to work
     from — which means converting the three height rules below and adding
     `object-fit: contain` so a bound `max-width` letterboxes instead of stretching a
     screenshot. Not done here: this file was being changed to frame the plates, and
     distorting ten screenshots to fix a shift nobody has reported is the wrong trade to
     make in passing. */
  aspect-ratio: 45 / 92;

  /* All three only ever apply to a plate that FAILED to load: a rendered image is a replaced
     element and ignores them. They are what turns the reserved box into the description
     written for it — and they matter more here than on the landing page, because these alt
     texts run to 700 characters. At inherited body size the box clipped about 180px of the
     longest one, which is a description a reader falls back to and then cannot finish. */
  line-height: 1.45;
  font-size: 0.82rem;
  color: var(--muted);
}

/* A shot standing alone can afford to be taller: there is nothing beside it to fit. Two or
   more go back to --shot so a row of them stays inside the measure.

   Written as :has() rather than as a modifier class so the markup carries no count — the
   figure is right whether it holds one picture or three. Where :has() is missing this rule
   is simply skipped and every shot takes the smaller size, which still reads. A graceful
   nothing, not a fallback to write. */
.handbook .fig-frames:not(:has(.shot-zoom + .shot-zoom)) img {
  max-height: 30rem;
}

/* The caption hangs its figure number in the same rail the clause numbers use, by the same
   mechanism — a two-track grid pulled left by --num. So Fig. 6 sits directly under §4 in one
   column of coordinates, which is the apparatus a datasheet actually keeps in its margin, and
   the caption text starts on the page's single left edge rather than on an axis of its own. */
.handbook figcaption {
  display: grid;
  grid-template-columns: var(--num) minmax(0, 1fr);
  align-items: baseline;
  margin-left: calc(-1 * var(--num));
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--muted);
  text-align: left;
  text-wrap: pretty;
  /* Captions are read against the picture above them, not across the page. */
  max-width: calc(34rem + var(--num));
}

.handbook .fig-no {
  font-family: var(--sans);
  /* 0.78rem, the same floor every other apparatus label on this page holds to. It clears the
     rail at every width: the glyph run measures 38px against a 40px rail on a phone, which is
     the tightest case, and Instrument Sans is the widest of the faces that can resolve here —
     a fallback is narrower, never wider, so the measured case is the worst one. */
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}

/* A control's name in a caption is the same kind of thing it is in the prose, and .ui already
   sets it — but .ui sets color: var(--ink), which against a muted caption would make the
   label the loudest thing in it. Inherit instead. */
.handbook figcaption .ui { color: inherit; }

/* ---------- Fig. 6, the pairing ladder ----------
   The page's hardest idea, drawn instead of only described. Positional pairing is the one
   rule here that fails *silently* — a wrong pairing writes a plausible exposure onto the
   wrong photograph — and prose alone had to ask the reader to hold six rows in their head to
   see it. Showing the two states, and letting the reader move between them, is the whole
   argument in one figure.

   **The control is a checkbox, not a script.** #gap:checked translates the four scan cells
   down by exactly two rows; the caption states both readings either way. So it works with
   JavaScript off, gets keyboard operation and a focus ring from the platform, and has
   nothing to degrade to because it never enhanced anything. The one authored motion on this
   page is here, and it is the motion the mechanism actually has: the left column moves, the
   right column never does.

   The figure is aria-hidden nowhere — every cell is real text, so a screen reader gets the
   ladder as a list of filenames and frames, and the surrounding prose and caption carry the
   rule itself. */

.handbook .fig-t { min-width: 0; }

/* A wide figure gets a taller cap as well as a wider track, because for Fig. 5 the height is
   what was actually in the way: a phone capture is about 0.46 as wide as it is tall, so the
   ordinary --shot put each of that figure's two screens at roughly 190px and the JSON preview
   the paragraph above it explicitly sends the reader to could not be read at rest.

   Bounded by the viewport as well as by a maximum, because the fix for an illegible figure is
   not a figure that fills the screen — at full readable width these two would stand about
   950px tall and displace the sentence they belong to, which is the trade the ordinary cap was
   set to avoid. The lightbox is still where full resolution lives. */
.handbook .fig-wide .fig-frames img { max-height: min(40rem, 62vh); }

.handbook .ladder-wrap {
  border: 1px solid var(--rule-2);
  border-radius: 0.3rem;
  padding: 0.9rem;
  background: var(--paper);
}

.handbook .ladder {
  display: grid;
  grid-template-columns: minmax(7.5rem, 0.85fr) minmax(0, 1.15fr);
  grid-auto-rows: var(--rung);
  align-items: center;
  font-size: 0.85rem;
}

.handbook .ladder-head {
  font-family: var(--sans);
  /* 0.78rem. Muted, uppercased and letterspaced at once, which is the combination this file
     sets a floor for in three other places — and this label was below it, exactly as the
     edition mark was. Small type in an apparatus label is the defect this page keeps
     re-introducing; the floor is the answer every time. */
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  align-self: end;
  padding-bottom: 0.45rem;
  grid-row: 1;
}

.handbook .ladder-head-l { grid-column: 1; }
.handbook .ladder-head-r { grid-column: 2; padding-left: 0.9rem; }

/* The rungs are the ladder. Drawn as full-width rows behind the cells so a scan and its frame
   are visibly *level* — which is the entire claim the figure is making. */
.handbook .rung {
  grid-column: 1 / -1;
  grid-row: calc(var(--r) + 1);
  border-top: 1px solid var(--rule);
  align-self: stretch;
}

.handbook .rung:last-of-type { border-bottom: 1px solid var(--rule); }

.handbook .scan,
.handbook .gap-cell {
  grid-column: 1;
  grid-row: calc(var(--i, 0) + 1);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  font-size: 0.82rem;
  color: var(--ink);
  padding-right: 0.9rem;
  transition: transform 420ms cubic-bezier(0.16, 1, 0.3, 1);
}

.handbook .scan[data-i="1"] { --i: 1; }
.handbook .scan[data-i="2"] { --i: 2; }
.handbook .scan[data-i="3"] { --i: 3; }
.handbook .scan[data-i="4"] { --i: 4; }

.handbook .gap-cell[data-g="2"] { grid-row: 3; }
.handbook .gap-cell[data-g="3"] { grid-row: 4; }

.handbook .gap-cell {
  color: var(--muted);
  font-style: italic;
  font-family: var(--serif);
  opacity: 0;
  transition: opacity 240ms ease-out;
}

/* The frame cell is two lines — the frame and its verdict — and it used to be three, because a
   flex column makes every child its own line and the frame number is a child. At three lines it
   measured 60px in a 56px row and every verdict sat on the rule below it. The number is wrapped
   with its title now, so the count matches the row. */
.handbook .frame {
  grid-column: 2;
  grid-row: calc(var(--i, 0) + 1);
  padding-left: 0.9rem;
  border-left: 1px solid var(--rule);
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.15rem;
  color: var(--ink);
}

.handbook .f-t { line-height: 1.35; }

.handbook .frame[data-i="1"] { --i: 1; }
.handbook .frame[data-i="2"] { --i: 2; }
.handbook .frame[data-i="3"] { --i: 3; }
.handbook .frame[data-i="4"] { --i: 4; }
.handbook .frame[data-i="5"] { --i: 5; }
.handbook .frame[data-i="6"] { --i: 6; }

.handbook .f-no {
  font-family: var(--sans);
  font-weight: 600;
  color: var(--muted);
  margin-right: 0.15em;
}

/* Every state is named as well as marked. The amber says "look at this" faster than reading
   does, but it is never the only thing saying it — a reader who cannot separate the two tones
   gets the same information from the words.

   .v-check, not .v-bad, since 24.08.2026, and the rename is the figure's correction in one
   class name. The row it marks is not WRONG: a blank frame usually does have a scan, so the
   pairing beside it is most often right. What it is is unverifiable — the log holds nothing on
   that row to check it against, which is what the phone's own warning says and why it asks you
   to look rather than telling you to repair. Amber for the row the app raises a warning about
   is the honest reading of "what is wrong" on a figure where nothing is provably wrong. */
.handbook .v {
  font-size: 0.76rem;
  line-height: 1.3;
}

.handbook .v-ok { color: var(--muted); }
.handbook .v-check { color: var(--fault); font-weight: 600; }
.handbook .v-none { color: var(--muted); font-style: italic; }

.handbook .v-after { display: none; }

/* The gap, inserted. Only the left column moves, and it moves by exactly the two rows the two
   blank frames occupy — which is the rule the figure exists to show. */
.handbook .ladder-toggle:checked ~ .ladder .scan[data-i="2"],
.handbook .ladder-toggle:checked ~ .ladder .scan[data-i="3"],
.handbook .ladder-toggle:checked ~ .ladder .scan[data-i="4"] {
  transform: translateY(calc(2 * var(--rung)));
}

.handbook .ladder-toggle:checked ~ .ladder .gap-cell { opacity: 1; }
.handbook .ladder-toggle:checked ~ .ladder .v-before { display: none; }
.handbook .ladder-toggle:checked ~ .ladder .v-after { display: block; }

.handbook .ladder-btn {
  display: inline-block;
  margin-top: 1rem;
  padding: 0.6rem 1rem;
  min-height: 2.75rem;
  border: 1px solid var(--rule-2);
  border-radius: 999px;
  background: var(--field);
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}

.handbook .ladder-btn:hover { border-color: var(--accent); color: var(--accent); }

/* The label is the control, so the focus ring has to be drawn on it — the input it belongs to
   is visually hidden, and a ring around a clipped 1px box is a ring nobody sees. :focus-visible
   still fires on that input, which is what this hangs off. */
.handbook .ladder-toggle:focus-visible ~ .ladder-btn {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.handbook .ladder-btn-off { display: none; }
.handbook .ladder-toggle:checked ~ .ladder-btn .ladder-btn-on { display: none; }
.handbook .ladder-toggle:checked ~ .ladder-btn .ladder-btn-off { display: inline; }

/* ---------- Footer ----------
   site.css pins it to the bottom edge of a one-screen grid and, above 62rem, gives it the
   landing page's two tracks. Here it is the end of a document: one column, the width of the
   sheet, ruled off from it. */

.handbook footer {
  align-self: auto;
  grid-template-columns: 1fr;
  margin-top: 3.5rem;
  padding-top: 1.25rem;
  border-top: 2px solid var(--ink);
}

.handbook footer p { max-width: var(--doc); margin: 0; }

/* ---------- A wide screen ----------
   At 1440 the sheet stopped at about 55% of the window and the rest was blank for the whole
   page — which read as a column floating in a browser rather than as a published sheet.

   The answer is not to stretch the prose: 68 characters is the measure a long technical read
   wants, and widening it to fill a monitor would trade the one thing the form is for. It is to
   give the page the apparatus a datasheet keeps in its margin. The rail already exists and
   already holds the clause numbers, so here it simply becomes wide enough to be a margin rather
   than an indent, and the figure numbers move into it (see figcaption) so §4 and Fig. 6 line up
   in one column of coordinates down the left of the sheet. The wide track grows with it, which
   is what actually fills the width — the §5 tag map and the §6 matrix are the widest things on
   the page and they are the things that benefit from the room.

   Every block scales together because they are all measured in --num and --wide; there is no
   second layout here, only bigger values for the one there is. */

@media (min-width: 78rem) {
  .handbook {
    --num: 7rem;
    --wide: 56rem;
  }

  /* The rail is a margin now, so its numbers can carry the weight a margin number has in print
     without competing with the titles they sit beside. */
  .handbook .sec-no { font-size: 1.15rem; }

  /* And the margin starts at the top of the sheet rather than at §1. Without this the first
     viewport paid the wider indent and got nothing back for it: the masthead, the identity
     block and the contents all sat inside a 7rem gutter with nothing in it, which is a worse
     composition than the narrow rail it replaced. The edition mark is what the form puts
     there. */
  .handbook .sheet-mark { display: block; }
}

/* ---------- The phone ----------
   One breakpoint, doing four separate jobs, kept as four queries so each carries its own
   reason rather than one block carrying four. */

/* Tables. A table that only scrolls sideways is the wrong answer here. Measured at 375px: the
   second column of the tag map was cut through the middle of a tag name, with no scrollbar
   drawn and nothing to suggest a swipe — it reads as a broken page rather than a wide one.
   The body cannot scroll sideways to rescue it either, by design (site.css).

   So below 34rem each row becomes a small block: the first cell is its heading, and every
   cell after it carries its column's name in front of it, out of `data-label`. That is why
   those attributes exist in the markup — they are not decoration, they are the header row,
   and a cell in a multi-column table that loses one becomes an unlabelled value. The tag
   map's two columns need none: term then value, in that order, is already the reading. */
@media (max-width: 34rem) {
  .handbook .dtable { min-width: 0; }
  .handbook .dtable thead { display: none; }

  .handbook .dtable,
  .handbook .dtable tbody,
  .handbook .dtable tr,
  .handbook .dtable th,
  .handbook .dtable td {
    display: block;
    width: auto;
  }

  .handbook .dtable tbody tr {
    padding: 0.85rem 0;
    border-bottom: 1px solid var(--rule);
  }

  /* The rules now belong to the row, and the striping to nothing: a block with a tinted
     background and a rule under it is two ways of saying one boundary. */
  .handbook .dtable tbody tr:nth-child(even) > * { background: none; }

  .handbook .dtable tbody th,
  .handbook .dtable tbody td {
    border: 0;
    padding: 0;
  }

  /* The row's heading: whichever of the two shapes the table uses — a <th scope="row"> in
     the comparison, a plain first cell in the others. */
  .handbook .dtable tbody th,
  .handbook .dtable tbody td:first-child {
    font-weight: 600;
    color: var(--ink);
    margin-bottom: 0.35rem;
  }

  .handbook .dtable tbody td + td { margin-top: 0.55rem; }

  .handbook .dtable td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 0.1rem;
  }
}

/* Screenshots. Nothing about them needs restructuring down here — the flex row wraps a pair
   by itself and the width was never the problem. Only the height is. Measured at 375px, the
   lone-shot size would put a 30rem picture into an 812px viewport, so the reader gets most of
   a screen of somebody else's screen and has to scroll it away to reach the sentence it
   belongs to. A picture that displaces its own explanation is worth less than the space it
   took.

   The pair stays two-across rather than stacking. Stacking buys nothing — height is the
   constraint, so a stacked shot is the same width and merely twice as far to scroll — and
   side by side is the one arrangement that shows the two screens are a sequence. */
@media (max-width: 34rem) {
  .handbook { --shot: 20rem; }
  .handbook .fig-frames:not(:has(.shot-zoom + .shot-zoom)) img { max-height: 26rem; }
}

/* The rail. Narrower, not gone: the number stays beside its title at every width, because the
   alternative — dropping it onto its own line above the heading — turns a coordinate into a
   decorative kicker, and this page's numbers are load bearing. 2.5rem still fits "1.4" at the
   step size and leaves about 36 characters of measure on a 375px screen, which is an ordinary
   mobile measure. */
@media (max-width: 34rem) {
  .handbook { --num: 2.5rem; }

  .handbook h1 { font-size: clamp(2rem, 11vw, 2.5rem); }
  .handbook .standfirst { font-size: 1.05rem; }

  /* The identity block stacks: a 9rem label column against what is left of a 375px screen
     wraps every value onto three lines. Stacked, though, the filled label bands turn four rows
     into eight alternating blocks that ate the whole first screen before the reader reached the
     contents — so down here the label loses its ground and keeps only its type. The block is
     still a ruled field; it is just no longer eight of them. */
  .handbook .sheet-id > div { grid-template-columns: 1fr; padding: 0.6rem 0.9rem; }
  .handbook .sheet-id > div + div { border-top: 1px solid var(--rule); }

  .handbook .sheet-id dt {
    background: none;
    border-right: none;
    padding: 0 0 0.15rem;
    /* --muted is fine here and not on --field: it measures 4.69:1 on the paper this now sits
       on, against 3.82:1 on the tinted ground it lost. Held at 0.78rem, which is this repo's
       floor for a label that is muted, uppercased and letterspaced all at once. */
    color: var(--muted);
    font-size: 0.78rem;
  }

  .handbook .sheet-id dd { padding: 0; }
}

/* The ladder. The filename column is the one that has to give, and it can: the point of the
   left column is that it *moves*, not that you can read every character of it. Rows grow
   because both cells wrap a line more down here. */
@media (max-width: 34rem) {
  .handbook { --rung: 4.25rem; }

  .handbook .ladder {
    grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
    font-size: 0.82rem;
  }

  /* 0.76rem, the smallest step this page has — the verdict lines in this same figure. 0.72 was
     a sixteenth step invented for one rule, two thirds of a pixel below one already in use. */
  .handbook .scan,
  .handbook .gap-cell { font-size: 0.76rem; padding-right: 0.5rem; }

  .handbook .frame { padding-left: 0.5rem; }
  .handbook .ladder-head-r { padding-left: 0.5rem; }
  .handbook .ladder-wrap { padding: 0.7rem; }
}

/* ---------- Motion ----------
   One authored moment on the page — the ladder — and it is the mechanism, not an entrance.
   Honour the system setting for it, and for the running head's fade. */

@media (prefers-reduced-motion: reduce) {
  .handbook .scan,
  .handbook .gap-cell,
  .handbook .runhead,
  .handbook .runhead:not([hidden]) ~ .doc-index,
  .handbook .runhead.is-live ~ .doc-index {
    transition: none;
  }
}

/* ---------- The lightbox ----------
   One overlay, shared by every screenshot on the page — see the markup and script at the end
   of the body. Not scoped under .handbook: the box sits outside <main>, a direct child of
   <body>, specifically so a section's stacking context can never clip or bury it.

   display: flex below needs an explicit [hidden] override, because an author rule that sets
   display always beats the UA stylesheet's [hidden] { display: none } regardless of
   specificity — without the first rule the overlay would render, full-screen, over
   everything, the moment the page loaded rather than the moment something was clicked. */

.lightbox { display: none; }

/* The one place site.css's focus ring cannot be used, and the reason survived the palette change
   with its sign flipped. That ring is var(--accent), chosen against var(--paper) and changing
   with the theme — but this overlay does not change: it is rgba(10, 9, 8, 0.88) in both. Under
   the warm palette --accent was the light amber (#9d4715), which measured 2.43:1 on the backdrop
   and was the first thing a keyboard user was shown and the one thing they could not see. Under
   the finishes --accent is the light INK (#1d1b17), which is worse — near-black on a backdrop
   that resolves to about #22211f.

   So the override stays and only its value changes. It was the dark amber, which under the
   24.08.2026 rule would now be a lie: amber means a fault, and a focus ring is an affordance.
   The black-paint finish's own ink is the honest monochrome answer and measures about 12:1 on
   that backdrop in either theme. Hardcoded for the same reason .lightbox-img's border and
   .lightbox-caption are — the surface does not track the palette, so neither can what sits on
   it. Scoped to the overlay, so the page's own ring is untouched. */
.lightbox :focus-visible {
  outline-color: #e2e0db;
}

.lightbox:not([hidden]) {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 4rem 1.5rem 2rem;
  background: rgba(10, 9, 8, 0.88);
}

.lightbox-img {
  display: block;
  width: auto;
  height: auto;
  /* Capped at the screenshot's own native width, not the viewport's: these are UI captures,
     and stretching a 480px-wide PNG past its own resolution to fill a wide desktop window
     would trade the sharpness the lightbox exists to give back for a bigger blur. */
  max-width: min(90vw, 30rem);
  max-height: 82vh;

  /* The hairline and the radius came off with the plates' on 24.08.2026, and the reason they
     existed went with them. They were here because a dark-mode capture is nearly this
     backdrop's own colour, so an unbordered screenshot and the overlay read as one shapeless
     block. What the lightbox opens now is a framed plate: the bezel is the edge, and a
     rectangular border around an image with transparent corners draws a box the phone sits
     inside — the same defect, one surface further in.

     The black body on this near-black scrim is the case that would still argue for an edge,
     and it does not need one: the screen it encloses is a LIGHT capture under the light theme,
     which is what separates the object from the backdrop. The rim merging into the scrim is
     the phone's own silhouette, not a missing border. */
}

.lightbox-caption {
  margin: 0;
  max-width: 32rem;
  font-family: var(--sans);
  font-size: 0.9rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.75);
  text-align: center;
  text-wrap: pretty;
}

.lightbox-close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  display: grid;
  place-items: center;
  /* 44px — the only way out of a full-screen overlay on a phone. */
  width: 2.75rem;
  height: 2.75rem;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  cursor: pointer;
}

.lightbox-close svg { width: 1.1rem; height: 1.1rem; }
.lightbox-close:hover { background: rgba(255, 255, 255, 0.22); }

/* ---------- Print ----------
   A handbook gets printed, and this form in particular is one people print — a datasheet's
   whole ancestry is paper. It costs a dozen declarations and it is the clearest thing the
   carousel made impossible: four slides that were not showing did not print at all.

   The ladder prints in its delivered state, which is the state its caption describes first. */

@media print {
  .handbook {
    --paper: #fff;
    --ink: #000;
    --ink-2: #222;
    --muted: #555;
    --rule: #bbb;
    --rule-2: #777;
    --band: #f2f2f2;
    --field: #eaeaea;
    font-size: 10.5pt;
  }

  .handbook .runhead,
  .handbook .doc-index,
  .handbook .skip,
  .handbook .ladder-btn,
  .handbook .shot-zoom::after,
  .lightbox { display: none !important; }

  .handbook .sec,
  .handbook .step,
  .handbook .fig,
  .handbook .notice { break-inside: avoid; }

  .handbook h2 { break-after: avoid; }

  /* A link's target is invisible on paper, so the internal ones print as their section
     number and the external ones as their address. */
  /* var(--muted), which this block has already re-tinted to #555 for paper. The literal was a
     second copy of that decision, and a second copy is the one that gets missed. */
  .handbook a[href^="#"]::after { content: " (" attr(href) ")"; color: var(--muted); font-size: 0.85em; }

  /* Except the clause numbers, which are links to themselves. On paper "1.4 (#step-4)" is a
     coordinate with a duplicate of itself stapled to it, and the number was already the address
     the suffix exists to supply. */
  .handbook a.sec-no::after,
  .handbook a.step-no::after { content: none; }
}
