/* Lenora · One Monday
   Warm paper, light the whole way down. Forest is ink, never a ground.

   The cream ground is a decision, not a default. The design hook flags warm
   off-white as the reflex AI surface and it is usually right, so the rule is
   suppressed for this folder only, in .impeccable/config.json. The decision is
   in brief.md under Decisions, 2026-10-30: light, warm, even white, the whole
   way down, with both planned dark bands removed.
   No library, no build step, no webfont request: the two faces the brief names
   are system faces on the Mac and fall back cleanly everywhere else.

   Every text colour here is a solid value. No opacity on type anywhere, which
   is how the previous comp failed contrast in four places.

   03/11/2026. These numbers were measured against the cream ground #f1eee5
   and three forest-green inks, which this site stopped using when D31 and
   D32 moved it to the blue palette. Two of the three old inks are now the
   same colour. Re-measured against what actually ships:

     --ink   #1A384F   12.19:1 on the white card, 11.52:1 on the canvas
                       headlines, the clock, the readout values, figures
     --narr  #596F80    5.24:1 on the white card,  4.95:1 on the canvas
     --label #596F80    same colour as --narr now
                       narration, labels, captions, readout labels

   The old per-line comments below were corrected in the same pass. Where one
   still reads 14.15, 9.70 or 7.58, it was missed: re-measure, do not trust it.
*/

:root {
  --cream: var(--sc-canvas);
  --paper: var(--sc-surface);
  --ink: var(--sc-ink);
  --narr: var(--sc-ink-soft);
  --label: var(--sc-ink-soft);
  --acid: var(--sc-accent);

  /* A solid value, not ink at an opacity. #10231c at 18% over the cream
     ground computes to this exact colour, so nothing moved on screen, and the
     "solid values, never ink at an opacity" rule now holds for rules as well
     as for type. */
  --hair: var(--paper-line-2);

  /* One page measure and one gutter. Before this the page had three different
     left text edges at 1440: 130px, 231px and 181px. */
  --page: 1200px;
  --gut: 7vw;

  /* One type scale. The page carried 23 distinct sizes at 1440; these are the
     eleven it actually needs, at a ratio near 1.33 through the display range.
     Every step is chosen at or below the size it replaces, so nothing grew and
     no chapter's fit changed. The four fixed steps are interface type: labels,
     captions, ledger rows, buttons. The seven fluid steps are reading type. */
  /* 03/11/2026. Jared, looking at chapter II and chapter III: "i feel all
     the fonts can be bigger". Every step below the display sizes goes up.
     The four fixed steps were set against the brief's 11px FLOOR, and a
     floor is a minimum, not a target: 11px uppercase with 0.1em tracking is
     legible and still looks like fine print, which is the wrong register for
     the eleven lines the whole argument rests on. The three display steps
     (t-5, t-6, t-7) are unchanged. They are already the largest thing on the
     page and raising them only costs the chapters vertical room. */
  --t-xs: 13px;                           /* was 11px, the brief's floor */
  --t-s:  15px;                           /* was 13px */
  --t-m:  17px;                           /* was 15px */
  --t-b:  19px;                           /* was 18px */
  --t-1: clamp(19px, 1.9vw, 23px);        /* was 17 / 1.7vw / 21 */
  --t-2: clamp(22px, 2.4vw, 30px);        /* was 20 / 2.2vw / 28 */
  --t-3: clamp(26px, 3.1vw, 41px);        /* was 24 / 2.9vw / 38 */
  --t-4: clamp(32px, 4.4vw, 60px);        /* was 30 / 4.2vw / 56 */
  --t-5: clamp(40px, 6vw, 86px);
  --t-6: clamp(52px, 8.4vw, 118px);
  --t-7: clamp(56px, 11vw, 158px);

  --serif: var(--sc-font-display);
  --sans: var(--sc-font-text);
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--serif);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

/* The smallest type on the page. The brief sets an 11px floor and this is it:
   nothing functional goes below this rule. */
.label {
  font-family: var(--sans);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--label);
}

/* ------------------------------------------------------------------ the HUD
   Fixed, bottom left. Three readouts that only ever move forward. The values
   sit in fixed-width boxes with tabular figures so a changing number never
   reflows the row. */
/* The background is the page's own cream, so it is invisible on an empty
   corner and stops page text reading through the figures where a chapter runs
   in normal flow (motion off, and every chapter on a phone). */
/* A band, not a chip. As a chip it covered only its own width, so a chapter
   eyebrow scrolling past the bottom edge was half hidden and half readable:
   "E ONE IT WOULD NOT DO" sitting beside the clock. The band is the same
   cream and carries the same hairline the phone layout already used. */
.hud {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--cream);
  border-top: 1px solid var(--hair);
  /* 01/11/2026. adapt.md. The palette button (fixed, x 15..53) sat on the
     TIME label at 768 wide, where --gut is 54px, so at least 64px on the left
     cleared it. 03/11/2026: the palette button is gone from this page, so the
     band starts at the gutter like everything else.
     The bottom padding clears an iPhone's home indicator, which sits in the
     last ~34px of the screen and would otherwise cross the readouts. */
  padding: 10px var(--gut) calc(12px + env(safe-area-inset-bottom, 0px)) var(--gut);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 18px;
  pointer-events: none;
}

/* Copy scrolling under the band was guillotined mid-glyph on the hairline.
   A short fade above it lets the line dissolve into the ground instead. */
.hud::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 28px;
  background: linear-gradient(to top, var(--cream), transparent);
  pointer-events: none;
}

.hud__pair { display: flex; align-items: center; gap: 8px; }

/* On a phone the chapters run in normal flow, so text scrolls under these.
   The readouts carry their own background so nothing reads through them. */
@media (max-width: 760px) {
  .hud { gap: 12px; padding: 10px 20px 12px; }
}

.hud__k { font-family: var(--sans); font-size: var(--t-xs); font-weight: 700;
          letter-spacing: 0.14em; color: var(--label); }

.hud__v {
  font-family: var(--sans);
  font-size: var(--t-s);
  font-weight: 800;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--sc-accent-ink);
  background: var(--acid);
  padding: 4px 8px;          /* the brief's 8px chip floor, both axes */
  min-width: 3.2em;
  text-align: center;
}

.hud__v--n { min-width: 2.4em; }

.hud__sep { width: 1px; height: 14px; background: var(--hair); }

/* 03/11/2026. The bigger type scale pushed the three readouts past a 320px
   screen: measured, the band ran to 330px against a 320px viewport, and the
   page gained 10px of sideways scroll that it did not have before. The
   readouts are a fixed instrument band, not reading matter, so under 360px
   they keep the sizes the rest of the page has left behind. 11px is still
   the floor in brief.md, and nothing here is a tap target. */
@media (max-width: 360px) {
  .hud { gap: 6px; padding: 6px 10px; }
  .hud__k { font-size: 11px; letter-spacing: 0.1em; }
  .hud__v { font-size: 13px; padding: 4px 6px; min-width: 2.9em; }
  .hud__v--n { min-width: 2.2em; }
}

/* The day as a rule along the bottom edge, 07:12 at the left to 18:00 at the
   right. It is scaled, never widened: animating width is layout work. */
.day {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 39;
  height: 1px;
  background: var(--hair);
  pointer-events: none;
}

.day__fill {
  height: 100%;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: left center;
}

/* -------------------------------------------------------------- chapter I
   A tall section with a sticky stage. The scroll fraction through the section
   drives everything inside it. */
#ch-1 { height: 360vh; position: relative; }

#ch-1.is-static { height: auto; }

.stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  background: var(--cream);
}

#ch-1.is-static .stage { position: relative; height: auto; padding: 72px 0 96px; }

.stage__inner {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0 var(--gut);
  text-align: center;
}

#ch-1.is-static .stage__inner { height: auto; }

.eyebrow { margin: 0 0 20px; }

.clock {
  font-family: var(--sans);
  font-weight: 800;
  font-size: var(--t-6);
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--ink);
  margin: 0;
  user-select: none;
}

.standing {
  font-family: var(--serif);
  font-size: var(--t-2);
  line-height: 1.45;
  color: var(--narr);
  margin: 22px 0 0;
  max-width: 24em;
}

/* ------------------------------------------------------------- the clearing
   The sentence the tally is carved around. Each line is its own span, because
   the mask is built per line: one client rect and known text per span makes
   the carve glyph-accurate instead of a bounding box. */
.clearing {
  margin: 0;
  padding: 0;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(86vw, 1100px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.02em;
  pointer-events: none;
}

.clearing .l {
  display: block;
  font-family: var(--serif);
  font-size: var(--t-6);   /* one step below --t-7, which the brief reserves
                              for the Chapter V climax, still unbuilt */
  line-height: 0.96;
  letter-spacing: -0.02em;
  color: var(--ink);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 700ms cubic-bezier(0.22, 1, 0.36, 1),
              transform 700ms cubic-bezier(0.22, 1, 0.36, 1);
}

.clearing .sub {
  display: block;
  font-family: var(--serif);
  font-style: italic;
  font-size: var(--t-2);
  line-height: 1.4;
  color: var(--narr);
  margin-top: 0.5em;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 520ms cubic-bezier(0.22, 1, 0.36, 1) 300ms,
              transform 520ms cubic-bezier(0.22, 1, 0.36, 1) 180ms;
}

.is-revealed .clearing .l,
.is-revealed .clearing .sub { opacity: 1; transform: none; }

/* The standing line steps back once the carved line arrives, so two pieces of
   narration are never competing at full strength. */
.is-revealed .standing,
.is-revealed .clock,
.is-revealed .eyebrow { opacity: 0; transition: opacity 420ms cubic-bezier(0.22, 1, 0.36, 1); }

/* ----------------------------------------------------------------- the jobs
   Fourteen things that arrived before ten o'clock. They sit in the outer
   margins, never over the clearing, and each one is tied to its stroke by a
   short rule. */
.mark {
  position: absolute;
  max-width: 15vw;
  display: flex;
  align-items: center;
  gap: 8px;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 220ms cubic-bezier(0.22, 1, 0.36, 1), transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
}

.mark.is-on { opacity: 1; transform: none; }

.mark__t { font-family: var(--sans); font-size: var(--t-xs); font-weight: 700;
           letter-spacing: 0.12em; text-transform: uppercase;
           color: var(--c, var(--label)); line-height: 1.3; }

.mark__r { width: 22px; height: 1px; background: var(--hair); flex: none; }

.mark--l { left: 2.5vw; text-align: left; }
.mark--r { right: 2.5vw; text-align: right; flex-direction: row-reverse; }

/* ------------------------------------------------- the hand-off rule, gone
   03/11/2026. A fixed 1px line, created here and moved down the page by each
   chapter in turn. Four cold design reviewers all reported it striking
   through body copy, and measurement agreed at four of five positions. Jared
   cut it. The hairlines already in the flow (.hook__rule, .cut, .bill__rule,
   .flat__hr, .end__rule) and the day rule along the bottom edge carry
   continuity instead, and none of them can cross a glyph. */

.caption {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 8vh;
  opacity: 0;
  transition: opacity 420ms cubic-bezier(0.22, 1, 0.36, 1);
  white-space: nowrap;
}

.is-handed .caption { opacity: 1; }

/* -------------------------------------------------------------- portrait
   The field gets a tighter pitch so its density relative to the letterforms
   is kept rather than diluted. The jobs cannot fit in margins, so they run as
   one column in the lower third and swap by opacity, never by layout. */
@media (max-width: 760px) {
  #ch-1 { height: 300vh; }

  .stage__inner { padding: 0 20px; }

  .clearing { width: calc(100vw - 40px); }
  .clearing .l { font-size: var(--t-5); }

  /* Portrait has no margins to put fourteen labels in, so they run as one
     column in the lower third: the four most recent are visible and the
     oldest falls away as the next arrives. Opacity and transform only,
     never layout. The slot is set by tally.js. */
  .clearing { top: 40%; }

  .mark,
  .mark--r {
    top: auto !important;
    bottom: 17vh;
    left: 20px;
    right: 20px;
    max-width: none;
    text-align: left;
    flex-direction: row-reverse;
    justify-content: flex-end;
    transform: translateY(0);
  }

  .mark__r { width: 14px; }


  .caption { white-space: normal; width: calc(100vw - 40px); text-align: center; }

  /* Portrait keeps TIME and DONE. The brief shrank the mobile HUD to the clock
     alone, which was right while DONE was permanently 0. From chapter II on,
     DONE moving off zero IS the moment, so hiding it loses the chapter on a
     phone. YOURS stays desktop-only: the two that fit are the two that matter. */
  /* 01/11/2026. On a phone the band started at x=20 and the palette button
     (fixed, x 15..53, y 790..830 at 844) sat on top of the TIME label: "ME 09:52".
     The band now runs edge to edge and its readouts start at x=64, clear of
     the button, and the band is 38px tall instead of 52px (measured), so it
     hides less of the line scrolling under it. */
  /* 03/11/2026. The 64px was reserved for that palette button, and the button
     is gone, so the band starts at 20px again. Three of four design reviewers
     said YOURS being display:none on a phone was the worst thing in the
     readouts: done against yours IS the page's argument, and a phone reader
     only ever saw half of it. The space the button was holding is what it
     needed. */
  .hud { left: 0; bottom: 0; gap: 8px; padding: 6px 16px; }
}

/* -------------------------------------------------------- reduced motion
   The end state, with nothing moving. The interaction, when there is any,
   survives: only the motion goes. */
/* 01/11/2026. adapt.md. At 768 the margin notes were 115px wide (15vw) and "The owner, asking about last month" ran to four lines. 21vw gives two to three, and stays clear of the carved headline. */
@media (min-width: 761px) and (max-width: 1100px) { .mark { max-width: 21vw; } }

@media (prefers-reduced-motion: reduce) {
  .clearing .l,
  .clearing .sub,
  .mark,
  .caption,
  .standing,
  .clock,
  .eyebrow { transition: none !important; opacity: 1; transform: none; }

  .is-revealed .standing,
  .is-revealed .eyebrow { opacity: 1; }
  .is-revealed .clock { opacity: 1; }

  /* With motion off the stage is a normal document: the field is drawn once,
     every word is at full strength, and the fourteen jobs become a plain list
     instead of margin notes with nothing to sit beside. */
  #ch-1 .stage { padding: 72px var(--gut) 140px; }

  .clearing { position: static; transform: none; width: auto; margin: 56px 0 8px; }

  .mark,
  .mark--l,
  .mark--r {
    position: static;
    top: auto;
    left: auto;
    right: auto;
    bottom: auto;
    margin: 9px 0;
    max-width: none;
    /* 01/11/2026. Each static job row spanned the whole stage, so tally.js
       carved a full-width box per row out of the field and the gates stopped
       at y=560 of 1079 at 1440 (measured: 0 gates below y=560). Fit-content
       carves only the words. */
    width: fit-content;
    align-self: flex-start;
    flex-direction: row-reverse;
    justify-content: flex-end;
    text-align: left;
  }

  .caption { position: static; transform: none; margin: 18px 0 0; opacity: 1;
             text-align: center; white-space: normal; }
}

/* ======================================================= CHAPTER II · 09:40
   The one it would not do.

   The fourteen jobs are TAKEN, never struck through. A strike reads as
   deleted, deleted reads as replaced, and replaced is the one thing this page
   must never imply. The mark is a square that fills, so the claim rule is
   enforced by the drawing and not only by the words.

   Warm orange is used once on the whole page, here, and only as a filled chip
   with ink text inside an ink border. As a bare bar on cream it measures
   1.76:1, which is not enough to carry meaning. Ink on orange measures 8.02:1
   and the border holds the shape at 11.52:1, so no meaning rests on the hue. */

.cut {
  border-top: 1px solid var(--hair);
  padding: 18px var(--gut);
  background: var(--cream);
}

#ch-2 { height: 420vh; position: relative; }
#ch-2.is-static { height: auto; }
#ch-2.is-static .stage { position: relative; height: auto; padding: 64px 0 96px; }

.stage__col {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 var(--gut);
  max-width: var(--page);
  margin: 0 auto;
}

#ch-2.is-static .stage__col { height: auto; }

/* 01/11/2026. MINIMUMS 2.1/2.2. The cap was `max-width: var(--page)` with the
   gutter padding inside it, so the content was 932px of a 1920 window (48%) and
   the visible text, a ~500px list left in that box, left 494px empty either
   side. Measured text block 494..1000 at 1920. From 1000px up the chapter is
   now two columns: the narration on the left, the fourteen and then the held
   pay run on the right, in a column whose content is --page wide. Same
   elements, same order in the DOM, no copy changed. */
@media (min-width: 1000px) {
  .stage__col {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    grid-template-rows: auto auto;
    column-gap: 6vw;
    align-content: center;
    max-width: calc(var(--page) + 2 * var(--gut));
  }
  .stage__col > .eyebrow--ch { grid-column: 1; grid-row: 1; align-self: end; }
  .stage__col > .narr { grid-column: 1; grid-row: 2; align-self: start; }
  .stage__col > .swap { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
}

.eyebrow--ch { margin: 0 0 24px; }

.narr {
  font-family: var(--serif);
  font-size: var(--t-2);
  line-height: 1.35;
  color: var(--narr);
  margin: 0 0 40px;
  max-width: 22em;
}

/* The fourteen and the one share a cell. The fourteen part away and the one
   arrives in the hole they leave, so the chapter never grows a second block
   below the first. */
.swap { display: grid; flex: none; }
.swap > * { grid-area: 1 / 1; }

/* Fixed height with the overflow clipped, so the parting is cut off by the
   box rather than faded out. No opacity touches this type. */
/* The rows share a grid cell with the held block, so they would otherwise
   sit on top of the two buttons and swallow the click. Nothing in here is
   interactive. */
.rows { position: relative; overflow: hidden; align-self: start;
        pointer-events: none; }

.row {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin: 0;
  height: 30px;
}

.row__sq {
  flex: none;
  width: 9px; height: 9px;
  border: 1px solid var(--ink);
  display: block;
  position: relative;
  top: -1px;
  overflow: hidden;
}

.row__fill {
  display: block;
  width: 100%; height: 100%;
  background: var(--ink);
  transform: scaleY(0);
  transform-origin: bottom center;
}

.row__t {
  font-family: var(--sans);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--label);          /* #596F80: 5.24:1 on white, 4.95:1 on the canvas */
  line-height: 1.3;
}

.row.is-taken .row__t { color: var(--ink); }   /* #1A384F: 12.19:1 on white, 11.52:1 on the canvas */

.row--held { margin: 0 0 26px; height: auto; align-items: center; }
/* 01/11/2026. adapt.md. Both chips were flex items. The hidden "Released by you" chip still took 130px, so at 390 "Held for you" wrapped to two lines and "The pay run" to two. Same grid cell now: one chip's width, and the released chip lands where the held one was. */
.row--held { display: grid; grid-template-columns: auto auto minmax(0, 1fr); justify-content: start; }
.row--held .row__t { white-space: nowrap; }
.row--held .chip { grid-column: 3; grid-row: 1; justify-self: start; white-space: nowrap; }
.row--held .row__t { color: var(--ink); }

.chip {
  font-family: var(--sans);
  font-size: var(--t-xs);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 8px;                 /* the brief's chip floor, both axes */
  border: 1px solid var(--ink);
  line-height: 1;
  opacity: 0;
}

.chip--orange { background: #ff9d65; color: var(--ink); }   /* 5.96:1 */
.chip--ink { background: var(--ink); color: var(--paper); } /* 16.14:1 */

/* 03/11/2026. The held row is one line of three nowrap columns: the time, the
   job and the chip. The bigger type took the "RELEASED BY YOU" chip to 160px
   and the row ran to 329px against a 320px screen, which gave the page 10px
   of sideways scroll it did not have before. The chip is a label on a state,
   not reading matter, so under 360px it holds at 11px, the same reason the
   readouts do. This block sits AFTER .chip rather than with the other narrow
   rules near the top of the file: .chip sets font-size at the same
   specificity, so an earlier block loses to it whatever the media query. */
@media (max-width: 360px) {
  .chip { font-size: 11px; letter-spacing: 0.08em; padding: 7px 6px; }
}

.row--held.is-held .chip--orange,
.row--held.is-released .chip--ink { opacity: 1; transition: opacity 240ms cubic-bezier(0.22, 1, 0.36, 1); }

/* --------------------------------------------------------- the held block */
/* Opaque. It sits over the list in the same grid cell, so without a ground of
   its own the parting rows read straight through the headline. */
.held {
  background: var(--cream);
  align-self: start;
  opacity: 0;
  transform: scale(0.92);
  transform-origin: left top;
}

.is-arrived .held {
  opacity: 1;
  transform: none;
  transition: opacity 320ms cubic-bezier(0.22, 1, 0.36, 1),
              transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}

.held__t {
  font-family: var(--serif);
  font-size: var(--t-4);
  line-height: 1.04;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0 0 16px;
}

.held__s {
  font-family: var(--serif);
  font-size: var(--t-1);
  line-height: 1.5;
  color: var(--narr);
  margin: 0 0 14px;
  max-width: 34em;
}

.caption--flow { position: static; transform: none; margin: 0 0 26px; opacity: 1;
                 white-space: normal; }
/* 01/11/2026. adapt.md. At 390, and with motion reduced at every width, the pay-run caption sat centred under left-aligned copy (text starts x=40, copy starts x=20). Doubled class beats the .caption rules in the phone and reduced-motion blocks. */
.caption.caption--flow { width: auto; text-align: left; }

/* ------------------------------------------------- the page's one control
   It hands work back. It does not ask for an email. */
/* `visibility: hidden` and not opacity alone. `pointer-events: none` stops the
   mouse but not the keyboard, so with opacity alone the first Tab on the page
   jumped past two chapters into these buttons and Enter answered the question
   before the reader had been asked it. `visibility` takes them out of the tab
   order, and snaps back at the end of the fade so it never swallows a press. */
.choice { display: flex; gap: 14px; flex-wrap: wrap; margin: 0 0 20px;
          opacity: 0; transform: translateY(12px);
          visibility: hidden; pointer-events: none; }

.is-asked .choice { opacity: 1; transform: none;
                    visibility: visible; pointer-events: auto;
                    transition: opacity 320ms cubic-bezier(0.22, 1, 0.36, 1), transform 320ms cubic-bezier(0.22, 1, 0.36, 1); }

.choice button {
  font-family: var(--serif);
  font-size: var(--t-b);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: 16px 22px;           /* ≥48px tall, over the 44px tap floor */
  cursor: pointer;
  transition: background 300ms cubic-bezier(0.22, 1, 0.36, 1), color 300ms cubic-bezier(0.22, 1, 0.36, 1);
}

.choice button:hover,
.choice button:focus-visible { background: var(--ink); color: var(--paper); }

.choice button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.choice.is-done button { cursor: default; }
/* 03/11/2026. The unchosen button used to fade with opacity alone, which
   leaves it in the tab order and in the accessibility tree. A keyboard reader
   could focus a button they cannot see and press it. visibility: hidden takes
   it out of both, and the opacity keeps the fade. Same choice as line 642. */
.choice.is-done button[aria-pressed="false"] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 300ms cubic-bezier(0.22, 1, 0.36, 1), visibility 0s 300ms;
}

.reply {
  font-family: var(--serif);
  font-size: var(--t-1);
  line-height: 1.5;
  color: var(--narr);
  margin: 0 0 24px;
  min-height: 1.5em;
}

/* ------------------------------------------------- the schedule receipt
   It expands under the answer, the way the reminder card does on
   deepseek.com/en/harness. Jared asked for the "give" in that motion.

   The give is NOT a back-out curve. MINIMUMS section 9 locks the page to
   cubic-bezier(0.22, 1, 0.36, 1) and a back-out is a different curve. It is
   built from the locked curve instead, in two parts: the card opens on a
   0fr to 1fr grid row, which is the only way a height animates to auto, and
   it settles 10px up, 2px past, then home. The three rows then arrive 70ms
   apart, which is where most of the feel in that card actually comes from.

   grid-template-rows is the animated property, not height, so nothing is
   measured in JavaScript and nothing reflows on a timer. */
.receipt {
  display: grid;
  grid-template-rows: 0fr;
  margin: 0 0 24px;
  transition: grid-template-rows 500ms cubic-bezier(0.22, 1, 0.36, 1);
}
.receipt__clip { overflow: hidden; }
/* display:grid beats the hidden attribute's own display:none, so without this
   the card is in the layout before anyone presses. Measured 01/11/2026 with
   motion reduced, where the rows are forced open: 138px of empty box above
   the pull line. On the normal page it was invisible only because 0fr
   collapsed it to zero height. */
.receipt[hidden] { display: none; }
.receipt.is-open { grid-template-rows: 1fr; }

.receipt__in {
  border: 1px solid var(--hair);
  background: var(--paper);
  padding: 16px 18px;
  max-width: 34rem;
  transform: translateY(10px);
}
.receipt.is-open .receipt__in { animation: receipt-settle 560ms cubic-bezier(0.22, 1, 0.36, 1) forwards; }

@keyframes receipt-settle {
  0%   { transform: translateY(10px); }
  68%  { transform: translateY(-2px); }
  100% { transform: translateY(0); }
}

.receipt__t {
  font-family: var(--sans);
  font-size: var(--t-s);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--label);
  margin: 0 0 12px;
}

.receipt__d { margin: 0; display: grid; gap: 10px; }
.receipt__d > div {
  display: grid;
  grid-template-columns: minmax(0, 10rem) minmax(0, 1fr);
  gap: 12px;
  align-items: baseline;
  transform: translateY(8px);
  opacity: 0;
}
.receipt.is-open .receipt__d > div {
  animation: receipt-row 460ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
.receipt.is-open .receipt__d > div:nth-child(1) { animation-delay: 160ms; }
.receipt.is-open .receipt__d > div:nth-child(2) { animation-delay: 230ms; }
.receipt.is-open .receipt__d > div:nth-child(3) { animation-delay: 300ms; }

@keyframes receipt-row {
  to { transform: translateY(0); opacity: 1; }
}

.receipt__d dt {
  font-family: var(--sans);
  font-size: var(--t-s);
  color: var(--label);
  margin: 0;
}
.receipt__d dd {
  font-family: var(--sans);
  font-size: var(--t-m);
  color: var(--ink);
  margin: 0;
}

.pull {
  font-family: var(--serif);
  font-size: var(--t-3);
  line-height: 1.34;
  color: var(--ink);
  margin: 0;
  opacity: 0;
  transform: translateY(12px);
}

.is-pulled .pull { opacity: 1; transform: none;
                   transition: opacity 320ms cubic-bezier(0.22, 1, 0.36, 1), transform 320ms cubic-bezier(0.22, 1, 0.36, 1); }

/* The chapter's one accent word. Acid under the ink, never behind it. */
.acc { color: var(--sc-accent); }

/* 03/11/2026. The name, under "One that does it." It carries the accent
   because DESIGN.md C1 reserves the accent for what Lenora does, and this is
   the line that says which one. Measured: #2671A5 on the canvas is 4.97:1. */
.pull__name { color: var(--sc-accent); font-weight: 600; }
/* The hand-off rule sits 40px under this block. The accent underline hangs
   below the baseline, so 40px read as 18px and the rule looked like it was
   cutting the line. */
#ch-2 .pull { padding-bottom: 10px; }

@media (max-width: 760px) {
  #ch-2 { height: 360vh; }
  .cut { padding: 14px 20px; }
  .stage__col { padding: 0 20px; }
  .narr { margin-bottom: 28px; }
  .row { height: 34px; gap: 11px; }
  .choice { flex-direction: column; }
  .choice button { width: 100%; padding: 16px 18px; }
}

@media (prefers-reduced-motion: reduce) {
  /* With motion off nothing parts, so the list and the held block cannot share
     a cell: they become a normal document, one under the other. */
  .swap { display: block; }
  .rows { pointer-events: auto; height: auto !important; }
  .held { margin-top: 36px; }
  .held, .choice, .pull { opacity: 1 !important; transform: none !important;
                          transition: none !important; }
  /* The receipt still appears on a press, it just does not animate in. The
     grid row is held open, the settle and the staggered rows are off, and the
     card renders at its end state the moment held.js unhides it. */
  .receipt { grid-template-rows: 1fr !important; transition: none !important; }
  .receipt__in,
  .receipt__d > div { transform: none !important; opacity: 1 !important;
                      animation: none !important; }
  .row { transition: none !important; }
  #ch-2 .stage { padding: 64px 0 96px; }
}

/* ===================================================================== HOOK
   Who Lenora is and what she does, before anything else. A visitor landing
   cold must not have to scroll to find out what this is.

   The ten jobs carry an empty square each, the same square Chapter II fills.
   The hook states the work waiting; the page then takes it. Nothing here
   animates on scroll: this screen has to be legible the instant it paints. */

/* The wordmark is a masthead at the top of the page, not a kicker stacked on
   the headline. A small tracked label sitting directly above an oversized hero
   line is the default AI SaaS hero shape, and this page is not that. It sits
   in the top corner with the headline centred well below it. */
.hook {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--cream);
  padding: 44px var(--gut) 72px;
}

.hook__in {
  width: 100%;
  max-width: var(--page);
  margin: 0 auto;
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.wordmark > p { margin: 0; font: inherit; letter-spacing: inherit; }
.wordmark {
  font-family: var(--sans);
  font-size: clamp(1.75rem, 1.2rem + 2.2vw, 2.75rem);   /* 03/11/2026: was var(--t-m), too small. ~28px phone, 44px desktop */
  font-weight: 800;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ink);              /* #1A384F: 12.19:1 on white, 11.52:1 on the canvas */
  margin: 0 0 auto;
  width: 100%;
  max-width: var(--page);
  align-self: center;
}

.hook__h {
  font-family: var(--serif);
  font-size: var(--t-5);
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin: 0 0 36px;
  max-width: 15em;
}

/* The hook's own static line. It used to need this comment to say it was not
   the page's travelling rule. There is no travelling rule any more. */
.hook__rule { height: 1px; width: 100%; margin: 0 0 32px; background: var(--hair); }

.hook__jobs {
  list-style: none;
  margin: 0 0 40px;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px 28px;
  max-width: var(--page);
}

.hook__jobs li {
  display: flex;
  align-items: center;
  gap: 11px;
  font-family: var(--sans);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--label);            /* #596F80: 5.24:1 on white, 4.95:1 on the canvas */
}

.hook__jobs .row__sq { top: 0; }

/* 01/11/2026. layout.md. "Treatment-plan follow-up" wrapped to two lines in a 190px cell at 1440 and 1920 while the other nine took one. Content-width columns spread across the measure keep every job on one line. */
@media (min-width: 1000px) {
  .hook__jobs { grid-template-columns: repeat(5, max-content); justify-content: space-between; }
  .hook__jobs li { white-space: nowrap; }
}

.hook__line {
  font-family: var(--serif);
  font-size: var(--t-2);
  line-height: 1.3;
  color: var(--narr);             /* #596F80: 5.24:1 on white, 4.95:1 on the canvas */
  margin: 0 0 44px;
}

/* The price is the line the owner-dentist is actually looking for. It ran as
   83 characters of 11px uppercase, which is a label treatment on a sentence.
   Sentence case, larger, and it stops being a caption. */
.hook__price {
  font-family: var(--sans);
  font-size: var(--t-b);
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.5;
  color: var(--ink);                  /* #1A384F: 12.19:1 on white, 11.52:1 on the canvas */
  margin: 0 0 14px;
  max-width: 34em;
}
/* 01/11/2026. typeset.md. At 34em the price line broke as a long line plus "per practice" alone (1440 and 1920, 580px then 105px). At 40em (720px) it sits on one line from 1000px up. Under 1000px it wraps as before. */
@media (min-width: 1000px) { .hook__price { max-width: 40em; } }

.hook__foot { margin: 0; }

/* The readouts belong to the day, not to the hook. They arrive with the clock. */
.hud, .day { opacity: 0; transition: opacity 420ms cubic-bezier(0.22, 1, 0.36, 1); }
body.is-started .hud,
body.is-started .day { opacity: 1; }

@media (max-width: 760px) {
  .hook { padding: 28px 20px 48px; }
  .wordmark { letter-spacing: 0.28em; }
  .hook__h { margin-bottom: 28px; }
  .hook__jobs { grid-template-columns: 1fr 1fr; gap: 10px 18px; margin-bottom: 32px; }
  .hook__line { margin-bottom: 32px; }
}

@media (prefers-reduced-motion: reduce) {
  /* 03/11/2026. These two are a readout of scroll position, and with reduced
     motion there is no scroll story to read. Every chapter's static end state
     runs at load, so the last one won and the FIRST screen showed TIME 11:15,
     DONE 31, YOURS 15. The ending, before the story. Four reviewers caught it.
     The counts are not lost: the close section still shows all twelve squares
     filled and its own "BY 18:00". Hiding a readout is not hiding content, and
     every interaction on the page still works. */
  .hud, .day { display: none; }
}

/* ====================================================== CHAPTER III · 11:15
   The price that doesn't move.

   Figures use tabular numerals in fixed-width boxes, so a textContent swap
   reflows nothing outside its own cell. No new colour is introduced: every
   value here is one of the four already measured at the top of this file. */

#ch-3 { height: 480vh; position: relative; }
#ch-3.is-static { height: auto; }
#ch-3.is-static .stage { position: relative; height: auto; padding: 64px 0 96px; }

/* 01/11/2026. The cap USED to be `max-width: var(--page)` with the gutter
   padding inside it, and `--gut` is 7vw. So the padding grew with the window
   while the cap stayed at 1200px, and the content shrank as the screen got
   bigger. Measured on the ledger's name column: 355px at 1440, 299px at 1920,
   184px at 2560. Jared's screenshot of this chapter on a wide display showed
   "Patient CRM & campaign suite" broken over three lines with the left half of
   the screen empty. The cap now holds the CONTENT at --page and lets the
   padding sit outside it, and --page itself grows with the viewport so a wide
   screen gets a wider ledger rather than a narrower one. */
.stage__cols {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 7fr 5fr;
  gap: 6vw;
  align-items: start;
  padding: 0 var(--gut);
  max-width: calc(clamp(1200px, 72vw, 1680px) + 2 * var(--gut));
  margin: 0 auto;
}

#ch-3 .stage { display: flex; flex-direction: column; justify-content: center; }
/* 01/11/2026. layout.md. The columns were a flex item with auto margins, so they shrank to fit: the ledger started at x=164 and the closing lines under it at x=120 (1440), 374 against 360 (1920). Full width puts every left edge at the same x. */
#ch-3 .stage__cols { width: 100%; }
#ch-3.is-static .stage { display: block; }

.bill__head { margin: 0 0 min(18px, 1.8vh); }

.bill__row {
  display: grid;
  grid-template-columns: 1fr auto 8.6ch;
  align-items: baseline;
  gap: 16px;
  margin: 0;
  padding: min(3px, 0.3vh) 0;
  opacity: 0;
}

.bill__k {
  font-family: var(--sans);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--label);                /* #596F80: 5.24:1 on white, 4.95:1 on the canvas */
}

.bill__u {
  font-family: var(--serif);
  font-style: italic;
  font-size: var(--t-m);
  color: var(--narr);                 /* #596F80: 5.24:1 on white, 4.95:1 on the canvas */
  white-space: nowrap;
}

/* Only the lines that are billed by use are marked, and only by a hairline. */

.bill__n {
  font-family: var(--sans);
  font-size: var(--t-m);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
  color: var(--ink);                  /* #1A384F: 12.19:1 on white, 11.52:1 on the canvas */
  text-align: right;
  white-space: nowrap;
}

/* REMOVED 03/11/2026: the proportional bar on each ledger row.

   It was added on 01/11 as a full-height grey block behind the row, so a
   $3,000 line showed as far bigger than a $95 one. That block failed twice:
   on a phone it was a grey stub behind the first word, and on desktop the
   label sat on it at 3.11:1, which fails AA. Earlier today it became a 3px
   band along the bottom of each row, which fixed both.

   Jared then looked at the fixed version and cut it: "I dont like the grey
   lines underneath the line itesm". His page, and he is reading what a
   visitor reads. The eleven figures are printed, right-aligned and tabular,
   so the size of each one is already on the page in the only form that
   cannot be misread.

   Everything it needed went with it: the ::before, the clear space its
   descenders needed, the phone and short-viewport overrides, and the --amt
   custom property that js/bill.js used to set. */

/* With motion off the figures are ranges, not single values, so the column is
   sized to the range instead of to the fixed row width. */
#ch-3.is-static .bill__row { grid-template-columns: 1fr auto max-content; }

.bill__step {
  margin: 10px 0 0;
  opacity: 0;
  transition: opacity 300ms cubic-bezier(0.22, 1, 0.36, 1);
}

.bill__step.is-on { opacity: 1; }

.bill__rule {
  height: 1px;
  background: var(--ink);
  margin: min(18px, 1.8vh) 0 8px;
  transform: scaleX(0);
  transform-origin: left center;
}

/* The total is set larger than the rows, so its column is sized to the figure
   rather than to the rows' fixed width. */
/* All three waited for nothing: the finished total, the verdict and the
   caption were on screen while five of eleven rows had landed. */
.bill__tot, .bill__verdict, .bill__cap {
  opacity: 0;
  transition: opacity 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.is-summed .bill__tot,
.is-summed .bill__verdict,
.is-summed .bill__cap { opacity: 1; }

.bill__tot { padding-top: 0; grid-template-columns: 1fr auto max-content; }
.bill__tot .bill__k { color: var(--ink); }
.bill__tot .bill__n { font-size: var(--t-1); }

/* The chapter's honest beat. The ledger lands under $2,000, so the page says
   that out loud instead of letting the reader discover it. */
/* 03/11/2026. Jared: "this should be in blue blue... as this is the tha
   tdoes it". Eleven lines above it name what the back office costs and not
   one of them does the work. This line is Lenora, so it takes the accent,
   which DESIGN.md C1 reserves for what Lenora does. It is the only ink on
   this screen that is not the ledger's. Measured: #2671A5 on the white field
   is 5.27:1 and on the canvas 4.97:1, both over AA. */
.bill__verdict {
  font-family: var(--serif);
  font-size: var(--t-1);
  line-height: 1.5;
  color: var(--acid);
  margin: min(16px, 1.6vh) 0 0;
  max-width: 44em;
}

.bill__verdict strong { font-family: var(--sans); font-weight: 800; }

.bill__cap {
  margin: min(22px, 2vh) 0 0;
  max-width: 46em;
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--t-s);
  line-height: 1.55;
  color: var(--label);                /* #596F80: 5.24:1 on white, 4.95:1 on the canvas */
}

/* --------------------------------------------------------- the flat price */
.flat { opacity: 0; transform: scale(0.94); transform-origin: left top; }

.is-priced .flat {
  opacity: 1;
  transform: none;
  transition: opacity 260ms cubic-bezier(0.22, 1, 0.36, 1),
              transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
}

.flat__k { margin: 0 0 6px; }

.flat__n {
  font-family: var(--sans);
  font-size: min(var(--t-5), 9.6vh);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  line-height: 0.95;
  color: var(--ink);
  margin: 0 0 8px;
}

.flat__n--sm { font-size: min(var(--t-4), 6.3vh); }

.flat__u { margin: 0; }

.flat__hr { height: 1px; background: var(--hair); margin: min(26px, 2.4vh) 0; }

/* A sentence, not a label. Same class of problem as the price line: uppercase
   is a label treatment, and this reads. */
.flat__note {
  margin: min(26px, 2.4vh) 0 10px;
  max-width: 24em;
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--t-m);
  line-height: 1.5;
  color: var(--ink);                  /* #1A384F: 12.19:1 on white, 11.52:1 on the canvas */
}

.flat__foot {
  font-family: var(--serif);
  font-size: var(--t-b);
  line-height: 1.5;
  color: var(--narr);
  margin: 0;
  max-width: 26em;
}

/* The line that makes the rest believable. */
.ch3__band {
  display: grid;
  /* 01/11/2026. layout.md. Was 1fr 1fr, so the second paragraph started at x=763 (1440) while the price column above it starts at 849 (7fr 5fr). Same split and gap as .stage__cols now. */
  grid-template-columns: 7fr 5fr;
  gap: 6vw;
  width: 100%;
  /* 01/11/2026. Same fault as .stage__cols above: the cap held the padding
     inside --page, so the band's text started 120px right of the ledger's at
     1920 (494 against 374) and ran 36 characters a line (408px columns). The
     cap now holds the content at --page: 14px off the ledger, 48 a line. */
  /* 01/11/2026. layout.md. Capped at --page (1200) while the columns above cap at 72vw: at 1920 the band text started at x=360 and the ledger at x=269. Same cap as .stage__cols. */
  max-width: calc(clamp(1200px, 72vw, 1680px) + 2 * var(--gut));
  margin: min(30px, 3vh) auto 0;
  padding: 0 var(--gut);
  opacity: 0;
  transform: translateY(8px);
}

.is-kept .ch3__band { opacity: 1; transform: none;
                      transition: opacity 300ms cubic-bezier(0.22, 1, 0.36, 1), transform 300ms cubic-bezier(0.22, 1, 0.36, 1); }

.ch3__band .flat__foot { max-width: 30em; }

/* 01/11/2026. adapt.md. Chapter III's phone layout (normal flow, price pinned to the bottom edge) now also serves a portrait tablet up to 1000px wide. At 768x1024 the pinned stage ran 13px past the viewport and the closing line sat under the HUD band; the ledger labels wrapped to two and three lines. Landscape tablets keep the pinned stage. */
@media (max-width: 760px), (max-width: 1000px) and (orientation: portrait) {
  .ch3__band { grid-template-columns: 1fr; gap: 20px; padding: 0 20px; }
  /* The band already pads. .kept pads again further down, so its text
     started 20px right of the paragraph above it. */
  .ch3__band .kept { padding-left: 0; padding-right: 0; }
}

.kept {
  font-family: var(--serif);
  /* 01/11/2026. typeset.md. Sat beside .flat__foot (18px) at 21px at 1920. Two paragraphs in one band now share one step. */
  font-size: var(--t-b);
  line-height: 1.5;      /* matches .flat__foot, so the two first lines share a baseline */
  color: var(--narr);
  max-width: 30em;
  margin: 0;
  padding: 0;
  width: auto;
}

.is-kept .kept { opacity: 1; transform: none;
                 transition: opacity 300ms cubic-bezier(0.22, 1, 0.36, 1), transform 300ms cubic-bezier(0.22, 1, 0.36, 1); }

.pull--ch3 { padding: 0 var(--gut); max-width: calc(clamp(1200px, 72vw, 1680px) + 2 * var(--gut)); width: 100%;
             margin: min(30px, 2.6vh) auto 0; }

.flat--bar { display: none; }

@media (max-width: 760px), (max-width: 1000px) and (orientation: portrait) {
  /* Stacking would mean the ledger and the price are never on screen at the
     same time, which loses the whole comparison. So the chapter is normal
     flow and the price is pinned to the bottom edge, like a total. */
  #ch-3 { height: auto; }
  #ch-3 .stage { position: relative; height: auto; display: block;
                 padding: 48px 0 0; }

  .stage__cols { grid-template-columns: 1fr; gap: 36px; padding: 0 20px; }

  /* The unit sits on its own line under the name. Named areas, because
     auto-placement pushes the amount onto a third row behind a full-width
     item and it lands under the name instead of beside it. */
  .bill__row {
    grid-template-columns: 1fr 8.6ch;
    grid-template-areas: "k n" "u u";
    gap: 2px 10px;
  }
  .bill__k { grid-area: k; }
  .bill__n { grid-area: n; }
  .bill__u { grid-area: u; font-size: var(--t-s); justify-self: start; }
  /* No band to clear any more, but two-line rows still need air between
     them or the unit line reads as part of the next row's name. */
  .bill__row:not(.bill__tot) { margin-bottom: 14px; }
  .bill__tot { grid-template-columns: 1fr max-content; }
  .kept, .pull--ch3 { padding: 0 20px; }
  .pull--ch3 { margin-bottom: 40px; }

  /* The HUD is fixed over the bottom 40px of the viewport. The bar's own
     bottom padding clears it, so the two never sit on top of each other. */
  /* 03/11/2026. The bar exists because, stacked on a phone, the ledger and
     the price are never on screen together and that loses the comparison.
     Once the ledger's own total has landed the comparison IS on screen, and
     the bar then printed "THIS MONTH $15,989.00" a second time about 400px
     under the first. It leaves when it stops being needed. */
  #ch-3 .stage.is-summed .flat--bar {
    opacity: 0;
    visibility: hidden;
    transition: opacity 240ms cubic-bezier(0.22, 1, 0.36, 1), visibility 0s 240ms;
  }

  .flat--bar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
    position: sticky;
    bottom: 0;
    z-index: 30;
    margin: 20px -20px 0;
    background: var(--cream);
    border-top: 1px solid var(--ink);
    padding: 12px 20px 56px;
  }

  .bar__cell { display: block; }
  .bar__cell--f { border-left: 1px solid var(--hair); padding-left: 14px; }
  .bar__k { display: block; margin-bottom: 3px; }

  .bar__n {
    display: block;
    font-family: var(--sans);
    font-size: var(--t-1);
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
  }

  .bar__n--f { font-size: var(--t-2); }
}

/* The phone block pads 20px. A tablet uses the page gutter, like every other chapter, so the ledger's left edge matches chapters I and II (x=54 at 768). */
@media (min-width: 761px) and (max-width: 1000px) and (orientation: portrait) {
  .stage__cols, .ch3__band, .pull--ch3 { padding-left: var(--gut); padding-right: var(--gut); }
  .kept, .ch3__band .kept { padding-left: 0; padding-right: 0; }
  .flat--bar { margin-left: calc(-1 * var(--gut)); margin-right: calc(-1 * var(--gut)); padding-left: var(--gut); padding-right: var(--gut); }
}

@media (prefers-reduced-motion: reduce) {
  .bill__row, .flat, .kept, .pull--ch3, .bill__step {
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
  .bill__rule { transform: scaleX(1) !important; }
  #ch-3 .stage { display: block; padding: 64px 0 96px; }
}

/* The chapter has eleven ledger lines, a total, a price column and two closing
   lines. All of it has to sit inside one viewport, so this block is sized
   against viewport height, not against the type scale used elsewhere. */
#ch-3 .narr { font-size: min(var(--t-2), 3.4vh); margin: 0 0 min(26px, 2.4vh); }
#ch-3 .eyebrow--ch { margin: 0 0 min(20px, 2vh); }
/* 01/11/2026. layout.md. The two sentences broke as "Ten bills and a timesheet. Not / one of them does the work. / They tell you it is waiting." (three lines, the first sentence cut mid-clause). A wider measure lets each sentence take one line: one line shorter, 24px at 1280x720. */
@media (min-width: 761px) { #ch-3 .narr { max-width: 32em; } }
#ch-3 .pull { font-size: min(var(--t-2), 3.2vh); }
#ch-3 .kept { font-size: min(var(--t-b), 2.4vh); }
/* The price was top-aligned with the chapter eyebrow, so $1,000 sat at the
   very top of the screen with 540px of nothing under it while the ledger it
   is compared against started 180px lower. It now starts level with the
   ledger's first line instead.

   Not centred: centring drops the block far enough that the page rule, which
   locks to the ledger's total line and runs the full width, cut straight
   through "One price, whatever the practice does that month." */
#ch-3 .stage__cols { align-items: start; }

/* 03/11/2026. The bigger type wrapped three lines inside the ledger card at
   1440x900: the card's heading, the "reporting & business-intelligence
   dashboard" row, and the caption. Three extra lines is about 70px, and
   chapter III has no 70px spare on a 900px screen: the first narration line
   was cut off at the top of the stage.

   The right column holds the two price blocks and then stops, so the room
   was there. 8fr/4fr instead of 7fr/5fr, with a 5vw gutter instead of 6vw,
   gives the ledger 743px instead of 650px at 1440. The price column still
   gets 371px, and the widest thing in it is "$2,000" at 118px tall, which
   measures 294px. */
@media (min-width: 761px) {
  #ch-3 .stage__cols { grid-template-columns: 8fr 4fr; gap: 5vw; }
}
/* Landscape only. Stacked on a phone there is no second column to line up
   with, and the offset just opened a 120px hole above the price. */
@media (min-width: 1001px), (min-width: 761px) and (orientation: landscape) { #ch-3 .flat { margin-top: min(120px, 12vh); } }

/* The HUD is fixed to the bottom left. The closing line has to clear it. */
#ch-3 .stage { padding-bottom: 72px; }
@media (max-width: 760px), (max-width: 1000px) and (orientation: portrait) { #ch-3 .stage { padding-bottom: 24px; } }

/* The readouts are fixed over the bottom of the viewport. Where a chapter runs
   in normal flow, the page needs room under its last line so that line is never
   permanently behind them. */
@media (max-width: 760px), (max-width: 1000px) and (orientation: portrait) { body { padding-bottom: 64px; } }
@media (prefers-reduced-motion: reduce) { body { padding-bottom: 72px; } }

/* 31/10/2026, the widened ledger. Eleven rows overflow a 1050-tall stage by
   5px. This tier closes the gaps between blocks and nothing else, so a large
   screen keeps its spacing. It is declared before the tighter tiers below so
   they win where both apply. */
@media (min-width: 761px) and (max-height: 1100px) {
  #ch-3 .ch3__band { margin-top: min(30px, 1.7vh); }
  #ch-3 .pull--ch3 { margin-top: min(27px, 1.5vh); }
  #ch-3 .bill__cap { margin-top: min(22px, 1.6vh); }
}

/* Short and normal laptop viewports. Eleven ledger rows plus the total do not
   fit a 900px-tall stage at the comfortable spacing: measured at -56px on
   1440x900 and -5px on 1680x1050 after the 31/10 ledger widening. The vh-aware
   clamps above are not enough on their own, because each row carries its
   padding twelve times over. */
@media (min-width: 761px) and (max-height: 1000px) {
  #ch-3 .bill__row { padding: 1px 0; }
  #ch-3 .ch3__band .flat__foot,
  #ch-3 .ch3__band .kept { font-size: var(--t-m); line-height: 1.45; }
  #ch-3 .narr { font-size: min(var(--t-2), 2.7vh); margin-bottom: 14px; }
  #ch-3 .eyebrow--ch { margin-bottom: 12px; }
  #ch-3 .bill__head { margin-bottom: 10px; }
  #ch-3 .bill__rule { margin-top: 12px; }
  #ch-3 .bill__cap { margin-top: 12px; font-size: var(--t-s); }
  #ch-3 .ch3__band { margin-top: 10px; }
  #ch-3 .pull--ch3 { margin-top: 8px; }
  #ch-3 .stage { padding-bottom: 44px; }
  #ch-3 .bill__verdict { margin-top: 8px; }
}

/* 800px and under. Eleven rows, the total, the verdict, the caption, the band
   and the pull line do not fit without closing every gap: measured at -28px
   on 1280x720 and -31px on 1024x768. Row type stays at 11px, which is the
   minimum in brief.md, so the space comes out of the gaps. */
@media (min-width: 761px) and (max-height: 800px) {
  #ch-3 .narr { font-size: min(var(--t-2), 2.4vh); margin-bottom: 10px; }
  #ch-3 .eyebrow--ch { margin-bottom: 10px; }
  #ch-3 .bill__head { margin-bottom: 6px; }
  #ch-3 .bill__step { margin-top: 6px; }
  #ch-3 .bill__rule { margin: 8px 0 6px; }
  #ch-3 .bill__verdict { margin-top: 6px; }
  #ch-3 .bill__cap { margin-top: 8px; }
  #ch-3 .ch3__band { margin-top: 6px; gap: 18px; }
  #ch-3 .pull--ch3 { margin-top: 4px; }
  #ch-3 .bill__cap { margin-top: 6px; font-size: var(--t-xs); }
  /* 01/11/2026. adapt.md. At 1024x768 and 1280x720 the closing line ended 11px and 4px under the top of the HUD band (727 and 672 against 716 and 668, measured). Rows lose their last 1px of padding each: 22px back. */
  #ch-3 .bill__row { padding: 0; }
  #ch-3 .stage { padding-bottom: 14px; }
}

/* 01/11/2026. adapt.md. On a short landscape screen the HUD takes less: 6px above and 8px below its chips instead of 10 and 12, and a 14px fade instead of 28. 12px of band and fade back to the chapter. */
@media (min-width: 761px) and (max-height: 800px) {
  .hud { padding-top: 6px; padding-bottom: 8px; }
  .hud::before { height: 14px; }
}

/* ============================================================ THE END PANEL
   D2, 31/10/2026. Four specialists found the page had no link, no form and no
   address anywhere, so a reader who finished the story had nothing to press.
   It sits after the last chapter, never over it. */

.end {
  background: var(--paper);
  border-top: 1px solid var(--ink);
  padding: clamp(56px, 9vh, 104px) var(--gut) clamp(72px, 11vh, 120px);
}

.end__in { max-width: var(--page); margin: 0 auto; }

.end__k { margin: 0 0 18px; }

.end__h {
  font-family: var(--serif);
  font-weight: 400;
  font-size: var(--t-4);
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 20px;
  max-width: 18em;
}

.end__p {
  font-family: var(--serif);
  font-size: var(--t-1);
  line-height: 1.5;
  color: var(--narr);
  margin: 0 0 32px;
  max-width: 34em;
}

.end__act { display: flex; flex-wrap: wrap; gap: 14px; margin: 0 0 8px; }

.btn {
  font-family: var(--sans);
  font-size: var(--t-b);
  font-weight: 700;
  line-height: 1;
  color: var(--paper);
  background: var(--ink);
  border: 1px solid var(--ink);
  padding: 18px 26px;            /* over the 44px tap floor on both axes */
  text-decoration: none;
  display: inline-block;
}

/* 01/11/2026. animate.md. The two end-panel buttons changed colour on hover with no transition (0ms). 300ms on the page's own curve, as the pay-run buttons already do. */
.btn { transition: background 300ms cubic-bezier(0.22, 1, 0.36, 1), color 300ms cubic-bezier(0.22, 1, 0.36, 1), border-color 300ms cubic-bezier(0.22, 1, 0.36, 1), transform 200ms cubic-bezier(0.22, 1, 0.36, 1); }
.btn:hover { background: var(--narr); border-color: var(--narr); }

.btn:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
/* 9.6. No :active on the end panel's buttons or the pay-run choice. */
.btn:active, .choice button:active { transform: translateY(1px); }
.btn:active { background: var(--narr); }
.btn--q:active { background: var(--ink); color: var(--paper); }

.btn--q { color: var(--ink); background: transparent; }
.btn--q:hover { color: var(--paper); background: var(--ink); }

.end__rule { height: 1px; background: var(--hair); margin: clamp(40px, 6vh, 72px) 0 26px; }

.end__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px 40px;
}

.end__t {
  font-family: var(--sans);
  font-size: var(--t-m);
  line-height: 1.6;
  color: var(--label);           /* #596F80: 5.24:1 on white, 4.95:1 on the canvas */
  margin: 0;
}

.end__t strong { color: var(--ink); font-weight: 800; }

.end__nav a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
/* 01/11/2026. MINIMUMS 5.4. About, Privacy and Credits were 43-49 x 20px on a
   phone. Padded to a 44px target without moving the line.
   02/11/2026. audit.md. The media query is gone: the same three links measured
   19px tall at 820 and 1440, where a mouse has to hit them. The target is now
   44px at every width and the negative margin still keeps the line where it was. */
.end__nav a { display: inline-block; min-height: 44px; line-height: 44px; padding: 0 6px; margin: -12px 0; }
.end__nav a:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }

/* ====================================================== NO JAVASCRIPT (D6)
   An inline script in <head> adds .js before first paint. Without it the page
   was 12,358px of blank scroll, because every chapter starts hidden and
   JavaScript is what reveals it. With no .js the chapters are laid out in
   normal flow at their end state: the same state prefers-reduced-motion gets.
   The buttons in chapter II stay visible and keyboard-operable, they simply
   record nothing, because recording is JavaScript's job. */

html:not(.js) #ch-1,
html:not(.js) #ch-2,
html:not(.js) #ch-3 { height: auto; }

html:not(.js) .stage {
  position: relative;
  height: auto;
  overflow: visible;
  display: block;
  padding: 56px var(--gut) 72px;
}

html:not(.js) .clock,
html:not(.js) .tally { display: none; }

html:not(.js) .row,
html:not(.js) .row--held,
html:not(.js) .bill__row,
html:not(.js) .bill__step,
html:not(.js) .bill__verdict,
html:not(.js) .bill__cap,
html:not(.js) .flat,
html:not(.js) .kept,
html:not(.js) .pull,
html:not(.js) .choice,
html:not(.js) .clearing,
html:not(.js) .clearing .l,
html:not(.js) .clearing .sub,
html:not(.js) .caption,
html:not(.js) .held {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
}

html:not(.js) .rows { height: auto; overflow: visible; }
html:not(.js) .row__fill { transform: scaleY(1); }
html:not(.js) .bill__rule { transform: scaleX(1); }
html:not(.js) .hud { display: none; }

/* The live figures are written by JavaScript, so with none they would be empty
   boxes. The static figures live in the markup's own fallback text. */
html:not(.js) .bill__n:empty::after { content: "see note"; }
/* 01/11/2026. MINIMUMS 7.4 and brief D8/D9. With JavaScript off every ledger
   row read "see note" and the total read "$1,324.30 – $1,461.40", the figure
   D1 had before D8 widened the ledger. The page's own total was $9,521, so a
   reader without scripts was shown a number the page contradicts. Each row now
   carries its frozen D9 figure (js/bill.js ROWS). 03/11/2026, D35: the hours
   row prints an em dash instead of $1,932.00 and the total is $7,589.00.
   The "Held for you" chip and the kept line were also opacity 0 with no script. */
html:not(.js) .bill__n[data-row="0"]:empty::after { content: "$5,250.00"; }
html:not(.js) .bill__n[data-row="1"]:empty::after { content: "$222.00"; }
html:not(.js) .bill__n[data-row="2"]:empty::after { content: "$340.00"; }
html:not(.js) .bill__n[data-row="3"]:empty::after { content: "$190.00"; }
html:not(.js) .bill__n[data-row="4"]:empty::after { content: "$122.00"; }
html:not(.js) .bill__n[data-row="5"]:empty::after { content: "$265.00"; }
html:not(.js) .bill__n[data-row="6"]:empty::after { content: "$160.00"; }
html:not(.js) .bill__n[data-row="7"]:empty::after { content: "$95.00"; }
html:not(.js) .bill__n[data-row="8"]:empty::after { content: "$3,000.00"; }
html:not(.js) .bill__n[data-row="9"]:empty::after { content: "$195.00"; }
html:not(.js) .bill__n[data-row="10"]:empty::after { content: "$3,000.00"; }
html:not(.js) .bill__n[data-row="11"]:empty::after { content: "$1,200.00"; }
html:not(.js) .bill__n[data-row="12"]:empty::after { content: "$950.00"; }
html:not(.js) .bill__n[data-row="13"]:empty::after { content: "$1,000.00"; }
html:not(.js) #bill-total:empty::after { content: "$15,989.00"; }
html:not(.js) .ch3__band,
html:not(.js) .kept,
html:not(.js) .chip--orange { opacity: 1; transform: none; }
html:not(.js) #flat-foot:empty::after {
  content: "One price, however many jobs the team runs. Nothing per user. Nothing per message. Thirty days notice, and your practice’s data leaves with you.";
}

/* The only focusable things on the page are in the end panel, so the first Tab
   threw a keyboard reader 12,160px down it. This gives them the jump on
   purpose, named, and gives any reader a way to the price without scrolling. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 60;
  font-family: var(--sans);
  font-size: var(--t-m);
  font-weight: 700;
  color: var(--paper);
  background: var(--ink);
  padding: 14px 20px;
  text-decoration: none;
}

.skip:focus {
  left: 12px;
  top: 12px;
}

/* 01/11/2026. MINIMUMS 5.2. On a phone the first visible call to action was
   "Book thirty minutes" at 10.3 screens (8,692px of 9,200). The skip link
   already says "Go to the price and the booking" and lands on it, so on a phone
   it is shown, in the hero under the wordmark, as an ink link of 44px height.
   First action: 0.07 screens. No new copy and no new element. The real button
   is still the end panel's. */
@media (max-width: 760px) {
  .skip, .skip:focus {
    left: 20px; right: 20px; top: 56px;
    background: none; color: var(--ink);
    padding: 0; min-height: 44px; display: flex; align-items: center;
    font-size: var(--t-s); line-height: 1.3;
    text-decoration: underline; text-underline-offset: 3px;
  }
  .skip:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }
}

/* Hidden once the end panel is on screen. See js/hud.js. */
body.is-ended .hud { opacity: 0; pointer-events: none; transition: opacity 200ms cubic-bezier(0.22, 1, 0.36, 1); }

/* 01/11/2026. MINIMUMS 9.1 and 9.5. Every transition above now uses
   cubic-bezier(0.22, 1, 0.36, 1) (was `ease` in 13 places and two other
   curves), and no hover or colour transition is under 200ms (two were 160 and
   180). Headings and the narration balance their lines, body copy avoids a
   one-word last line. Measured orphan: "it." alone on a line in Chapter II's
   narration at 390 and at 1920 (two-column). */
h1, h2, h3, .narr, .held__t, .pull, .hook__line, .end__h { text-wrap: balance; }
p, li { text-wrap: pretty; }

/* 01/11/2026. delight.md. Browser surfaces take the palette: a selected word is ink on acid (11.6:1 measured against the ink, acid is the HUD chip colour), the scrollbar is ink on cream. */
::selection { background: var(--acid); color: var(--ink); }
html { scrollbar-color: var(--label) var(--cream); }

/* 01/11/2026. delight.md. A figure on the HUD that goes up gets one 300ms nudge: a job was taken. DONE and YOURS only. TIME changes every few pixels of scroll, so it gets none, which keeps the animation from restarting on each scroll tick. js/hud.js adds and removes the class. */
@keyframes hud-tick { 0% { transform: scale(1); } 35% { transform: scale(1.14); } 100% { transform: scale(1); } }
.hud__v.is-tick { animation: hud-tick 300ms cubic-bezier(0.22, 1, 0.36, 1); }

/* 01/11/2026. overdrive.md. Moving from the story to About, Privacy, Book or Credits and back dissolves the page over 500ms instead of cutting. Cross-document view transition: opacity only, no script, ignored by browsers without it. */
@view-transition { navigation: auto; }
::view-transition-old(root),
::view-transition-new(root) { animation-duration: 500ms; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  .hud__v.is-tick { animation: none; }
}

/* 01/11/2026. optimize.md / MINIMUMS 7.4. With scripts off the page was 1820px wide at a 1440 window (380px of sideways scroll). Chapter I's headline sat on top of its own standing line, and in Chapter II the fourteen rows were printed over the pay-run headline and the two buttons. The same static rules the reduced-motion page uses are applied here: headline in flow, rows and held block one under the other, jobs as a plain list. */
html:not(.js) .clearing { position: static; transform: none; width: auto; margin: 40px 0 8px; align-items: flex-start; }
html:not(.js) .swap { display: block; }
html:not(.js) .held { margin-top: 36px; }
html:not(.js) .mark, html:not(.js) .mark--l, html:not(.js) .mark--r {
  position: static; max-width: none; margin: 6px 0; opacity: 1; transform: none; flex-direction: row-reverse; justify-content: flex-end;
}
html:not(.js) .caption { position: static; transform: none; white-space: normal; margin: 18px 0 0; }
html:not(.js) .stage__inner { height: auto; align-items: flex-start; text-align: left; }
html:not(.js) body { overflow-x: hidden; }


/* The blue Jared asked for on 02/11/2026: "we wanted the blue in the
   background etc. and text in blue etc". .bloom and the accent on em come
   from ../../theme/brand.css; the opening chapter only has to make room for
   the wash and keep its own content above it. */
.hook { position: relative; overflow: hidden; }
.hook > :not(.bloom):not(.hook__weave) { position: relative; z-index: 1; }
.bloom { top: 52%; }
@media (prefers-reduced-motion: reduce) { .bloom { transition: none; } }

/* ------------------------------------------------------------- the closing list
   delight, 02/11/2026. The hook opens with ten empty boxes and nothing on the
   page ever closed them. The day ends with the same ten, filled one at a time,
   and one line for the part that stays with the reader. No new claim: the ten
   names and the words are already on the page. */

@media (min-width: 980px) {
  .end__in { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, .75fr);
             gap: 0 clamp(48px, 6vw, 104px); align-items: start; }
  .end__copy { grid-column: 1; }
  .end__done { grid-column: 2; padding-top: 6px; }
  .end__rule, .end__foot, .end__t, .end__nav { grid-column: 1 / -1; }
}

.end__done-t { margin: 0 0 14px; font: 500 var(--t-xs)/1.2 var(--sc-font-mono);
               letter-spacing: .18em; text-transform: uppercase; color: var(--sc-accent); }

.end__jobs { list-style: none; margin: 0 0 18px; padding: 0;
             display: grid; gap: 10px 24px; }
.end__jobs li { display: flex; align-items: baseline; gap: 10px;
                font-size: var(--t-m); color: var(--narr); }

/* The fill is a wipe from the bottom edge, the way a line gets ruled off. */
.end__jobs .row__sq { top: 0; }
.end__jobs .row__sq::after {
  content: ""; position: absolute; inset: 0; background: var(--sc-accent);
  transform: scaleY(0); transform-origin: 50% 100%;
}
.end__done.is-in .row__sq::after {
  animation: end-fill 380ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes end-fill { to { transform: scaleY(1); } }
.end__done.is-in li:nth-child(1)  .row__sq::after { animation-delay:   0ms; }
.end__done.is-in li:nth-child(2)  .row__sq::after { animation-delay:  70ms; }
.end__done.is-in li:nth-child(3)  .row__sq::after { animation-delay: 140ms; }
.end__done.is-in li:nth-child(4)  .row__sq::after { animation-delay: 210ms; }
.end__done.is-in li:nth-child(5)  .row__sq::after { animation-delay: 280ms; }
.end__done.is-in li:nth-child(6)  .row__sq::after { animation-delay: 350ms; }
.end__done.is-in li:nth-child(7)  .row__sq::after { animation-delay: 420ms; }
.end__done.is-in li:nth-child(8)  .row__sq::after { animation-delay: 490ms; }
.end__done.is-in li:nth-child(9)  .row__sq::after { animation-delay: 560ms; }
.end__done.is-in li:nth-child(10) .row__sq::after { animation-delay: 630ms; }
.end__done.is-in li:nth-child(11) .row__sq::after { animation-delay: 700ms; }
.end__done.is-in li:nth-child(12) .row__sq::after { animation-delay: 770ms; }

/* The one that did not get handed over. It lands after the last box. */
.end__done-y { margin: 0; font-size: var(--t-m); color: var(--ink); font-weight: 600; }
.end__done.is-in .end__done-y { animation: end-y 420ms cubic-bezier(0.22, 1, 0.36, 1) 860ms both; }
@keyframes end-y { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .end__jobs .row__sq::after { transform: scaleY(1); animation: none !important; }
  .end__done-y { opacity: 1 !important; transform: none !important; animation: none !important; }
}

/* ===================================================== colour and surface */
/* 02/11/2026. Jared, looking at this page next to version 21: "its so white
   ... V-story is so white and text is all balck all the way through".
   He was right, and it measures. Over the page's 13,205px the browser
   reported 61 text elements in --narr, 47 in --ink and 4 in --acid, on one
   unbroken --cream ground from top to bottom. The brand colour was a
   garnish and nothing on the page said where a chapter began.
   He chose colour and surfaces, not a navigation bar, so the scroll story is
   untouched. Three moves, and no new file is downloaded for any of them.

   On why the surfaces are white cards and not tinted full-width panels: the
   readout strip is fixed and paints --cream, so a tinted panel behind a
   chapter would leave that strip sitting on the page as a band of the old
   ground. A card is inside the chapter, so the strip always meets --cream. */

:root {
  /* A hairline that belongs to the brand rather than to the greys. */
  --hair-blue: color-mix(in srgb, var(--sc-accent) 26%, var(--paper-line-2));
}

/* 1. The accent carries the page's own landmarks. ------------------------ */

/* Chapter I is one clock alone in the middle of the screen. That emptiness is
   the chapter, so it keeps it: the clock itself carries the colour. */
.clock { color: var(--acid); }

/* "Chapter II" and "Chapter III" only. The time and the title stay in the
   label grey, so the line still reads as one line. */
.eyebrow__n { font-weight: 700; color: var(--acid); }

/* The figures in Chapter III's band. */
.flat__n { color: var(--acid); }

.flat__hr { background: var(--hair-blue); }
.bill__rule { background: var(--hair-blue); }

/* 2. Two chapters get a surface. ----------------------------------------- */

/* Chapter II's list and Chapter III's ledger are the two blocks on this page
   with an edge to give. Chapter I is a clock in open space and the hook is a
   headline, and a card around either would be a box drawn for its own sake.

   The surface is a white field under a 2px accent rule, with no box drawn
   round it. A full border was tried first and it lost to the travelling
   .motif line: that line was fixed at the body, and .stage is position:
   sticky, which opens a stacking context whatever its z-index, so nothing
   inside a chapter could be raised above it. Measured at 1440, the pixel at
   y 635 inside the card read rgb(26, 56, 79) with the card at z-index 3.

   03/11/2026: the travelling rule is gone, so that constraint is gone with
   it and a full border is possible again. It has not been put back, because
   nobody asked for it. If the cards should be bordered, this is the comment
   that says they now can be. */
.swap,
#ch-3 .bill {
  background: var(--paper);
  border-top: 2px solid var(--acid);
  padding: 22px 26px;
}

/* The rows box is a fixed height with its overflow clipped, so the parting
   rows are cut by the box rather than faded. Padding the card must not eat
   into that height or the cut lands in the wrong place. */
.swap { padding: 20px 24px; }

/* The white field has to paint over the page's own ground, not under it. */
.swap,
#ch-3 .bill { position: relative; z-index: 1; }

@media (max-width: 760px) {
  .swap { padding: 16px 16px; }
  /* The ledger card is dropped on a phone and only its blue rule is kept.
     Stacked, the card runs 1284px tall while its content stops at about 1140,
     and the portrait price bar sits over the gap: the reader saw an empty
     white strip with a border under the price. The list card above is a
     different shape and keeps its card. */
  #ch-3 .bill {
    background: none;
    border-top: 2px solid var(--acid);
    padding: 18px 0 0;
  }
}

/* 3. The hook carries faint rules in the accent. ------------------------- */
/* Drawn with a repeating gradient and faded out by a mask, so the corners of
   the screen stay clean and nothing is downloaded. */
/* The selector carries .hook because `.hook > :not(.bloom)` above lifts every
   child to position: relative, which collapsed this layer to zero height. */
.hook > .hook__weave {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* Four long arcs, each one ring of a circle whose centre is off the screen,
     so what crosses the hero is a sweep rather than a hatch. A hatch was the
     first attempt and it read as graph paper at every width. The radii are
     wide enough that the curvature is gentle at 1440 and still gentle at 390,
     where the circle's centre moves no closer. */
  --arc: color-mix(in srgb, var(--sc-accent) 30%, transparent);
  background:
    radial-gradient(circle 1100px at -12% 118%,
      transparent 0 1098px, var(--arc) 1098px 1100px, transparent 1100px),
    radial-gradient(circle 1460px at -4% 132%,
      transparent 0 1458px, var(--arc) 1458px 1460px, transparent 1460px),
    radial-gradient(circle 980px at 108% -22%,
      transparent 0 978px, var(--arc) 978px 980px, transparent 980px),
    radial-gradient(circle 1320px at 116% -8%,
      transparent 0 1318px, var(--arc) 1318px 1320px, transparent 1320px);
  background-repeat: no-repeat;
  /* The headline and the copy sit in the left two thirds, and the first
     attempt ran arcs straight through both. The mask keeps the art off that
     column entirely and brings it up only on the open right-hand side, where
     the bloom already sits. */
  -webkit-mask-image: linear-gradient(96deg, transparent 0 50%, #000 94%);
          mask-image: linear-gradient(96deg, transparent 0 50%, #000 94%);
}

/* ===================================================================== */
/* Phones and tablets, 03/11/2026. Jared: "also i need you to make this
   website mobile optimised".

   The mechanical checks were already clean at 320, 360, 390, 430 and 768:
   no horizontal overflow, no tap target under 44px, no text under 11px, no
   clipped text, no console errors. What was wrong was everything a checker
   cannot see, and all of it was measured. */

/* 1. The sticky stage was height: 100vh.

   On a phone, 100vh is the LARGE viewport: the height the page would have if
   the browser's address bar were hidden. While the bar is showing, which is
   most of the time, every pinned chapter is taller than the screen and its
   last lines are below the fold, and the page jumps the moment the bar
   retracts. 100svh is the small viewport, the height that is always visible,
   so the chapter fits whatever the bar is doing.

   The vh line stays first as the fallback for a browser without svh. */
.stage { height: 100vh; height: 100svh; }
.hook { min-height: 100vh; min-height: 100svh; }

/* 2. The readout band is fixed over the bottom 42px of a phone screen, and
   the stage centres its content in the full height, so content ran under it.
   Measured at 390x844 over 31 scroll positions: 29 distinct pieces of text
   passed beneath the band, among them "Nothing on that list needed you to do
   it", "It did not release the pay run", the word "yours", and four of the
   fourteen job rows. The band's own top edge is at y=802 of 844.

   The stage keeps its full height, so its background still fills the screen.
   Only the content inside it is pushed clear. The hook is excluded: the band
   is not shown until the reader reaches chapter I. */
@media (max-width: 760px), (max-width: 1000px) and (orientation: portrait) {
  .stage__inner,
  .stage__col,
  .stage__cols { padding-bottom: calc(50px + env(safe-area-inset-bottom, 0px)); }
}

/* 3. Chapter I spent three phone screens on a clock.

   Measured at 390x844: frames at 7%, 14% and 21% of the page were the same
   composition with a different clock reading and a few more labels, roughly
   2,500px of scrolling. DESIGN.md's Flow chapter calls that dead scroll, and
   its settled ceilings are 180vh, 200vh and auto on a phone. The old values
   were 300vh and 360vh. Chapter III is already auto here. */
@media (max-width: 760px), (max-width: 1000px) and (orientation: portrait) {
  #ch-1 { height: 180svh; }
  #ch-2 { height: 200svh; }
}

/* 3b. A short screen did not fit chapter II, at any width.

   The chapter stacks an eyebrow, a three-line thesis, and a grid cell that
   holds both the fourteen rows and the pay-run card. The card is the taller
   of the two and it is what sets the height. Measured, column wanted against
   column had, with the band already reserved:

     320x568   675 of 568    over by 107
     375x667   694 of 667    over by  27
     360x740   740 of 740    fits
     390x844   844 of 844    fits

   The stage is overflow: hidden, so over means cut off, and at 320 that was
   17 pieces of text including the chapter's own thesis and the word "yours".

   Two steps, each sized to what was measured rather than to a round number.
   Under 700px tall the margins come in, which buys 22px and covers the 27 at
   667 with the eyebrow's own shrink. Under 620px tall no trim can find 107px,
   so the chapters stop pinning and run in normal flow, which is the same
   shape the reduced-motion page already uses. A phone in landscape is about
   390px tall and lands in the same rule. */
@media (max-height: 700px) {
  #ch-2 .eyebrow--ch { margin-bottom: 14px; }
  #ch-2 .narr { margin-bottom: 16px; }
}

@media (max-height: 620px) {
  #ch-1, #ch-2, #ch-3 { height: auto; }
  #ch-1 .stage, #ch-2 .stage, #ch-3 .stage {
    position: relative;
    height: auto;
    overflow: visible;
    padding: 48px 0 64px;
  }
  #ch-1 .stage__inner,
  #ch-2 .stage__col,
  #ch-3 .stage__cols { height: auto; }
}

/* 5. The day rule is 1px at the very bottom edge, which on an iPhone sits
   under the home indicator. It moves up by the safe-area inset so it stays
   visible, and the band above it already does the same. */
@supports (bottom: env(safe-area-inset-bottom)) {
  .day { bottom: env(safe-area-inset-bottom, 0px); }
}


/* 03/11/2026. Jared: remove the chapter number and the clock from the chapter
   lines, keep the titles, make them larger, and give them room under the top
   edge. The clock still runs in the HUD at the bottom. */
.chtitle {
  font-family: var(--sc-font-display);
  font-size: var(--t-3);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.015em;
  text-transform: none;
  color: var(--ink);
  margin: 0 0 clamp(14px, 2.4vh, 26px);
}
#ch-3 .eyebrow--ch.chtitle { margin: 0 0 clamp(10px, 1.8vh, 20px); }
@media (min-width: 761px) {
  #ch-2 .stage, #ch-3 .stage { box-sizing: border-box; padding-top: clamp(40px, 7vh, 80px); }
}
.cut:empty, .cut > :empty { min-height: 0; }

/* The ledger grew to fourteen lines and the title grew from 13px to 41px, so
   the block no longer fitted a 900px screen and its top was cut off. Tighter
   rows, a title sized against the screen height, and the top and bottom room
   sized against it as well. Measured at 1440x900, 1280x720 and 1920x1080. */
@media (min-width: 761px) {
  #ch-3 .stage { padding-top: clamp(36px, 5.6vh, 64px); padding-bottom: clamp(44px, 6.4vh, 68px); }
  #ch-3 .bill__row { padding: 0; line-height: 1.25; }
  #ch-3 .chtitle { font-size: min(var(--t-3), 4.6vh); }
  #ch-3 .bill__head { margin-bottom: min(10px, 1.1vh); }
  #ch-3 .narr { margin-bottom: min(16px, 1.6vh); }
  #ch-2 .stage { padding-top: clamp(40px, 7vh, 80px); }
}

/* The closing lines now sit in the price column, under the price. */
@media (min-width: 761px) {
  #ch-3 .flat .ch3__band { display: block; padding: 0; margin-top: min(20px, 2.2vh); }
  #ch-3 .flat .ch3__band .flat__foot, #ch-3 .flat .ch3__band .kept { max-width: none; padding: 0; margin: 0 0 min(10px, 1.1vh); }
  #ch-3 .flat .pull--ch3 { padding: 0; max-width: none; width: auto; margin-top: min(18px, 2vh); font-size: min(var(--t-2), 3.4vh); }
}

/* Short screens: the price column is now the tallest thing in the chapter. */
@media (min-width: 761px) and (max-height: 880px) {
  #ch-3 .flat__n { font-size: min(var(--t-6), 11vh); }
  #ch-3 .flat__n--sm { font-size: min(var(--t-5), 8vh); }
  #ch-3 .flat { margin-top: 2vh; }
  #ch-3 .flat .pull--ch3 { font-size: min(var(--t-2), 3vh); }
}

/* The price column keeps the same proportions on short screens: stepped zoom,
   because the closing lines under the price are the tallest thing in the
   chapter and they must end above the bottom edge. */
@media (min-width: 761px) and (max-height: 920px) { #ch-3 .flat { zoom: 0.94; } }
@media (min-width: 761px) and (max-height: 840px) { #ch-3 .flat { zoom: 0.86; } }
@media (min-width: 761px) and (max-height: 760px) { #ch-3 .flat { zoom: 0.78; } }
@media (min-width: 761px) and (max-height: 680px) { #ch-3 .flat { zoom: 0.7; } }

/* Phone, chapter II. The fourteen-job list plus the larger title was taller
   than the screen, and the stage centres its content, so the title was cut
   off at the top (-15 px at 390x844, -94 px at 375x667). Tighter rows and
   narration, content starts at the top. */
@media (max-width: 760px) {
  #ch-2 .stage__col { justify-content: flex-start; padding-top: 22px; }
  #ch-2 .chtitle { font-size: 24px; margin-bottom: 8px; }
  #ch-2 .narr { font-size: var(--t-1); margin-bottom: 14px; }
  #ch-2 .row { height: 28px; }
  #ch-2 .swap { padding-top: 8px; padding-bottom: 8px; }
}

/* 03/11/2026. The divider between chapters had a solid cream background, so it
   covered the orbit behind it: arcs were cut at the line and the centre square
   was cut to a sliver. With the label gone it is only a hairline. */
.cut { background: transparent !important; padding: 0 !important; height: 0; }


/* 03/11/2026. Jared: the job labels were easy to miss; put the "An Australian
   dental practice" line and the "You got in..." sentence to the right of the
   time, with room around it; and let the labels move as the page scrolls. */
.mark { max-width: 17vw; margin-top: 4vh; transform: translateY(calc(var(--dy, 0px) - 4px)); }
.mark.is-on { transform: translateY(var(--dy, 0px)); }
.mark__t { font-size: clamp(15px, 1.3vw, 19px); font-weight: 800; letter-spacing: 0.1em; color: var(--c, var(--ink)); }
.mark__r { width: 34px; height: 2px; background: var(--ink); opacity: 0.35; }

@media (max-width: 760px), (max-width: 1000px) and (orientation: portrait) {
  .mark, .mark--r { max-width: none; margin-top: 0; }
  .mark { transform: translateY(0); }
}


/* 03/11/2026. Jared: next to the time, a second counter that rolls through the jobs the way the
   time rolls through minutes. Built by tally.js. Wide landscape screens only: below 1280px there
   is no room beside the time. */
.jobroll { display: none; }
@media (min-width: 1280px) and (orientation: landscape) {
  #ch-1 .clockblock { display: grid; grid-template-columns: auto auto; column-gap: clamp(40px, 4vw, 72px); justify-content: center; align-items: center; }
  #ch-1 .clockblock .eyebrow { grid-column: 1 / -1; grid-row: 1; }
  #ch-1 .clockblock .clock { grid-column: 1; grid-row: 2; }
  #ch-1 .clockblock .standing { grid-column: 1 / -1; grid-row: 3; max-width: 24em; margin-left: auto; margin-right: auto; }
  .jobroll { display: block; grid-column: 2; grid-row: 2; width: clamp(320px, 30vw, 520px); height: var(--h, 120px); overflow: hidden;
             position: relative; text-align: left; transition: opacity 420ms cubic-bezier(0.22, 1, 0.36, 1); }
  .jobroll__l { list-style: none; margin: 0; padding: 0; will-change: transform; }
  .jobroll__i { height: var(--h, 120px); box-sizing: border-box; padding-top: 0.5em; display: flex; align-items: center; font-family: var(--sans); font-weight: 800; text-transform: uppercase;
                letter-spacing: 0.04em; line-height: 1.12; color: var(--ink); font-size: clamp(22px, 2.3vw, 36px); }
  .is-revealed .jobroll { opacity: 0; }
}

/* 03/11/2026. polish. Jared: the pay-run text is not centred in its box, the job list does not
   fill the white box, and the price column does not line up with the ledger box. */

/* Chapter II. The held block painted a cream panel inside the white box, which left a white
   band round it. It now paints the box's own white. */
.held { background: var(--paper); }
/* The empty reply line reserved 78px under the buttons. It takes room only when it has text. */
.reply:empty { min-height: 0; margin: 0; }
/* Until the reader answers, the pull line is invisible but still takes its room, which left the
   block top-heavy. The block sits half that room lower, then settles when the pull arrives.
   held.js sets --hs. */
/* Desktop and landscape only. On a phone the block slid about 60px mid-scroll, which read as a glitch. */
@media (min-width: 1001px), (min-width: 761px) and (orientation: landscape) {
  .held > :not(.pull) { translate: 0 var(--hs, 0px); transition: translate 420ms cubic-bezier(0.22, 1, 0.36, 1); }
  .is-pulled .held > :not(.pull) { translate: none; }
}
/* The list spreads over the whole box. held.js sets --rowh to box height / row count. */
#rows .row { height: var(--rowh, 30px); box-sizing: border-box; padding-top: calc((var(--rowh, 30px) - 30px) / 2); }
@media (prefers-reduced-motion: reduce) { .held > :not(.pull) { translate: none; transition: none; } }

/* Chapter III, landscape. The price column is as tall as the ledger box: first label level with the
   box top, last line level with the box bottom, the space shared between the groups. */
@media (min-width: 1280px) and (orientation: landscape) {
  #ch-3 .stage__cols { align-items: stretch; }
  #ch-3 .flat { margin-top: 0; display: flex; flex-direction: column; justify-content: space-between; }
}


/* 03/11/2026. responsive. On a phone the skip link is absolutely placed under the wordmark and takes
   up to two lines at 375 wide, which printed over the headline. The headline block starts below it. */
@media (max-width: 760px) { .hook__in { margin-top: 32px; } }

/* 03/11/2026. responsive. Chapter II on a short phone. The pay-run block is 583px tall and the chapter
   is pinned to the screen height, so at 667 tall and below the closing line ran under the clock bar and
   past the end of the chapter. Type and spacing now scale with the screen height. Buttons stay 44px tall. */
@media (max-width: 760px) and (max-height: 800px) {
  #ch-2 .stage__col { padding-top: clamp(12px, 2.4vh, 22px); }
  #ch-2 .eyebrow--ch { margin-bottom: clamp(6px, 1.2vh, 14px); }
  #ch-2 .narr { font-size: clamp(16px, 2.7vh, var(--t-1)); line-height: 1.3; margin-bottom: clamp(8px, 1.6vh, 16px); }
  #ch-2 .swap { padding: clamp(8px, 1.6vh, 16px) 16px; }
  #ch-2 .held__t { font-size: clamp(23px, 4.2vh, var(--t-4)); margin-bottom: clamp(6px, 1.2vh, 16px); }
  #ch-2 .held__s { font-size: clamp(14px, 2.2vh, var(--t-1)); line-height: 1.4; margin-bottom: clamp(6px, 1vh, 14px); }
  #ch-2 .caption--flow { margin-bottom: clamp(8px, 1.8vh, 26px); }
  #ch-2 .choice { gap: clamp(6px, 1vh, 14px); margin-bottom: clamp(8px, 1.6vh, 20px); }
  #ch-2 .choice button { padding: clamp(10px, 1.5vh, 16px) 18px; font-size: clamp(16px, 2.2vh, var(--t-b)); }
  #ch-2 .pull { font-size: clamp(19px, 3vh, var(--t-3)); line-height: 1.25; }
}

/* Very short phones (568 tall and under, such as a 320x568 iPhone SE 1st gen). */
@media (max-width: 760px) and (max-height: 620px) {
  #ch-2 .stage__col { padding-top: 10px; }
  #ch-2 .eyebrow--ch { font-size: 22px; margin-bottom: 4px; }
  #ch-2 .narr { font-size: 15px; margin-bottom: 6px; }
  #ch-2 .swap { padding: 6px 14px; }
  #ch-2 .held__t { font-size: 21px; line-height: 1.05; margin-bottom: 4px; }
  #ch-2 .held__s { font-size: 13px; margin-bottom: 4px; line-height: 1.3; }
  #ch-2 .caption--flow { font-size: 12px; margin-bottom: 6px; }
  #ch-2 .choice { gap: 5px; margin-bottom: 6px; }
  #ch-2 .choice button { padding: 9px 14px; font-size: 15px; }
  #ch-2 .pull { font-size: 17px; line-height: 1.2; }
}
/* The job list sets the box height before the pay-run block arrives, so its rows shrink here too. */
@media (max-width: 760px) and (max-height: 620px) { #ch-2 .row { height: 22px; } #ch-2 .row__t { font-size: 12px; } }

/* 03/11/2026. responsive. Landscape phones (about 320 to 560px tall). Chapter II is pinned to the screen
   height and its pay-run block is taller than that, so the title ran off the top. Everything in the
   chapter shrinks to fit. Buttons keep a 40px tap height. */
@media (max-height: 560px) and (orientation: landscape) {
  #ch-2 .stage__col { padding-top: 6px; padding-bottom: 44px; }
  #ch-2 .eyebrow--ch { font-size: 18px; margin-bottom: 2px; }
  #ch-2 .narr { font-size: 14px; line-height: 1.25; margin-bottom: 4px; max-width: none; }
  #ch-2 .swap { padding: 6px 14px; }
  #ch-2 .row { height: 17px; }
  #ch-2 .row__t { font-size: 12px; }
  #ch-2 .held__t { font-size: 20px; line-height: 1.05; margin-bottom: 3px; }
  #ch-2 .held__s { font-size: 13px; line-height: 1.3; margin-bottom: 3px; }
  #ch-2 .caption--flow { font-size: 11px; margin-bottom: 4px; }
  #ch-2 .choice { gap: 6px; margin-bottom: 4px; }
  #ch-2 .choice button { padding: 9px 14px; font-size: 14px; }
  #ch-2 .pull { font-size: 16px; line-height: 1.2; }
}
@media (max-height: 560px) and (orientation: landscape) { #ch-2 .stage { padding-top: 8px; padding-bottom: 44px; } #ch-2 .stage__col { padding-top: 0; padding-bottom: 0; } }
@media (max-height: 400px) and (orientation: landscape) {
  #ch-2 .eyebrow--ch { font-size: 16px; }
  #ch-2 .narr { font-size: 13px; margin-bottom: 2px; }
  #ch-2 .row { height: 14px; } #ch-2 .row__t { font-size: 11px; }
  #ch-2 .held__t { font-size: 18px; } #ch-2 .held__s { font-size: 12px; }
  #ch-2 .choice button { padding: 7px 12px; font-size: 13px; }
  #ch-2 .pull { font-size: 15px; }
}

/* 03/11/2026. responsive. Phone details from the 390 and 375 review.
   End panel: same 20px edge as every other chapter (it used the wider page gutter), both buttons the
   same full width, and room between the buttons and the "By 18:00" list.
   Chapter I: the italic line under the headline stays centred when it wraps.
   Chapter III: the two closing paragraphs use one size (one followed the screen height, one did not). */
@media (max-width: 760px) {
  .end { padding-left: 20px; padding-right: 20px; }
  .end__act .btn { flex: 1 1 100%; text-align: center; }
  .end__done { margin-top: 32px; }
  .clearing .sub { text-align: center; margin-left: auto; margin-right: auto; }
  #ch-3 .flat__foot { font-size: min(var(--t-b), 2.4vh); }
}

/* 03/11/2026. responsive. Jared, on a phone: the ledger fonts were far too large and the rows used three
   lines each, so the ledger ran about 1,100px, more than a screen. Name, unit and amount now share one
   line (the name wraps to two only when it must), and type and spacing scale with the screen height so
   the ledger, from its title to the total, fits one screen. */
@media (max-width: 760px), (max-width: 1000px) and (orientation: portrait) {
  #ch-3 .stage { padding-top: clamp(8px, 1.6vh, 28px); }
  #ch-3 .stage__cols { gap: clamp(14px, 2.4vh, 36px); }
  #ch-3 .bill { padding: clamp(10px, 1.4vh, 20px) 14px; }
  #ch-3 .chtitle { font-size: min(clamp(18px, 3vh, var(--t-3)), 5.3vw); white-space: nowrap; margin-bottom: clamp(3px, .6vh, 12px); }
  #ch-3 .narr { font-size: clamp(13.5px, 1.75vh, var(--t-1)); line-height: 1.28; margin-bottom: clamp(5px, 1vh, 14px); max-width: none; }
  #ch-3 .bill__head { font-size: clamp(10.5px, 1.3vh, 12.5px); letter-spacing: 0.06em; line-height: 1.3; margin-bottom: clamp(3px, .7vh, 10px); }
  #ch-3 .bill__row {
    grid-template-columns: minmax(0, 1fr) auto max-content;
    grid-template-areas: "k u n";
    align-items: baseline;
    gap: 0 8px;
    margin-bottom: 0;
    padding: clamp(1.5px, .4vh, 4px) 0;
    line-height: 1.2;
  }
  #ch-3 .bill__k { font-size: clamp(11px, 1.4vh, 12.5px); letter-spacing: 0.03em; line-height: 1.2; }
  #ch-3 .bill__u { font-size: clamp(11px, 1.4vh, 12.5px); justify-self: end; }
  #ch-3 .bill__n { font-size: clamp(13px, 1.7vh, 15px); min-width: 7.6ch; }
  #ch-3 .bill__tot { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "k n"; }
  #ch-3 .bill__tot .bill__u { display: none; }
  #ch-3 .bill__tot .bill__n { font-size: clamp(16px, 2.2vh, var(--t-1)); }
  #ch-3 .bill__step { font-size: clamp(10.5px, 1.3vh, 12.5px); line-height: 1.3; }
  #ch-3 .bill__rule { margin: clamp(5px, .9vh, 14px) 0 3px; }
  #ch-3 .bill__verdict { font-size: clamp(15px, 2vh, var(--t-1)); margin-top: clamp(5px, .9vh, 14px); line-height: 1.3; }
  #ch-3 .bill__cap { font-size: clamp(11px, 1.35vh, 12.5px); line-height: 1.35; margin-top: clamp(5px, 1vh, 16px); }
}
/* Tighter again: the name column needs the width, so unit and amount shrink and the gaps close. */
@media (max-width: 760px), (max-width: 1000px) and (orientation: portrait) {
  #ch-3 .bill__row { gap: 0 6px; }
  #ch-3 .bill__k { font-size: 11px; letter-spacing: 0.02em; }
  #ch-3 .bill__u { font-size: 11px; }
  #ch-3 .bill__n { font-size: clamp(12.5px, 1.55vh, 14px); min-width: 7ch; }
  #ch-3 .eyebrow--ch.chtitle { margin-bottom: 4px; }
  #ch-3 .bill__step { margin-top: 4px; }
}
/* Shorter phones (under 780px tall): closer rows. */
@media (max-width: 760px) and (max-height: 780px), (max-width: 1000px) and (orientation: portrait) and (max-height: 780px) {
  #ch-3 .stage { padding-top: 6px; }
  #ch-3 .bill { padding-top: 8px; padding-bottom: 8px; }
  #ch-3 .bill__row { padding: 1px 0; line-height: 1.12; }
  #ch-3 .bill__k, #ch-3 .bill__u { line-height: 1.12; }
  #ch-3 .narr { font-size: 13px; line-height: 1.22; margin-bottom: 4px; }
  #ch-3 .bill__head { margin-bottom: 3px; }
  #ch-3 .bill__rule { margin: 4px 0 2px; }
  #ch-3 .bill__verdict { font-size: 14.5px; margin-top: 4px; }
  #ch-3 .bill__cap { margin-top: 4px; line-height: 1.25; }
}

/* 03/11/2026. responsive. Jared, on a phone, twice: the type is still too big. One smaller type scale for
   every phone screen. All sizes in the file come from these steps, so every chapter follows. Desktop and
   tablet keep the scale at the top of the file. Body text stays at 15px or more, labels at 11.5px. */
@media (max-width: 760px) {
  :root {
    --t-xs: 11.5px;
    --t-s:  13px;
    --t-m:  15px;
    --t-b:  16px;
    --t-1: 17px;
    --t-2: 19px;
    --t-3: 22px;
    --t-4: 27px;
    --t-5: 34px;
    --t-6: 44px;
    --t-7: 48px;
  }
}
