/* Rowsmith. "Adding-machine tape": a bookkeeper's adding machine proving the
   statement adds up; the tape is the proof. A clean near-white page; white
   paper for the rows, fields and sheets; the tape alone on ivory roll paper;
   ink; the ribbon's dusty crimson for negative figures and nothing else; the
   bookkeeper's yellow highlighter for what needs a look.
   Errors are ink with a filled mark and a dashed edge, so they never read as
   a red figure. See CLAUDE.md, "Design direction", for the measured contrast. */

/* The tape prints in its own face: Doto, a dot-matrix design (SIL OFL 1.1,
   fonts/OFL.txt), instanced at weight 900 with half-round dots and subset to
   the tape's characters (DEPENDENCIES.md). 1.1 KB. Only the tape uses it;
   anything else falls back to the system stack with tabular figures. */
@font-face {
  font-family: "Rowsmith Tape";
  src: url("../../fonts/doto-tape.woff2") format("woff2");
  font-weight: 100 1000;
  font-style: normal;
  font-display: swap;
  unicode-range: U+002B-002D, U+0030-0039, U+0054, U+2013-2014, U+2212;
}

:root {
  color-scheme: light;
  /* Grounds. The page is near-white, not cream; paper is for the rows,
     fields, sheets and the page view; tape is the ivory roll paper of the
     tape and nothing else, so the strip reads as the machine's own. */
  --ground: #f5f5f2;
  --paper: #ffffff;
  --sheet: #ffffff;
  --tape: #fff9e6;
  --tape-edge: #d3d0c6;
  --rule: #e4e1d8;
  --rule-strong: #c9c6bc;
  --hover: #f3f1ea;
  --backdrop: rgba(32, 32, 30, 0.36);
  /* Inks. Red is the second colour of the ribbon: negatives only. A dusty
     crimson, as ribbon ink prints, not a signal red, so it's no sibling's
     (ΔE ≥ 6 from every red in Coffer, Warden and Footfall; CLAUDE.md,
     Design direction). */
  --ink: #20201e;
  --ink-2: #5c5b56;
  --ink-hover: #3a3a37;
  --on-ink: #fffdf8;
  --red: #8d364a;
  /* Edges of fields and buttons: 3:1 against every ground they sit on. */
  --control: #8a8881;
  /* The dashed fill-in line under a cell at rest (3:1 or more where it shows):
     on paper and hovered rows, and on flagged rows. Dark mode has its own,
     so its lines weigh no more than light mode's. */
  --dash: #8a8881;
  --dash-flag: #8a8881;
  --focus: #20201e;
  /* The highlighter: rows that need a look, their lines on the page, blanks to fill in. */
  --highlight: rgba(255, 226, 77, 0.34);
  --highlight-2: rgba(255, 226, 77, 0.48);
  --mark-fill: rgba(255, 214, 10, 0.42);
  --mark-edge: rgba(128, 94, 0, 0.95);
  /* The tape's serrated tear, a 10 × 6 tile in the tape's own colours. */
  --tear-top: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 6 5 1 10 6Z' fill='%23fff9e6'/%3E%3Cpath d='M0 6 5 1 10 6' fill='none' stroke='%23d3d0c6'/%3E%3C/svg%3E");
  --tear-bottom: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0 5 5 10 0Z' fill='%23fff9e6'/%3E%3Cpath d='M0 0 5 5 10 0' fill='none' stroke='%23d3d0c6'/%3E%3C/svg%3E");

  /* The error mark (a disc with "!" cut out), as a mask, so it takes the text colour. */
  --icon-error: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 2.75a9.25 9.25 0 1 0 0 18.5 9.25 9.25 0 1 0 0-18.5Zm-1.2 4.75h2.4l-.4 7h-1.6ZM12 15.6a1.3 1.3 0 1 0 0 2.6 1.3 1.3 0 1 0 0-2.6Z'/%3E%3C/svg%3E");

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI Variable Text", "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  /* The adding machine's print head: the tape's figures and symbols only. */
  --font-tape: "Rowsmith Tape", var(--font);
  /* The type scale. Every size in this file is one of these (a test checks). */
  --text-caption: 12px;
  --text-footnote: 13px;
  --text-body: 15px;
  --text-headline: 17px;
  --text-title3: 20px;
  --text-title2: 24px;
  --text-title1: 30px;
  --text-display: 56px;
  /* One spacing scale on a 4 px grid. */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 20px;
  --s6: 24px;
  --s8: 32px;
  --s10: 40px;
  --s12: 48px;
  --s16: 64px;
  --s20: 80px;
  /* One small radius for everything that has one. The tape has none. */
  --radius: 3px;
  /* One grid: a 1,200 px column with 24 px gutters (16 on phones). */
  --page: 1200px;
  --gutter: 24px;
  --bar-h: 56px;
  --rail: 300px;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Dark: the desk at night, not an inversion. A charcoal page, the tape a
   step lighter, light ink, the crimson lifted to a rose that still reads as
   the ribbon's second colour, and the highlighter as a thin amber wash. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --ground: #161614;
    --paper: #1e1e1b;
    --sheet: #262521;
    --tape: #2a2822;
    --tape-edge: #4a4842;
    --rule: #34332e;
    --rule-strong: #4f4d46;
    --hover: #2c2b27;
    --backdrop: rgba(0, 0, 0, 0.62);
    --ink: #edeae2;
    --ink-2: #b8b5ac;
    --ink-hover: #d4d1c8;
    --on-ink: #1a1a18;
    --red: #ffb2be;
    --control: #9a978f;
    --dash: #7a7870;
    --dash-flag: #8b8881;
    --focus: #edeae2;
    --highlight: rgba(255, 196, 40, 0.13);
    --highlight-2: rgba(255, 196, 40, 0.17);
    --mark-fill: rgba(255, 214, 10, 0.26);
    --mark-edge: rgba(255, 214, 10, 0.8);
    --tear-top: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 6 5 1 10 6Z' fill='%232a2822'/%3E%3Cpath d='M0 6 5 1 10 6' fill='none' stroke='%234a4842'/%3E%3C/svg%3E");
    --tear-bottom: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0 5 5 10 0Z' fill='%232a2822'/%3E%3Cpath d='M0 0 5 5 10 0' fill='none' stroke='%234a4842'/%3E%3C/svg%3E");
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --ground: #161614;
  --paper: #1e1e1b;
  --sheet: #262521;
  --tape: #2a2822;
  --tape-edge: #4a4842;
  --rule: #34332e;
  --rule-strong: #4f4d46;
  --hover: #2c2b27;
  --backdrop: rgba(0, 0, 0, 0.62);
  --ink: #edeae2;
  --ink-2: #b8b5ac;
  --ink-hover: #d4d1c8;
  --on-ink: #1a1a18;
  --red: #ffb2be;
  --control: #9a978f;
  --dash: #7a7870;
  --dash-flag: #8b8881;
  --focus: #edeae2;
  --highlight: rgba(255, 196, 40, 0.13);
  --highlight-2: rgba(255, 196, 40, 0.17);
  --mark-fill: rgba(255, 214, 10, 0.26);
  --mark-edge: rgba(255, 214, 10, 0.8);
  --tear-top: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 6 5 1 10 6Z' fill='%232a2822'/%3E%3Cpath d='M0 6 5 1 10 6' fill='none' stroke='%234a4842'/%3E%3C/svg%3E");
  --tear-bottom: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0 5 5 10 0Z' fill='%232a2822'/%3E%3Cpath d='M0 0 5 5 10 0' fill='none' stroke='%234a4842'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------ base */

*, *::before, *::after { box-sizing: border-box; }
html { background: var(--ground); -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  margin: 0;
  min-height: 100vh;
  background: var(--ground);
  color: var(--ink);
  font: var(--text-body)/1.47 var(--font);
  /* Figures line up wherever they appear. */
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  caret-color: var(--ink);
}
#app { display: flex; flex-direction: column; min-height: 100vh; }
#app > .main { flex: 1; width: 100%; }
::selection { background: var(--highlight-2); color: var(--ink); }
a { color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
@media (hover: hover) { a:hover { text-decoration-thickness: 2px; } }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
h1, h2, h3, p, dl, dd, figure { margin: 0; }
.visually-hidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.skip-link { position: absolute; left: var(--s3); top: -64px; z-index: 50; padding: var(--s3) var(--s4); border-radius: var(--radius); background: var(--ink); color: var(--on-ink); font: 600 var(--text-footnote)/1 var(--font); text-decoration: none; }
.skip-link:focus { top: var(--s2); }
.muted { color: var(--ink-2); }
.small { font-size: var(--text-footnote); }
.num { font-variant-numeric: tabular-nums; }
.nowrap { white-space: nowrap; }
.spacer { flex: 1; }
.noscript { margin-top: var(--s6); color: var(--ink-2); }
.icon { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: square; stroke-linejoin: miter; flex-shrink: 0; }
.icon-sm { width: 16px; height: 16px; }

/* ------------------------------------------------------------ top bar: the wordmark, double-ruled like a final total */

.topbar { position: sticky; top: 0; z-index: 20; background: var(--ground); border-bottom: 1px solid var(--rule); }
.topbar-inner, .main, .site-foot-inner { max-width: calc(var(--page) + 2 * var(--gutter)); margin: 0 auto; padding-inline: var(--gutter); }
.topbar-inner { display: flex; align-items: center; gap: var(--s2); min-height: var(--bar-h); }
.topbar-inner > .btn-plain:last-child { margin-right: calc(-1 * var(--s4) - 1px); }
.promise-short { display: none; }
@media (max-width: 359px) {
  .promise-long { display: none; }
  .promise-short { display: inline; }
}
.brand { padding-bottom: 2px; border-bottom: 3px double var(--ink); color: var(--ink); font: 700 var(--text-headline)/1 var(--font-display); letter-spacing: -0.01em; text-decoration: none; }

/* ------------------------------------------------------------ buttons and fields */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 36px; padding: 0 var(--s4); border: 1px solid var(--control); border-radius: var(--radius);
  background: var(--paper); color: var(--ink); font: 600 var(--text-body)/1 var(--font);
  cursor: pointer; white-space: nowrap; text-decoration: none; transition: background-color 0.12s, transform 0.08s;
}
/* A key going down: the press moves a pixel, like a machine's key. */
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.45; cursor: default; transform: none; }
.btn[hidden] { display: none; }
.btn-primary { border-color: var(--ink); background: var(--ink); color: var(--on-ink); }
.btn-plain { border-color: transparent; background: transparent; color: var(--ink); }
.btn-danger { color: var(--ink); }
.btn-small { min-height: 32px; padding: 0 var(--s3); font-size: var(--text-footnote); }
.btn-large { min-height: 48px; padding: 0 var(--s6); font-size: var(--text-headline); }
.icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: var(--radius); background: transparent; color: var(--ink-2); cursor: pointer; }
.icon-btn:disabled { opacity: 0.35; cursor: default; background: transparent; }
.icon-btn .icon { width: 18px; height: 18px; }
@media (hover: hover) {
  .btn:hover { background: var(--hover); }
  .btn-primary:hover { border-color: var(--ink-hover); background: var(--ink-hover); }
  .btn-plain:hover { background: var(--hover); }
  .icon-btn:hover { background: var(--hover); color: var(--ink); }
  .btn:disabled:hover { background: var(--paper); }
}

.field { display: grid; align-content: start; gap: var(--s1); }
.field-label { font-size: var(--text-footnote); font-weight: 600; color: var(--ink); }
.field-hint { font-size: var(--text-footnote); color: var(--ink-2); }
/* Errors are ink with the filled mark, never red: red means a negative figure. */
.field-error, .cell-error, .tape-error { display: flex; align-items: flex-start; gap: var(--s1); font-size: var(--text-footnote); font-weight: 600; line-height: 1.35; color: var(--ink); }
.field-error::before, .cell-error::before { content: ""; flex-shrink: 0; width: 14px; height: 14px; margin-top: 2px; background: currentColor; -webkit-mask: var(--icon-error) center / contain no-repeat; mask: var(--icon-error) center / contain no-repeat; }
.field-error:empty { display: none; }
.input, .select {
  width: 100%; min-height: 40px; padding: var(--s2) var(--s3); border: 1px solid var(--control); border-radius: var(--radius);
  background: var(--paper); color: var(--ink); font: inherit;
}
.input::placeholder, .cell-input::placeholder, .tape-input::placeholder { color: var(--ink-2); opacity: 1; }
textarea.input { resize: vertical; font-family: var(--font-mono); font-size: var(--text-footnote); }
.input:focus, .select:focus { outline: 2px solid var(--focus); outline-offset: -1px; border-color: var(--focus); }
/* A value that can't be read: a dashed ink edge (focus is solid), plus the message. */
.input[aria-invalid="true"], .cell-input[aria-invalid="true"], .tape-input[aria-invalid="true"] { border-color: transparent; outline: 2px dashed var(--ink); outline-offset: -2px; background: var(--paper); }
.input[aria-invalid="true"]:focus, .cell-input[aria-invalid="true"]:focus, .tape-input[aria-invalid="true"]:focus { outline-style: solid; }
/* The selected segment is ink, like a key held down; the label carries it too (weight). */
.segmented { display: inline-flex; flex-shrink: 0; border: 1px solid var(--control); border-radius: var(--radius); background: var(--paper); }
.segmented button { min-height: 30px; padding: 0 var(--s3); border: 0; background: transparent; color: var(--ink-2); font: 500 var(--text-footnote)/1 var(--font); cursor: pointer; }
.segmented button + button { border-left: 1px solid var(--control); }
.segmented button[aria-pressed="true"] { background: var(--ink); color: var(--on-ink); font-weight: 650; }
@media (hover: hover) { .segmented button:not([aria-pressed="true"]):hover { color: var(--ink); background: var(--hover); } }
.seg-short { display: none; }
.btn-promise .promise-long, .btn-promise .promise-short { text-decoration: underline dotted 1px; text-underline-offset: 4px; }
.link-btn { padding: 0; border: 0; background: none; color: var(--ink); font: inherit; cursor: pointer; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
/* A plain label for real state (the sample), not a pill. */
.tag { display: inline-flex; align-items: center; min-height: 20px; padding: 0 var(--s1); border: 1px solid var(--ink); border-radius: var(--radius); color: var(--ink); font: 600 var(--text-caption)/1 var(--font); }

/* ------------------------------------------------------------ the tape */

/* Paper tape: a narrow strip cut by a serrated tear at both ends. Labels are
   the bookkeeper's pencil notes; figures are unsigned, with the machine's
   symbol (+, −, T) in its own column and anything taken away printed red. */
.tape { display: flex; flex-direction: column; color: var(--ink); font-variant-numeric: tabular-nums; }
.tape-tear { display: block; height: 6px; background: var(--tear-top) 0 0 / 10px 6px repeat-x; }
.tape-tear.is-bottom { background-image: var(--tear-bottom); }
.tape-lines { margin: 0; padding: var(--s2) var(--s4) var(--s3); list-style: none; background: var(--tape); border-inline: 1px solid var(--tape-edge); }
.tape-line { display: grid; grid-template-columns: minmax(0, 1fr) auto 20px; align-items: center; column-gap: var(--s2); min-height: 32px; }
.tape-label { min-width: 0; color: var(--ink-2); font-size: var(--text-footnote); line-height: 1.3; }
.tape-fig { font-family: var(--font-tape); font-size: var(--text-headline); font-weight: 560; line-height: 24px; text-align: right; white-space: nowrap; }
.tape-sym { justify-self: start; font-family: var(--font-tape); font-size: var(--text-headline); font-weight: 700; line-height: 24px; }
.tape-tick, .tape-alert { width: 18px; height: 18px; stroke-width: 2.4; vertical-align: -3px; }
/* Off by anything: the verdict's triangle, in ink (red still means only a negative figure). */
.tape-alert { stroke-width: 2; color: var(--ink); }
.tape-line.is-red .tape-fig, .tape-line.is-red .tape-sym, .tape-line.is-red .tape-input { color: var(--red); }
/* The rule the machine prints before a total. */
.tape-line.is-total { margin-top: var(--s2); padding-top: var(--s2); border-top: 1px solid var(--ink); }
.tape-line.is-total .tape-fig, .tape-line.is-result .tape-fig { font-weight: 650; }
.tape-line.is-result .tape-label { color: var(--ink); font-weight: 600; }
/* The two balances are the statement's own figures and are corrected in place:
   a dotted fill-in line at rest, a field when hovered or focused. Their printed
   copy shows only on a folded tape. */
.tape-static { display: none; }
.tape-input {
  width: 6em; min-height: 32px; margin-right: -5px; padding: 0 var(--s1); border: 1px solid transparent; border-bottom: 1px dashed var(--control); border-radius: 0;
  background: transparent; color: var(--ink); font: 560 var(--text-headline)/1 var(--font-tape); font-variant-numeric: tabular-nums; text-align: right;
}
.tape-input.is-empty { border: 1px solid var(--control); border-radius: var(--radius); background: var(--paper); }
/* "Missing" is a prompt, not a figure: lighter, in body type. */
.tape-input::placeholder { font-size: var(--text-body); font-weight: 400; }
@media (hover: hover) { .tape-input:hover { border: 1px solid var(--control); border-radius: var(--radius); } }
.tape-input:focus { outline: 2px solid var(--focus); outline-offset: -1px; border: 1px solid var(--focus); border-radius: var(--radius); background: var(--paper); }
.tape-line.is-invalid .tape-input { color: var(--ink); }
.tape-error { grid-column: 1 / -1; margin: var(--s1) 0 var(--s2); }
.tape-error .icon { margin-top: 1px; }

/* The check: the verdict in words, then the tape that proves it. */
.check { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4); align-content: start; }
.check-verdict { display: grid; gap: var(--s3); align-content: start; }
.check-title { display: flex; align-items: center; gap: var(--s2); font: 650 var(--text-title3)/1.25 var(--font-display); letter-spacing: -0.01em; }
.check-icon { width: 22px; height: 22px; stroke-width: 2; }
.check.is-unknown .check-icon { color: var(--ink-2); stroke-dasharray: 3 3.4; }
.check-sentence { margin-top: var(--s1); color: var(--ink-2); }
.check-hint { margin-top: var(--s1); color: var(--ink); font-weight: 500; }
.check-actions { display: flex; flex-wrap: wrap; gap: var(--s2); }
/* "Not quite right?": under the verdict when something may be read wrong; after the rows on
   phones, where the space under the verdict belongs to the flagged row's fix. */
.check-share, .rows-share { color: var(--ink-2); font-size: var(--text-footnote); line-height: 1.45; }
.check-share strong, .rows-share strong { color: var(--ink); font-weight: 600; }
.rows-share { display: none; }
.tape-toggle { display: none; }
.tape-toggle .icon { transition: transform 0.2s var(--ease); }
.tape-toggle[aria-expanded="true"] .icon { transform: rotate(180deg); }

/* ------------------------------------------------------------ welcome: the front door */

.main { padding-bottom: var(--s16); }
.welcome { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s10); padding-top: var(--s12); }
.hero h1 { max-width: 11em; font: 700 var(--text-display)/1.02 var(--font-display); letter-spacing: -0.035em; text-wrap: balance; }
.hero h1 span { display: block; }
.lede { max-width: 32em; margin-top: var(--s6); color: var(--ink-2); font-size: var(--text-title3); line-height: 1.45; }
.welcome-actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s8); }
.drop-hint { margin-top: var(--s3); color: var(--ink-2); font-size: var(--text-footnote); }
.start-error { margin: var(--s4) 0 0; color: var(--ink); font-size: var(--text-body); font-weight: 600; }
@media (hover: none) { .drop-hint { display: none; } }

/* The hero tape: the sample statement's check, labelled first as the
   sample's, then torn in two around the middle rows at every width, so the
   first screen shows the story (row 2 and the difference), not 18 figures. */
.hero-tape { width: min(100%, 320px); overflow: clip; }
/* The tape prints under its label. */
.hero-tape figcaption { position: relative; z-index: 1; padding-bottom: var(--s3); background: var(--ground); color: var(--ink-2); font-size: var(--text-body); line-height: 1.4; }
.hero-tape figcaption strong { color: var(--ink); font-weight: 600; }
/* The story in the margin, in pencil: what row 2 printed and what the balance says. */
.tape-note { font-size: var(--text-footnote); line-height: 1.3; }
.tape-line.is-marked .tape-note { font-weight: 500; }
.tape.is-hero .tape-lines { padding: var(--s4) var(--s5) var(--s4); }
.tape.is-hero .tape-line { min-height: 26px; }
.tape.is-hero .tape-line.tape-entry .tape-fig { line-height: 26px; }
.tape.is-hero .tape-line:not(.tape-entry) { min-height: 36px; }
.tape.is-hero .tape-line.is-total ~ .tape-line .tape-fig, .tape.is-hero .tape-line.is-total .tape-fig { font-size: var(--text-title3); }
/* Row 2, marked with the highlighter across the tape. */
.tape-line.is-marked { margin-inline: calc(-1 * var(--s2)); padding-inline: var(--s2); background: var(--highlight); }
.tape-line.is-marked .tape-label { color: var(--ink); font-weight: 600; }
/* Torn in two: the middle rows are left out (and counted). */
.tape.is-hero .tape-entry.is-more { display: none; }
.tape.is-hero .tape-gap {
  display: block; position: relative; margin: var(--s2) calc(-1 * var(--s5) - 1px); padding: var(--s3) var(--s5);
  background: var(--ground) var(--tear-bottom) 0 0 / 10px 6px repeat-x; color: var(--ink-2); font-size: var(--text-footnote);
}
.tape.is-hero .tape-gap::after { content: ""; position: absolute; inset: auto 0 0; height: 6px; background: var(--tear-top) 0 0 / 10px 6px repeat-x; }
/* The facts, as a spec list: a quiet label column, a strong value column. */
.spec { max-width: 40em; border-top: 1px solid var(--rule); }
.spec > div { display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: var(--s4); padding: var(--s3) 0; border-bottom: 1px solid var(--rule); }
.spec dt { color: var(--ink-2); }
@media (min-width: 960px) {
  /* The first screen holds the two ways in and the labelled tape, nothing
     else; the facts start below it. */
  /* The headline starts where the page does (it was centred in the first
     screen, under ~180 px of empty ground at 1280×860); the facts follow
     after a full break, at the foot of the first screen. */
  .welcome { grid-template-columns: minmax(0, 1fr) 380px; grid-template-areas: "hero tape" "spec ."; column-gap: var(--s16); row-gap: var(--s16); align-items: start; padding-top: var(--s16); }
  .hero { grid-area: hero; }
  .spec { grid-area: spec; max-width: none; }
  .hero-tape { grid-area: tape; width: auto; }
  .hero-tape figcaption { padding-top: var(--s2); }
  /* The machine's print at reading size: figures 20, totals 24, labels 15. */
  .tape.is-hero .tape-fig, .tape.is-hero .tape-sym { font-size: var(--text-title3); line-height: 30px; }
  .tape.is-hero .tape-line { min-height: 30px; }
  .tape.is-hero .tape-line.tape-entry .tape-fig { line-height: 30px; }
  .tape.is-hero .tape-line.is-total ~ .tape-line .tape-fig, .tape.is-hero .tape-line.is-total .tape-fig { font-size: var(--text-title2); }
  .tape.is-hero .tape-label { font-size: var(--text-body); }
  .tape.is-hero .tape-note { font-size: var(--text-footnote); }
}
/* One authored moment: the tape prints top down, a line at a time, once,
   so the difference comes last, as a machine prints it (site.css too). */
@media (min-width: 960px) and (prefers-reduced-motion: no-preference) {
  .hero-tape .tape:not(.is-printed) { animation: feed 2s steps(11, jump-start) 0.2s backwards; }
}
/* The tape slides down out of the machine a line at a time (the owner's
   choice, 2026-10-28, over lines appearing in place). */
@keyframes feed { from { transform: translateY(-100%); } }
/* Files dragged over the page: the whole window is the drop target. */
.is-dragging::after {
  content: "Drop to read the statements"; position: fixed; inset: var(--s3); z-index: 90; display: grid; place-items: center;
  border: 2px dashed var(--ink); border-radius: var(--radius); background: color-mix(in srgb, var(--ground) 92%, transparent);
  color: var(--ink); font: 650 var(--text-title3)/1.3 var(--font-display); pointer-events: none;
}

/* ------------------------------------------------------------ footer: the legal pages and how to reach us */

.site-foot { border-top: 1px solid var(--rule); color: var(--ink-2); font-size: var(--text-footnote); }
.site-foot-inner { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--s2) var(--s8); padding-block: var(--s5) calc(var(--s6) + env(safe-area-inset-bottom)); }
.site-foot ul { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s5); margin: 0; padding: 0; list-style: none; }
.site-foot a { color: var(--ink-2); }
@media (hover: hover) { .site-foot a:hover { color: var(--ink); } }
.site-foot a[aria-current="page"] { color: var(--ink); font-weight: 600; text-decoration: none; }
.is-docked .site-foot-inner { --page: 1632px; }

/* ------------------------------------------------------------ legal pages */

/* Jumps to a heading (the skip link, #legal-notice) land below the sticky bar. */
html:has(.doc) { scroll-padding-top: calc(var(--bar-h) + var(--s4)); }
.doc { max-width: 42em; padding-top: var(--s12); }
.doc h1 { font: 700 var(--text-title1)/1.15 var(--font-display); letter-spacing: -0.02em; }
.doc-meta { margin-top: var(--s2); color: var(--ink-2); font-size: var(--text-footnote); }
/* Marked with the highlighter: it needs a look before launch. */
.doc-draft { margin-top: var(--s6); padding: var(--s3) var(--s4); border-radius: var(--radius); background: var(--highlight); }
.doc-draft strong { font-weight: 650; }
.doc h2 { margin-top: var(--s10); font: 650 var(--text-title3)/1.3 var(--font-display); letter-spacing: -0.01em; }
.doc h3 { margin-top: var(--s6); font: 650 var(--text-body)/1.4 var(--font); }
.doc p, .doc ul, .doc dl { max-width: 68ch; margin-top: var(--s3); line-height: 1.6; }
.doc ul { padding-left: var(--s5); }
.doc li + li { margin-top: var(--s1); }
.doc h2 + p, .doc h3 + p { margin-top: var(--s2); }
/* A blank the owner fills in, highlighted like one on a form. */
.fill { padding: 0 2px; border-radius: var(--radius); background: var(--highlight); color: var(--ink); }
.doc .spec { margin-top: var(--s4); max-width: none; }
.doc .spec > div { grid-template-columns: 160px minmax(0, 1fr); }
/* Tables in the drafts: ledger hairlines, figures and words top-aligned. */
.doc table { width: 100%; margin-top: var(--s4); border-collapse: collapse; line-height: 1.5; }
.doc th { padding: var(--s2) var(--s3) var(--s2) 0; border-bottom: 1px solid var(--rule-strong); color: var(--ink-2); font-size: var(--text-footnote); font-weight: 600; text-align: left; vertical-align: bottom; }
.doc td { padding: var(--s2) var(--s3) var(--s2) 0; border-bottom: 1px solid var(--rule); vertical-align: top; }
.doc ol { max-width: 68ch; margin-top: var(--s3); padding-left: var(--s6); line-height: 1.6; }
.doc li > ul, .doc li > ol { margin-top: var(--s1); }
.doc code { padding: 1px 2px; border-radius: var(--radius); background: var(--paper); font: var(--text-footnote) var(--font-mono); }

/* ------------------------------------------------------------ workspace */

.workspace { padding-top: var(--s4); }
.workspace-main { min-width: 0; container: review / inline-size; }
/* The strip is ruled like ledger paper: a hairline under every row of
   buttons, so a current button's underline sits on a rule however many rows
   they wrap to (the rows are --tab-h tall, and so are the rules' steps). */
.file-strip { --tab-h: 40px; display: flex; align-items: flex-end; margin-bottom: var(--s6); }
.file-list { position: relative; isolation: isolate; display: flex; flex-wrap: wrap; flex: 1; min-width: 0; gap: 0 var(--s1); }
/* The rules: --rule through a mask of one hairline at the foot of each --tab-h step. */
.file-list::before { content: ""; position: absolute; inset: 0; z-index: -1; background: var(--rule); mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='40' preserveAspectRatio='none'%3E%3Crect y='39' width='4' height='1' shape-rendering='crispEdges'/%3E%3C/svg%3E") 0 0 / 4px var(--tab-h) repeat; pointer-events: none; }
.file-tab { display: inline-flex; align-items: center; gap: var(--s2); min-width: 0; max-width: min(260px, 100%); min-height: var(--tab-h); padding: 0 var(--s2); border: 0; border-bottom: 2px solid transparent; background: none; color: var(--ink-2); font: 500 var(--text-footnote)/1 var(--font); cursor: pointer; }
.file-tab.is-current { border-bottom-color: var(--ink); color: var(--ink); font-weight: 600; }
@media (hover: hover) { .file-tab:not(.is-current):hover { color: var(--ink); border-bottom-color: var(--rule-strong); } }
.file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Cut in the middle, never the end: an account's number, a file's dates stay. */
.cut { display: inline-flex; min-width: 0; max-width: 100%; }
.cut-head { min-width: 3ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cut-tail { flex-shrink: 0; white-space: pre; }
/* A PDF's accounts, together after its name (said once, in --ink-2, with the file's mark). */
.file-group { display: flex; flex-wrap: wrap; align-items: center; min-width: 0; max-width: 100%; column-gap: var(--s1); }
/* Space after a PDF's group (never before, so a row that starts with one starts flush). */
.file-list > .file-group:not(:last-child), .file-list > .file-tab:has(+ .file-group) { margin-right: var(--s4); }
.file-group-name { display: inline-flex; align-items: center; gap: var(--s1); min-width: 0; max-width: 200px; min-height: var(--tab-h); padding-right: var(--s1); color: var(--ink-2); font-size: var(--text-footnote); }
.file-group-name > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* An account's title alone takes the room the file's name had. */
.file-group .file-tab { max-width: min(320px, 100%); }
.file-dot { display: inline-flex; color: var(--ink-2); }
.file-dot .icon { width: 16px; height: 16px; }
.file-dot:not(.is-reading), .file-tab.is-current .file-dot { color: var(--ink); }
/* Not checked (yet, or ever: rows outside a PDF's accounts): the verdict's dashed ring. */
.file-dot.is-unknown .icon, .file-dot.is-apart .icon { stroke-dasharray: 3 3.4; }
.file-actions { display: flex; gap: var(--s2); padding: 0 0 var(--s2) var(--s3); border-bottom: 1px solid var(--rule); }
/* Phones with several files: one control in place of the tabs. */
.file-menu { display: none; position: relative; align-items: center; gap: var(--s2); flex: 1; min-width: 0; min-height: 44px; margin-bottom: var(--s2); padding: var(--s1) var(--s3) var(--s1) var(--s2); border: 1px solid var(--control); border-radius: var(--radius); background: var(--paper); }
.file-menu:focus-within { outline: 2px solid var(--focus); outline-offset: 2px; }
.file-menu-text { display: grid; flex: 1; min-width: 0; line-height: 1.25; }
.file-menu-count { overflow: hidden; font-size: var(--text-footnote); font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.file-menu-name { overflow: hidden; color: var(--ink-2); font-size: var(--text-caption); text-overflow: ellipsis; white-space: nowrap; }
.file-menu-chevron { color: var(--ink-2); }
/* At least 16 px, or iOS zooms in when it's tapped. */
.file-menu-select { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; font-size: var(--text-headline); cursor: pointer; }

/* Reading, a password, an error: plain words on the grid, left-aligned like
   the review. No box: it's the only thing on screen. */
.status-card { display: grid; gap: var(--s2); max-width: 560px; padding-top: var(--s4); }
.status-head { display: flex; align-items: center; gap: var(--s3); }
.status-card h2 { min-width: 0; font: 650 var(--text-title3)/1.3 var(--font-display); letter-spacing: -0.01em; overflow-wrap: anywhere; }
.status-card > p { padding-left: 36px; }
.status-icon { width: 24px; height: 24px; color: var(--ink); stroke-width: 1.8; }
.row-actions { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s3); padding-left: 36px; }
.password-form { display: grid; gap: var(--s1); margin-top: var(--s2); padding-left: 36px; }
.password-form .row-actions { padding-left: 0; }
.progress { width: 100%; max-width: 320px; height: 2px; margin: var(--s2) 0 0 36px; overflow: hidden; background: var(--rule); }
.progress span { display: block; height: 100%; background: var(--ink); transition: width 0.2s; }
.progress.is-waiting span { width: 30%; }
@media (prefers-reduced-motion: no-preference) {
  .progress.is-waiting span { animation: sweep 1.4s var(--ease) infinite; }
}
@keyframes sweep { from { transform: translateX(-100%); } to { transform: translateX(340%); } }

/* ------------------------------------------------------------ review */

.review-head { margin-bottom: var(--s6); }
.review-title { font: 700 var(--text-title1)/1.15 var(--font-display); letter-spacing: -0.02em; overflow-wrap: anywhere; }
/* The account, the period and the currency: separate facts, set apart by space. */
.review-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s4); flex: 1; min-width: 0; }
.review-meta > span { white-space: nowrap; }
.review-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s6); }
.review-rows { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4); min-width: 0; }

/* One account of several: how many the PDF holds, under the facts. */
.review-note { max-width: 60em; margin-top: var(--s2); color: var(--ink-2); font-size: var(--text-footnote); line-height: 1.4; }
/* Inline in a sentence: not padded out to a touch target (WCAG 2.5.8's inline exception). */
.review-note .link-btn { min-height: 0; color: var(--ink); text-align: start; }
/* How it's read is one of the facts, with "Change" beside it. */
.review-head .review-meta { margin-top: var(--s2); }
.review-kind { white-space: nowrap; }
.review-kind .link-btn { color: var(--ink); }
/* Under the verdict: the sample's note, in the verdict's own column. */
.sample-note { max-width: 34em; color: var(--ink-2); font-size: var(--text-footnote); line-height: 1.4; }
.sample-note a, .sample-note .link-btn { color: var(--ink); }
/* Inline in a sentence, so not padded out to a touch target (WCAG 2.5.8's inline exception). */
.sample-note .link-btn { min-height: 0; }

/* Phones, tablets and beside the docked page view: the tape folds to its
   result (T, the closing balance, the difference), so the rows start in the
   first screen; "Edit balances" unfolds it. */
@container review (width < 1040px) {
  .tape-toggle { display: inline-flex; }
  /* The "Sample" tag and the flagged row's own note say it here; the head
     stays short, so the flagged row is in the first screen. */
  .sample-note { display: none; }
  .tape.is-folded .tape-opening, .tape.is-folded .tape-in, .tape.is-folded .tape-out, .tape.is-folded .tape-input { display: none; }
  .tape.is-folded .tape-static { display: block; }
  .tape.is-folded .tape-line.is-total { margin-top: 0; padding-top: 0; border-top: 0; }
}
/* Phones: the folded tape right under the verdict prints the sentence's
   figures (what the rows add up to, the closing balance, the difference),
   so the sentence steps aside there (it's still announced), and the
   flagged row's fix stays in the first screen. */
@container review (width < 560px) {
  .check:has(> .tape.is-folded) .check-sentence { display: none; }
  /* The file control above says "Account 2 of 3 in cu.pdf", and lists each
     account with its state, so the note steps aside and a flagged row's fix
     stays in the first screen. */
  .review-note, .review-meta .meta-account { display: none; }
}
/* Tablets, and beside the docked page view: the verdict (and the sample's
   note), and the tape beside it. */
@container review (560px <= width < 1040px) {
  .review-body > .check { grid-template-columns: minmax(0, 1fr) 300px; column-gap: var(--s8); align-items: start; }
  .check-verdict { gap: var(--s2); }
  /* A tighter head, so the rows start in the first screen beside the page view. */
  .file-strip { margin-bottom: var(--s4); }
  .review-head { margin-bottom: var(--s4); }
  .review-body { gap: var(--s4); }
  .review-title { font-size: var(--text-title2); }
}
/* Wide review columns: the tape runs beside the rows as a rail, and stays in
   view while they scroll. */
@container review (width >= 1040px) {
  .review-body { grid-template-columns: minmax(0, 1fr) var(--rail); column-gap: var(--s8); align-items: start; }
  .review-rows { grid-column: 1; grid-row: 1; }
  /* Only the statement's own check is a sticky rail (never the Year view's verdict, which is static). */
  .review-body > .check { grid-column: 2; grid-row: 1; position: sticky; top: calc(var(--bar-h) + var(--s4)); }
  .check-hint, .check-sentence { font-size: var(--text-body); }
  .check .tape-lines { padding-inline: var(--s3); }
  .col-date { width: 136px; }
  .col-amount, .col-balance { width: 124px; }
}
@media (max-height: 640px) { .check { position: static !important; } }

.table-wrap { overflow: clip; border: 1px solid var(--tape-edge); border-radius: var(--radius); background: var(--paper); }
.tx-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
/* Headers start where the cell text does: 4 px cell padding + 1 px border + 7 px input padding. */
.tx-table th { vertical-align: bottom; }
.tx-table th { position: sticky; top: var(--bar-h); z-index: 2; padding: var(--s2) var(--s3); border-bottom: 1px solid var(--control); background: var(--paper); color: var(--ink-2); font-size: var(--text-caption); font-weight: 600; text-align: left; }
.tx-table td { padding: 2px var(--s1); border-bottom: 1px solid var(--rule); vertical-align: top; }
.tx-table tbody tr:last-child td { border-bottom: 0; }
.tx-table tr.is-flagged { background: var(--highlight); }
/* The row on the docked page: a cursor bar at its left edge. */
.tx-table tr.is-shown > td:first-child { box-shadow: inset 2px 0 0 var(--ink); }
@media (hover: hover) {
  .tx-table tbody tr:hover { background: var(--hover); }
  .tx-table tbody tr.is-flagged:hover { background: var(--highlight-2); }
}
/* Arrow keys and Tab keep the cell clear of the sticky header and export bar. */
.tx-table td :is(input, textarea, button) { scroll-margin: 104px 0 88px; }
.col-flag { width: 32px; text-align: center; }
/* Cells sit at the top of a tall row (a wrapped description, a flag note), so
   the flag and the printed balance line up with the first line of text. */
.tx-table td.col-flag { padding-top: 11px; }
.tx-table td.col-balance { padding-top: 10px; }
.tx-table th.col-flag { padding: 0; }
.col-date { width: 148px; }
.col-amount { width: 136px; }
.col-balance { width: 132px; padding-right: var(--s3) !important; color: var(--ink-2); font-size: var(--text-body); text-align: right; }
.tx-table tr.is-flagged .col-balance { color: var(--ink); }
/* One "Row actions" button: 32 px with a mouse, 44 px on touch. */
.col-actions { width: 48px; text-align: right; white-space: nowrap; }
@media (pointer: coarse) { .col-actions { width: 56px; } }
th.col-amount, th.col-balance { text-align: right; }
/* What a card's marks mean: under "Amount", or over the rows on phones (no header row there). */
.th-key { display: block; font-weight: 400; text-wrap: balance; }
.sign-key { display: none; }
.cell-input { width: 100%; min-height: 36px; padding: 6px 7px; border: 1px solid transparent; border-radius: 0; background: transparent; color: var(--ink); font: inherit; font-size: var(--text-body); }
/* At rest, the tape's dashed fill-in line marks a blank to fill in only
   where fixing happens: the amounts, and every cell of a flagged row. The
   row you're on (hovered, or with focus inside) shows all its lines; a
   hovered cell is a field; focus is solid. Every cell is also in the row
   menu ("Edit date", "Edit description"). */
.tx-table .cell-input.amount, .tx-table tr.is-flagged .cell-input, .tx-table tr:focus-within .cell-input { border-bottom: 1px dashed var(--dash); }
.tx-table tr.is-flagged { --dash: var(--dash-flag); }
@media (hover: hover) {
  .tx-table tbody tr:hover .cell-input { border-bottom: 1px dashed var(--dash); }
  .tx-table tbody tr .cell-input:hover { border: 1px solid var(--control); border-radius: var(--radius); }
}
.tx-table tr .cell-input:focus, .cell-input:focus { outline: 2px solid var(--focus); outline-offset: -1px; border: 1px solid var(--focus); border-radius: var(--radius); background: var(--paper); }
.tx-table tr .cell-input[aria-invalid="true"] { border-color: transparent; }
.cell-input.amount { font-variant-numeric: tabular-nums; text-align: right; }
/* Money out is printed in the ribbon's red, as the tape prints it. */
.cell-input.amount.is-out { color: var(--red); }
.tx-table .cell-input[aria-invalid="true"] { color: var(--ink); }
.cell-error { margin: 2px 0 6px; font-size: var(--text-caption); line-height: 1.3; justify-content: flex-end; text-align: right; }
.col-date .cell-error { justify-content: flex-start; text-align: left; }
/* Descriptions wrap onto as many lines as they need (300 characters at
   most), so no text is ever hidden. JS sizes them where field-sizing isn't
   supported. */
.cell-desc { display: block; resize: none; overflow: hidden; line-height: 22px; field-sizing: content; overflow-wrap: anywhere; }
/* The date is text in the review's date style; the calendar is a button at
   the field's end, shown on the row you're on, not in every row. The native
   date input behind it is never seen, only its picker. */
.date-cell { position: relative; }
.date-cell .cell-date { padding-right: 32px; }
.date-pick { position: absolute; top: 50%; right: 2px; width: 28px; height: 28px; transform: translateY(-50%); visibility: hidden; }
.tx-table tr:focus-within .date-pick { visibility: visible; }
@media (hover: hover) { .tx-table tr:hover .date-pick { visibility: visible; } }
.date-native { position: absolute; right: 0; bottom: 0; width: 1px; height: 1px; padding: 0; border: 0; opacity: 0; pointer-events: none; }
/* What a cell was first read as, once edited: the audit trail, quietly. */
.cell-was { margin: 2px 8px 4px; color: var(--ink-2); font-size: var(--text-caption); line-height: 1.3; }
.cell-was .link-btn { color: var(--ink); }
.col-amount .cell-was { text-align: right; }
/* Row actions: with a mouse they appear on the row you're on (and always on
   flagged rows); they stay focusable, and focus shows them. Touch screens
   always show them. */
@media (hover: hover) and (pointer: fine) {
  .tx-table tbody tr:not(.is-flagged):not(.is-shown) .col-actions .icon-btn { opacity: 0; transition: opacity 0.12s; }
  .tx-table tbody tr:hover .col-actions .icon-btn,
  .tx-table tbody tr:focus-within .col-actions .icon-btn { opacity: 1 !important; }
}
.flag { display: inline-flex; color: var(--ink-2); }
.flag[title] { cursor: help; }
.flag.is-look { color: var(--ink); }
.flag .icon { width: 17px; height: 17px; }
.flag-note { margin: 0 var(--s2) var(--s2); color: var(--ink); font-size: var(--text-footnote); line-height: 1.35; }
/* Why a row's date or amount isn't the one printed on its line; nothing to fix, so quieter. */
.flag-note.is-info { color: var(--ink-2); }
/* On the flagged row: the fix its balance implies, and the page. */
.flag-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s2); margin: calc(-1 * var(--s1)) var(--s2) var(--s2); }
.flag-actions .btn-plain { padding: 0 var(--s2); }
.add-row { margin: var(--s2); }
.show-more { display: flex; margin: var(--s3) auto var(--s1); }

.layout-preview { max-height: 220px; overflow: auto; margin: var(--s2) 0 0; padding: var(--s3); border: 1px solid var(--rule); border-radius: var(--radius); background: var(--ground); font: var(--text-caption)/1.5 var(--font-mono); white-space: pre; }
.plain-list { margin: var(--s2) 0 var(--s3); padding-left: var(--s5); }
.plain-list li { margin: var(--s1) 0; }

/* Page view: the PDF's text redrawn at its positions, in a sheet or docked. */
dialog.sheet.sheet-pages { width: min(980px, calc(100vw - 32px)); }
.page-bar { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); margin-bottom: var(--s3); }
.page-count { min-width: 92px; font-size: var(--text-footnote); font-weight: 600; text-align: center; }
.page-caption { max-width: 60%; margin: 0; overflow: hidden; text-align: right; text-overflow: ellipsis; white-space: nowrap; }
.page-clipped { flex-basis: 100%; }
.page-frame { max-height: min(68vh, 760px); overflow: auto; padding: var(--s3); border: 1px solid var(--rule); border-radius: var(--radius); background: var(--ground); scrollbar-color: var(--control) transparent; }
.page-svg { display: block; width: 100%; height: auto; outline: 1px solid var(--tape-edge); }
/* Larger than the frame: the frame scrolls both ways. */
.page-frame[data-zoom="150"] .page-svg { width: 150%; }
.page-frame[data-zoom="200"] .page-svg { width: 200%; }
.page-zoom { margin-left: var(--s2); }
.page-paper { fill: var(--paper); }
.page-text { fill: var(--ink); font-family: var(--font); white-space: pre; }
.page-mark { fill: var(--mark-fill); stroke: var(--mark-edge); stroke-width: 0.8; }
/* Narrow: the caption becomes a second line under the page buttons. */
@media (max-width: 640px) {
  .page-bar .spacer { display: none; }
  .page-caption { flex-basis: 100%; max-width: none; text-align: left; white-space: normal; }
  .page-frame { padding: var(--s2); }
}

/* Wide windows: the page view docks beside the table instead of covering it.
   Not under 1440 px: beside it, descriptions wrapped to 3–4 lines. */
.page-dock { display: none; }
@media (min-width: 1440px) {
  .is-docked .topbar-inner, .is-docked .main { --page: 1632px; }
  .workspace.has-dock { display: grid; grid-template-columns: minmax(0, 1fr) max(380px, min(44%, calc(100% - 700px))); gap: var(--s6); align-items: start; }
  .page-dock {
    position: sticky; top: calc(var(--bar-h) + var(--s4)); display: flex; flex-direction: column; max-height: calc(100vh - var(--bar-h) - var(--s8));
    padding: var(--s3) var(--s4); border: 1px solid var(--tape-edge); border-radius: var(--radius); background: var(--paper);
  }
  .dock-head { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s2); }
  .dock-title { flex: 1; min-width: 0; overflow: hidden; font: 650 var(--text-headline)/1.25 var(--font-display); text-overflow: ellipsis; white-space: nowrap; }
  .page-dock .page-bar { margin-bottom: var(--s2); }
  .page-dock .page-caption { flex-basis: 100%; max-width: none; text-align: left; white-space: normal; }
  .page-dock .page-frame { flex: 1; min-height: 0; max-height: none; padding: var(--s2); }
  .page-dock .sheet-note { margin-top: var(--s2); }
}

/* ------------------------------------------------------------ check a conversion */

/* The welcome's second way in: one quiet line under the two buttons. Inline
   in a sentence, so not padded out to a touch target (WCAG 2.5.8's inline exception). */
.check-path { max-width: 34em; margin-top: var(--s4); color: var(--ink-2); line-height: 1.45; }
.check-path .link-btn { min-height: 0; color: var(--ink); font-weight: 600; }
.is-dragging.is-checking::after { content: "Drop the PDF and the file to check"; }
/* "Back to the statement": a way out above the title, quieter than it. */
.compare-back { margin-bottom: var(--s2); }
.compare-back .link-btn { display: inline-flex; align-items: center; gap: var(--s1); min-height: 32px; color: var(--ink-2); font-size: var(--text-footnote); text-decoration: none; }
@media (hover: hover) { .compare-back .link-btn:hover { color: var(--ink); text-decoration: underline; } }
@media (pointer: coarse) { .compare-back .link-btn { min-height: 44px; } }
.compare .review-title:focus { outline: none; }
.compare .review-title:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; }
/* Under the verdict, the kinds, counted, each a link to its group. */
.cmp-kinds { margin-top: var(--s2); color: var(--ink-2); line-height: 1.6; }
.cmp-kinds a { color: var(--ink); font-weight: 500; }
.cmp-file { display: inline-flex; align-items: center; gap: var(--s1); min-width: 0; }
.cmp-file .icon { color: var(--ink-2); }
.review-meta > .cmp-against { white-space: normal; }
.review-meta .link-btn { color: var(--ink); }
/* One table, one header; each kind is a group led by its own heading row. */
.cmp-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.cmp-table thead th { padding: var(--s2) var(--s3); border-bottom: 1px solid var(--control); color: var(--ink-2); font-size: var(--text-caption); font-weight: 600; text-align: left; vertical-align: bottom; }
.cmp-table td { padding: var(--s2) var(--s3); border-bottom: 1px solid var(--rule); vertical-align: top; }
.cmp-kind th { padding: var(--s6) var(--s3) var(--s2); border-bottom: 1px solid var(--rule-strong); text-align: left; font-weight: 400; scroll-margin-top: calc(var(--bar-h) + var(--s4)); }
.cmp-kind-body:first-of-type .cmp-kind th { padding-top: var(--s3); }
.cmp-kind-title { font: 650 var(--text-headline)/1.3 var(--font-display); }
.cmp-count { margin-left: var(--s2); color: var(--ink-2); font-weight: 500; }
.cmp-kind-note { display: block; margin-top: 2px; color: var(--ink-2); font-size: var(--text-footnote); }
.cmp-table tbody:last-child tr:last-child td { border-bottom: 0; }
.cmp-table .cmp-date { width: 84px; white-space: nowrap; }
.cmp-table .cmp-fig { width: 136px; text-align: right; }
.cmp-table .cmp-go { width: 212px; }
.cmp-table th.cmp-fig .th-key { font-weight: 400; }
.cmp-desc-text { display: block; overflow-wrap: anywhere; }
.cmp-where { display: block; margin-top: 2px; color: var(--ink-2); font-size: var(--text-footnote); line-height: 1.35; overflow-wrap: anywhere; }
.cmp-why { display: block; color: var(--ink-2); font-size: var(--text-footnote); }
.cmp-none { color: var(--ink-2); }
.cmp-fig .is-red { color: var(--red); }
.cmp-label { display: none; }
/* The value that's wrong in the file, marked with the bookkeeper's highlighter. */
.compare.is-reference .cmp-mark { margin-right: -2px; padding: 0 2px; border-radius: var(--radius); background: var(--highlight); }
/* Where to see a row: two buttons of one style, stacked on one edge. */
.cmp-table td.cmp-go .btn { display: flex; width: max-content; margin: -4px 0 0 calc(-1 * var(--s3)); padding: 0 var(--s3); }
.cmp-table td.cmp-go .btn + .btn { margin-top: 2px; }
.cmp-more td { text-align: center; }
.cmp-match { display: flex; align-items: center; gap: var(--s2); font-weight: 500; }
.cmp-match .icon { flex-shrink: 0; }
/* Narrow review columns: each difference is a short entry, its figures labelled. */
@container review (width < 640px) {
  .cmp-table, .cmp-table tbody { display: block; }
  .cmp-table thead { display: none; }
  .cmp-kind, .cmp-kind th { display: block; }
  .cmp-kind th { padding: var(--s5) var(--s3) var(--s2); }
  .cmp-table tr:not(.cmp-kind):not(.cmp-more) { display: grid; grid-template-columns: 64px minmax(0, 1fr) minmax(0, 1fr); grid-template-areas: "date desc desc" "date stmt theirs" "go go go"; column-gap: var(--s3); row-gap: var(--s1); padding: var(--s3); border-bottom: 1px solid var(--rule); }
  .cmp-table td { display: block; width: auto !important; padding: 0; border: 0; }
  .cmp-table .cmp-date { grid-area: date; color: var(--ink-2); font-size: var(--text-footnote); line-height: 22px; white-space: normal; }
  .cmp-table .cmp-desc { grid-area: desc; }
  .cmp-table .cmp-fig { grid-area: stmt; text-align: left; }
  .cmp-table .cmp-fig + .cmp-fig { grid-area: theirs; }
  .cmp-label { display: block; color: var(--ink-2); font-size: var(--text-caption); }
  .cmp-table td.cmp-go { grid-area: go; display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--s2); }
  .cmp-table td.cmp-go:empty { display: none; }
  .cmp-table td.cmp-go .btn, .cmp-table td.cmp-go .btn + .btn { margin: 0 0 0 calc(-1 * var(--s3)); }
}

/* Which column is which: one line per column, its first values, and a choice. */
.map-lede { max-width: 40em; margin-bottom: var(--s2); }
.map-rule { max-width: 40em; margin-bottom: var(--s3); color: var(--ink-2); font-size: var(--text-footnote); }
.map-header { display: inline-flex; align-items: center; gap: var(--s2); min-height: 40px; margin-bottom: var(--s2); font-weight: 500; cursor: pointer; }
.map-header input { width: 16px; height: 16px; margin: 0; accent-color: var(--ink); }
.map-list { max-width: 760px; margin: 0; padding: 0; list-style: none; border: 1px solid var(--tape-edge); border-radius: var(--radius); background: var(--paper); }
.map-col { display: grid; grid-template-columns: minmax(0, 1fr) 260px; align-items: center; gap: var(--s2) var(--s4); padding: var(--s3) var(--s4); }
.map-col + .map-col { border-top: 1px solid var(--rule); }
.map-col-text { display: grid; gap: 2px; min-width: 0; cursor: pointer; }
.map-col-name { font-weight: 600; overflow-wrap: anywhere; }
.map-col-samples { overflow: hidden; color: var(--ink-2); font-size: var(--text-footnote); text-overflow: ellipsis; white-space: nowrap; }
.compare-map > .field-error { margin-top: var(--s3); }
/* Below the list on narrow columns, the file's sums start after a break. */
@container review (width < 1040px) {
  .compare .review-rows + .check { margin-top: var(--s6); padding-top: var(--s6); border-top: 1px solid var(--rule); }
}
@container review (width < 560px) {
  .map-col { grid-template-columns: minmax(0, 1fr); }
}

/* The welcome's check: the statement PDF and the file to check, each a blank to fill. */
.setup { max-width: 760px; padding-top: var(--s8); }
.setup-lede { max-width: 40em; margin-top: var(--s3); color: var(--ink-2); font-size: var(--text-headline); line-height: 1.45; }
.setup-slots { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s4); margin-top: var(--s8); }
.setup-slot { display: grid; align-content: start; gap: var(--s3); min-height: 140px; padding: var(--s5); border: 1px dashed var(--control); border-radius: var(--radius); background: var(--paper); }
.setup-slot.is-filled { border-style: solid; border-color: var(--tape-edge); }
.setup-label { font-weight: 600; }
.setup-hint { color: var(--ink-2); font-size: var(--text-footnote); line-height: 1.4; }
.setup-file { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
.setup-actions { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: auto; }
.setup .drop-hint { margin-top: var(--s4); }

/* ------------------------------------------------------------ the year */

/* The welcome's other ways in, one under the other. */
.check-path + .check-path { margin-top: var(--s2); }
/* With a year on screen, or more than six files, the one control at every width (rows of tabs would push the verdict down). */
.file-strip.menu-always .file-list { display: none; }
.file-strip.menu-always .file-menu { display: flex; max-width: 34em; }
.file-strip.menu-always .file-actions { border-bottom: 0; }
/* "Year" on the strip comes first, set apart from the statements after it. */
.file-list > .file-tab.is-year:not(:has(+ .file-tab.is-year)) { margin-right: var(--s4); }

.year-verdict { max-width: 52em; margin-bottom: var(--s6); }
/* Headings focused when a view opens (for screen readers) aren't controls: no ring. */
h1[tabindex="-1"]:focus { outline: none; }
.year-verdict .check-status { display: grid; gap: var(--s1); }
/* What isn't read from the PDFs as given, under the verdict: typed, worked out, taken, saved again, marked not a problem. */
.check-note { max-width: 46em; color: var(--ink); }
/* Focused controls clear the top bar and the bar at the foot; the reason's buttons and a toast clear both too. */
html:has(.review.year) { scroll-padding: calc(var(--bar-h) + var(--s4)) 0 96px; }
.finding-form .input { scroll-margin-bottom: 96px; }
/* Scrolled into view when it opens: its buttons clear the bar and a toast above it. */
.finding-form { scroll-margin-bottom: 48px; }
.year-accounts { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) var(--s3); margin-top: var(--s2); color: var(--ink-2); font-size: var(--text-footnote); }
.year-accounts .link-btn { min-height: 0; color: var(--ink); font-weight: 600; }
.year-frame { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s2); margin: 0 0 var(--s8); }
/* One line of help under the control, the width of the row. */
.year-frame-hint { flex: 1 1 100%; margin: 0; }
.month-strip:has(+ .year-frame) { margin-bottom: var(--s3); }
@container review (width < 560px) { .year-frame { margin-bottom: var(--s6); } }
.year-frame .field-label { margin: 0; }
.year-frame-select { flex: 0 1 auto; width: auto; min-width: 0; min-height: 36px; padding-block: var(--s1); font-size: var(--text-footnote); }
@media (pointer: coarse) { .year-frame-select { min-height: 44px; font-size: var(--text-headline); } }

/* The month strip: one ruled strip, like a ledger's row of columns, a cell a
   month. The rule two cells share is their join: ink where the month
   carries forward, dashed where it can't be proven, cut where it doesn't
   (the difference printed across the cut on the highlighter); a missing
   month is a dashed cell. A row of the strip starts flush, with its year. */
.month-strip { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); margin: 0 0 var(--s8); padding: 0; list-style: none; border-top: 1px solid var(--rule-strong); background: var(--paper); }
.month { --join: var(--rule); --join-style: solid; position: relative; display: grid; grid-template-rows: auto auto auto 1fr; align-content: start; gap: var(--s1); min-width: 0; min-height: 96px; padding: var(--s2) var(--s2) var(--s2) var(--s3); border-bottom: 1px solid var(--rule-strong); }
/* The shared rule, on the cell's left edge; a row's first cell has none. */
.month::before { content: ""; position: absolute; top: 0; bottom: 0; left: 0; border-left: 1px var(--join-style) var(--join); }
.month.join-carried { --join: var(--ink); }
.month.join-unknown { --join: var(--ink-2); --join-style: dashed; }
.month.join-broken { --join: transparent; }
.month-name { font: 600 var(--text-footnote)/1.2 var(--font); }
.month-year { display: none; margin-left: var(--s1); color: var(--ink-2); font-weight: 400; }
.month-state { display: flex; align-items: flex-start; gap: var(--s1); color: var(--ink-2); font-size: var(--text-caption); line-height: 1.3; }
.month-icon { flex: none; width: 14px; height: 14px; margin-top: 1px; color: var(--ink); stroke-width: 2.2; }
.month-note { color: var(--ink-2); font-size: var(--text-caption); line-height: 1.3; }
.month-fig { align-self: end; font-size: var(--text-footnote); text-align: right; white-space: nowrap; }
.month-fig.is-red { color: var(--red); }
.month-files { display: grid; min-width: 0; }
.month-file { overflow: hidden; color: var(--ink-2); font-size: var(--text-caption); text-overflow: ellipsis; white-space: nowrap; }
/* The one thing to look at takes the highlighter: a month missing, or one that doesn't carry over. */
.month.is-missing, .month.is-break, .month.is-gap, .month.is-off { background: var(--highlight); }
.month:is(.is-missing, .is-break, .is-gap, .is-off) .month-words { color: var(--ink); font-weight: 600; }
/* Missing: an empty cell with a dashed edge and the words, no hatching. */
.month.is-missing { outline: 1px dashed var(--control); outline-offset: -1px; }
.month.is-within { outline: 1px dashed var(--rule-strong); outline-offset: -1px; }
.month.is-unknown .month-icon { color: var(--ink-2); }
/* A break's difference, printed across the cut rule (a band above the names makes room for it). */
.month-strip:has(.month-join) .month { padding-top: 28px; }
.month-join { position: absolute; top: 4px; left: 0; z-index: 1; padding: 1px var(--s1); border: 1px solid var(--ink); border-radius: var(--radius); background: var(--paper); color: var(--ink); font: 650 var(--text-caption)/1.25 var(--font); white-space: nowrap; transform: translateX(-50%); }
.month-join::before { content: ""; position: absolute; inset: 0; z-index: -1; background: var(--highlight); }
.month-strip.has-years .month.is-jan .month-year { display: inline; }
/* Twelve across: a row starts at 1, 13, 25… */
.month:nth-child(12n + 1)::before, .month:nth-child(12n + 1) .month-join { display: none; }
.month-strip.has-years .month:nth-child(12n + 1) .month-year { display: inline; }
@container review (width < 1180px) {
  .month-strip { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .month:nth-child(6n + 1)::before, .month:nth-child(6n + 1) .month-join { display: none; }
  .month-strip.has-years .month:nth-child(6n + 1) .month-year { display: inline; }
}
/* Phones: 4 × 3, every state kept. The verdict's sum steps aside (the tape
   prints it, and the live region says it), as the account does (the control
   above names it), so the first finding's action is near the first screen. */
@container review (width < 560px) {
  .year-verdict { margin-bottom: var(--s4); }
  .year-verdict .check-sentence:not(.is-gap) { display: none; }
  .review-note.year-note { display: block; }
  .month-strip { grid-template-columns: repeat(4, minmax(0, 1fr)); margin-bottom: var(--s6); }
  .month { min-height: 84px; padding: var(--s1) 6px var(--s2) var(--s2); }
  .month-strip:has(.month-join) .month { padding-top: 26px; }
  .month:nth-child(6n + 1)::before, .month:nth-child(6n + 1) .month-join { display: block; }
  .month-strip.has-years .month:nth-child(6n + 1):not(:nth-child(4n + 1)):not(.is-jan) .month-year { display: none; }
  .month:nth-child(4n + 1)::before, .month:nth-child(4n + 1) .month-join { display: none; }
  .month-strip.has-years :is(.month:nth-child(4n + 1), .month.is-jan) .month-year { display: block; }
  .month-year { margin-left: 0; }
  .month-fig { font-size: var(--text-caption); }
}

/* The tape beside the findings and statements on wide columns; under the strip on narrow ones. */
.year-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s8); }
.year-body > section { min-width: 0; }
.year-h2 { margin-bottom: var(--s3); font: 650 var(--text-headline)/1.3 var(--font-display); }
.year-count { color: var(--ink-2); font-weight: 400; }
.year-rail .tape { max-width: 420px; }
@container review (width >= 1040px) {
  .year-body { grid-template-columns: minmax(0, 1fr) var(--rail); grid-template-rows: auto 1fr; column-gap: var(--s8); align-items: start; }
  .year-findings, .year-statements { grid-column: 1; }
  .year-body.no-findings .year-statements { grid-row: 1 / span 2; }
  .year-rail { grid-column: 2; grid-row: 1 / span 2; position: sticky; top: calc(var(--bar-h) + var(--s4)); }
  .year-rail .tape-lines { padding-inline: var(--s3); }
}
/* Under the difference, what explains it, a line each, to "Unexplained" under a double rule. */
.year-tape .tape-line.is-first-why { margin-top: var(--s2); padding-top: var(--s2); border-top: 1px dashed var(--tape-edge); }
.year-tape .tape-line.is-final { margin-top: var(--s2); padding-top: var(--s2); padding-bottom: var(--s1); border-top: 1px solid var(--ink); border-bottom: 3px double var(--ink); }
.year-tape .tape-why .tape-label { padding-block: var(--s1); }
.tape-ring { width: 16px; height: 16px; color: var(--ink-2); stroke-width: 2; stroke-dasharray: 3 3.4; vertical-align: -2px; }

/* Findings: a sentence and one action each, ruled like the rows. */
.finding-list { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--rule); }
.finding { display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: var(--s3); padding: var(--s4) var(--s3); border-bottom: 1px solid var(--rule); }
/* The open problem's title takes the highlighter on its words, as a pen marks a line; never a panel round the finding. */
.finding.is-marked .finding-title-text { margin-inline: -2px; padding-inline: 2px; background: var(--highlight); -webkit-box-decoration-break: clone; box-decoration-break: clone; }
.finding-icon { width: 20px; height: 20px; margin-top: 1px; color: var(--ink); stroke-width: 2; }
.finding.is-info .finding-icon { color: var(--ink-2); }
.finding-title { font-weight: 600; overflow-wrap: anywhere; }
.finding-text { max-width: 60em; margin-top: 2px; color: var(--ink-2); overflow-wrap: anywhere; }
.finding-actions { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s3); }
.finding.is-dismissed .finding-title { color: var(--ink-2); }
.finding-next { max-width: 60em; margin-top: var(--s2); color: var(--ink); font-weight: 500; }
/* The drafted request, shown where it's offered: an email's text, quoted, to read or select. */
.request-draft { max-width: 36em; margin-top: var(--s2); padding: var(--s1) 0 var(--s1) var(--s3); border-left: 1px solid var(--rule-strong); color: var(--ink); font-size: var(--text-footnote); white-space: pre-wrap; user-select: text; }
.finding-reason { margin-top: var(--s2); color: var(--ink); font-weight: 500; }
.finding-reason .link-btn { min-height: 0; }
.finding-form { display: grid; gap: var(--s1); max-width: 36em; margin-top: var(--s3); }
.finding-form .finding-actions { margin-top: var(--s2); }

/* The statements: one ruled entry each; two lines on phones, the state kept. */
.year-files { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--rule); }
.year-file { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1.3fr) minmax(0, 1fr) 4.5em 7em; grid-template-areas: "name period state pages sha" "notes notes notes notes notes"; align-items: baseline; gap: var(--s1) var(--s4); padding: var(--s3) var(--s2); border-bottom: 1px solid var(--rule); font-size: var(--text-footnote); }
/* The state's words sit on the row's baseline; its shape is centred on them. */
.year-file-state { grid-area: state; display: flex; align-items: baseline; gap: var(--s1); min-width: 0; }
.year-file-state .icon { flex: none; align-self: center; }
.year-file-notes { grid-area: notes; color: var(--ink-2); font-size: var(--text-caption); }
.year-file-name { grid-area: name; justify-self: start; min-width: 0; font-weight: 600; text-align: start; overflow-wrap: anywhere; }
.year-file-period { grid-area: period; color: var(--ink-2); }
.year-file-pages { grid-area: pages; color: var(--ink-2); }
.year-file-sha { grid-area: sha; color: var(--ink-2); font-family: var(--font-mono); font-size: var(--text-caption); }
.year-files.is-pack .year-file { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1.6fr) 7em; grid-template-areas: "name state sha"; }
@container review (width < 760px) {
  .year-file { grid-template-columns: minmax(0, 1fr) 4em 6.5em; grid-template-areas: "name state state" "period pages sha" "notes notes notes"; column-gap: var(--s3); padding-inline: 0; }
  .year-file-state { justify-self: end; max-width: 16em; text-align: right; }
}
.pack .year-files { margin-top: var(--s4); }
.year-tape-note { max-width: 420px; margin-top: var(--s2); }
/* How the files match the pack: a line under Rowsmith's own verdict, with its own shape. */
.pack-match { display: flex; align-items: baseline; gap: var(--s2); max-width: 46em; margin-top: var(--s1); color: var(--ink); }
.pack-match .icon { flex: none; align-self: center; }
/* What the pack itself says: the sender's words, quoted, quieter than the check, never with a tick. */
.pack-said { display: grid; gap: var(--s1); max-width: 52em; margin-top: var(--s4); padding-top: var(--s3); border-top: 1px solid var(--rule); }
.pack-said-title { color: var(--ink-2); }
.pack-said-differs { color: var(--ink); font-weight: 600; }
.pack-said-reason { color: var(--ink-2); }
.pack-figures { margin: 0; padding-left: var(--s5); color: var(--ink-2); font-size: var(--text-footnote); overflow-wrap: anywhere; }
/* How it's matched: quieter than the result it explains. */
.pack-how { max-width: 46em; color: var(--ink-2); font-size: var(--text-footnote); }
.pack-source { display: flex; align-items: center; gap: var(--s1); margin-top: var(--s2); }
.pack .setup-actions { margin-top: var(--s4); }
.pack .year-verdict { margin: var(--s4) 0 0; }
.pack-title { font-size: var(--text-title2); }
@media (max-width: 560px) {
  .pack .year-files.is-pack .year-file { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name name" "state sha"; padding-inline: 0; }
}
.request-text { font-family: var(--font); font-size: var(--text-body); }
/* Two statements' pages in one sheet. */
.pair-page + .pair-page { margin-top: var(--s6); }
.pair-after { max-width: 60em; margin-top: var(--s4); }
.pair-title { margin-bottom: var(--s2); font: 600 var(--text-body)/1.3 var(--font); }
.sheet-pair .page-frame { max-height: min(30vh, 340px); }

/* The Year view's bar keeps to one row under 1,040 px: its note steps aside. */
@container review (width < 1040px) { .year .export-bar > p { display: none; } }
/* Sticky at the bottom; rows pass under its hairline. */
.export-bar {
  position: sticky; bottom: 0; z-index: 5; display: flex; align-items: center; gap: var(--s3);
  margin: var(--s6) 0 0; padding: var(--s3) 0 calc(var(--s3) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--rule); background: var(--ground);
}

/* ------------------------------------------------------------ export sheet */

.export-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
/* One list, not a stack of boxes: one line per format, hairlines between
   them; the selected one takes a 2 px ink edge and adds its description. */
.format-list { display: grid; border: 1px solid var(--control); border-radius: var(--radius); background: var(--paper); }
.format { display: flex; align-items: center; gap: var(--s3); min-height: 44px; padding: var(--s2) var(--s3); cursor: pointer; }
.format + .format { border-top: 1px solid var(--rule); }
.format:first-child { border-radius: var(--radius) var(--radius) 0 0; }
.format:last-child { border-radius: 0 0 var(--radius) var(--radius); }
.format.is-selected { position: relative; outline: 2px solid var(--ink); outline-offset: -1px; }
@media (hover: hover) { .format:not(.is-selected):hover { background: var(--hover); } }
.format input { width: 16px; height: 16px; margin: 0; accent-color: var(--ink); }
/* Not for several statements at once: listed, with the reason, not hidden. */
.format.is-disabled { cursor: default; }
.format.is-disabled .format-name, .format.is-disabled .format-ext { color: var(--ink-2); font-weight: 400; }
@media (hover: hover) { .format.is-disabled:hover { background: transparent !important; } }
.export-one-note { margin-top: calc(-1 * var(--s1)); color: var(--ink-2); line-height: 1.4; }
/* Inline in a sentence, so not padded out to a touch target (WCAG 2.5.8's inline exception). */
.export-one-note .link-btn { min-height: 0; color: var(--ink); text-align: start; }
.format-text { display: grid; flex: 1; min-width: 0; }
.format-name { font-weight: 600; }
.format-hint { color: var(--ink-2); font-size: var(--text-footnote); }
.format-ext { color: var(--ink-2); font-size: var(--text-footnote); }
.export-options { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s3); }
.export-options .field-wide { grid-column: 1 / -1; }
.notice { display: flex; align-items: flex-start; gap: var(--s3); font-size: var(--text-footnote); }
.notice > .icon { margin-top: 1px; color: var(--ink-2); }
.notice > p { flex: 1; min-width: 0; }
.notice > .btn { flex-shrink: 0; align-self: center; }
/* Needs a look: the highlighter, with the triangle. */
.notice.is-warn { padding: var(--s3); border-radius: var(--radius); background: var(--highlight); }
.notice.is-warn > .icon { color: var(--ink); }
.export-output { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s2); }
.export-output-title { color: var(--ink-2); font-size: var(--text-footnote); font-weight: 600; }
.export-files { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s1); margin: 0; padding: 0; list-style: none; }
.export-files li { display: flex; align-items: center; gap: var(--s2); }
.export-files .icon { color: var(--ink-2); }
.export-name { flex: 1; min-width: 0; overflow: hidden; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.export-name.cut { display: flex; }
.export-rows { color: var(--ink-2); white-space: nowrap; }
/* The first rows of the file, as the spreadsheet will show them. */
.export-preview { width: 100%; border-collapse: collapse; table-layout: fixed; border: 1px solid var(--rule-strong); font-size: var(--text-caption); }
.export-preview th, .export-preview td { padding: 6px var(--s2); overflow: hidden; border-bottom: 1px solid var(--rule); text-align: left; text-overflow: ellipsis; white-space: nowrap; }
.export-preview th { background: var(--ground); color: var(--ink); font-weight: 600; }
.export-preview tr:last-child td { border-bottom: 0; }
.export-preview .is-num { text-align: right; }
.export-preview col.col-date { width: 92px; }
.export-preview col.col-num { width: 84px; }
.prose p + p, .prose p + h3, .prose ul + p { margin-top: var(--s3); }
.prose code { padding: 1px var(--s1); border-radius: var(--radius); background: var(--ground); font: var(--text-footnote) var(--font-mono); white-space: nowrap; }
.prose h3 { margin-top: var(--s4); font-size: var(--text-body); }
.prose-lead { font-size: var(--text-headline); font-weight: 600; line-height: 1.4; }
.prose-more { margin-top: var(--s4); border-top: 1px solid var(--rule); }
.prose-more > summary { display: flex; align-items: center; min-height: 44px; font-weight: 600; cursor: pointer; }
.prose-more > summary::marker { content: ""; }
.prose-more > summary::-webkit-details-marker { display: none; }
.prose-more > summary::after { content: ""; width: 8px; height: 8px; margin-left: var(--s2); border: solid var(--ink-2); border-width: 0 1.5px 1.5px 0; transform: translateY(-2px) rotate(45deg); }
.prose-more[open] > summary::after { transform: translateY(2px) rotate(-135deg); }
.prose-more > p { margin-top: 0; }
.prose ul { margin: var(--s2) 0 0; padding-left: var(--s5); }
.prose li + li { margin-top: var(--s1); }

/* sheets (native dialog): paper on a dimmed page, edged rather than shadowed */
dialog.sheet { width: min(560px, calc(100vw - 32px)); max-height: min(90vh, 860px); padding: 0; border: 1px solid var(--tape-edge); border-radius: var(--radius); background: var(--sheet); color: var(--ink); }
dialog.sheet[open] { display: flex; flex-direction: column; }
@media (prefers-reduced-motion: no-preference) { dialog.sheet[open] { animation: sheet-in 0.22s var(--ease); } }
dialog.sheet::backdrop { background: var(--backdrop); }
.sheet-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding: var(--s4) var(--s5) var(--s2); }
.sheet-title { font: 650 var(--text-title3)/1.25 var(--font-display); letter-spacing: -0.01em; }
.sheet-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s3); padding: var(--s2) var(--s5) var(--s4); overflow: auto; }
.sheet-foot { display: flex; align-items: center; gap: var(--s2); padding: var(--s3) var(--s5) var(--s4); border-top: 1px solid var(--rule); }
.sheet-foot:empty { display: none; }
.sheet-note { flex: 1; min-width: 0; }
/* Phones: the note takes the foot's whole width and the buttons wrap under it (it was a one-word column behind them). */
@media (max-width: 560px) {
  .sheet-foot { flex-wrap: wrap; }
  .sheet-foot .sheet-note { flex: 1 1 100%; }
}
@keyframes sheet-in { from { transform: translateY(8px); opacity: 0; } }

/* "Row actions" on touch screens: the actions by name, one per line. */
.action-list { display: grid; gap: var(--s2); }
.action-list .btn { justify-content: flex-start; min-height: 44px; }
.action-rule { width: 100%; margin: var(--s1) 0; border: 0; border-top: 1px solid var(--rule); }

/* "Row actions" with a mouse: a small menu by the row's button, edged like
   the sheets (no shadow), above everything (a popover in the top layer). */
.menu { position: fixed; inset: auto; min-width: 232px; margin: 0; padding: var(--s1); border: 1px solid var(--control); border-radius: var(--radius); background: var(--sheet); color: var(--ink); }
.menu-item { display: flex; align-items: center; gap: var(--s2); width: 100%; min-height: 34px; padding: 0 var(--s3) 0 var(--s2); border: 0; border-radius: var(--radius); background: transparent; color: var(--ink); font: 500 var(--text-body)/1.2 var(--font); text-align: left; cursor: pointer; }
.menu-item .icon { color: var(--ink-2); }
.menu-item:focus { outline: none; }
.menu-item:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.menu-item:focus, .menu-item:hover { background: var(--hover); }
.menu-rule { margin: var(--s1) var(--s2); border-top: 1px solid var(--rule); }

/* toasts: ink slips */
.toasts { position: fixed; left: 50%; bottom: 88px; z-index: 100; display: grid; gap: var(--s2); width: max-content; max-width: calc(100vw - 32px); transform: translateX(-50%); }
.toast { display: flex; align-items: center; gap: var(--s3); padding: var(--s2) var(--s2) var(--s2) var(--s4); border-radius: var(--radius); background: var(--ink); color: var(--on-ink); font-size: var(--text-body); }
.toast.is-error::before { content: ""; flex-shrink: 0; width: 16px; height: 16px; margin-left: calc(-1 * var(--s1)); background: currentColor; -webkit-mask: var(--icon-error) center / contain no-repeat; mask: var(--icon-error) center / contain no-repeat; }
.toast button { min-height: 32px; padding: 0 var(--s3); border: 1px solid currentColor; border-radius: var(--radius); background: transparent; color: inherit; font: 600 var(--text-footnote)/1 var(--font); cursor: pointer; }

/* ------------------------------------------------------------ touch: 44 px targets */

@media (pointer: coarse) {
  .btn, .btn-small, .icon-btn, .segmented button, .file-tab, .link-btn { min-height: 44px; }
  .file-strip { --tab-h: 44px; }
  /* "Change" on the meta line keeps a finger's target without making the
     line 44 px tall: the target reaches past the text, into the space around it. */
  .review-kind .link-btn { position: relative; min-height: 0; }
  .review-kind .link-btn::after { content: ''; position: absolute; inset: -13px -8px; }
  .icon-btn { min-width: 44px; }
  .tape-input { min-height: 44px; }
  .site-foot a { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ------------------------------------------------------------ small screens */

@media (max-width: 760px) {
  :root { --gutter: 16px; }
  .topbar-inner { gap: var(--s1); padding-right: var(--s2); }
  .topbar .btn-plain { padding: 0 var(--s2); font-size: var(--text-footnote); }
  .topbar-inner > .btn-plain:last-child { margin-right: -1px; }
  .topbar .btn, .topbar .icon-btn { min-width: 44px; min-height: 44px; }
  .welcome { gap: var(--s8); padding-top: var(--s8); }
  .hero h1 { font-size: var(--text-title1); line-height: 1.1; letter-spacing: -0.025em; }
  .lede { margin-top: var(--s3); font-size: var(--text-headline); }
  .welcome-actions { display: grid; grid-template-columns: minmax(0, 1fr); margin-top: var(--s6); }
  /* The tape spans the column, like the buttons above it. */
  .hero-tape { width: 100%; }
  .spec > div, .doc .spec > div { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .doc { padding-top: var(--s8); }
  .doc h1 { font-size: var(--text-title2); }
  .doc h2 { margin-top: var(--s8); }
  /* Each row stands on its own, every cell under its column's name: no sideways scrolling. */
  .doc table, .doc tbody, .doc tr, .doc td { display: block; }
  .doc thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .doc tr { padding: var(--s2) 0; border-bottom: 1px solid var(--rule); }
  .doc td { padding: 2px 0; border: 0; }
  .doc td::before { content: attr(data-label); display: block; color: var(--ink-2); font-size: var(--text-footnote); font-weight: 600; }
  .site-foot-inner { flex-direction: column; gap: var(--s2); }
  .site-foot ul { gap: 0 var(--s5); }
  .workspace { padding-top: var(--s3); }
  .file-strip { --tab-h: 44px; margin-bottom: var(--s3); }
  .file-actions .btn { min-height: 44px; }
  .file-strip.has-menu .file-actions { border-bottom: 0; }
  .file-strip.has-menu .file-list { display: none; }
  .file-strip.has-menu .file-menu { display: flex; }
  .file-strip.has-menu .btn-add { width: 44px; padding: 0; }
  .file-strip.has-menu .file-actions .btn:not(.btn-add) { padding: 0 var(--s3); }
  .file-menu { gap: var(--s1); padding-right: var(--s2); }
  .file-strip.has-menu .btn-add .btn-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .status-card { padding-top: var(--s2); }
  .status-card > p, .row-actions, .password-form { padding-left: 0; }
  .progress { margin-left: 0; }
  .review-head { margin-bottom: var(--s3); }
  .review-title { font-size: var(--text-title2); }
  .review-meta { gap: 0 var(--s3); font-size: var(--text-footnote); }
  .review-meta .tag { margin-right: var(--s1); }
  .review-body { gap: var(--s3); }
  .review-rows { gap: var(--s3); }
  .check { gap: var(--s3); }
  .check-verdict { gap: var(--s2); }
  .seg-long { display: none; }
  .seg-short { display: inline; }
  .check-title { font-size: var(--text-headline); }
  .check-icon { width: 20px; height: 20px; }
  .check-hint, .check-sentence, .check-note { margin-top: 2px; font-size: var(--text-footnote); }
  .check-share:not(.is-open) { display: none; }
  .rows-share { display: block; }
  .check .tape-lines { padding: var(--s1) var(--s3) var(--s2); }
  .check .tape-line { min-height: 26px; }
  /* The print head's dots need the 17 px size to read; only the line is shorter. */
  .export-bar { margin: var(--s4) calc(-1 * var(--gutter)) 0; padding: var(--s2) var(--gutter) calc(var(--s2) + env(safe-area-inset-bottom)); }
  .export-bar p { display: none; }
  .export-bar { flex-wrap: wrap; gap: var(--s2); }
  .export-bar .btn { min-height: 44px; }
  .export-bar .btn-plain { padding: 0 var(--s2); }
  .export-bar .btn-primary { flex: 1; }
  /* Rows become two-line entries on phones; the flag column only where there's a flag. */
  .tx-table, .tx-table tbody, .tx-table tr { display: block; }
  .tx-table thead { display: none; }
  .sign-key { display: block; margin: 0; padding: var(--s2) var(--s3); border-bottom: 1px solid var(--control); color: var(--ink-2); font-size: var(--text-caption); }
  /* Two lines: the description and the amount over the date and the running
     balance (quieter, as a statement prints it); "Row actions" beside both. */
  .tx-table tr { display: grid; grid-template-columns: minmax(0, 1fr) auto 44px; grid-template-areas: "desc amount actions" "date balance actions"; align-items: center; padding: var(--s1) 2px var(--s1) var(--s1); border-bottom: 1px solid var(--rule); }
  .tx-table tbody tr:last-child { border-bottom: 0; }
  .tx-table tr.is-flagged { grid-template-columns: 24px minmax(0, 1fr) auto 44px; grid-template-areas: "flag desc amount actions" "flag date balance actions"; }
  .tx-table td { display: block; width: auto !important; padding: 0; border: 0; }
  .tx-table .col-flag { display: none; }
  .tx-table tr.is-flagged .col-flag { display: block; grid-area: flag; align-self: start; padding-top: var(--s2) !important; }
  /* The description cell lets its parts join the row's grid, so a flag
     note, its fixes and an edit mark take the row's whole width, under the
     entry, instead of a 190 px column. */
  .tx-table .col-desc { display: contents; }
  .tx-table .col-desc > .cell-desc { grid-area: desc; min-width: 0; align-self: start; }
  .tx-table .col-desc > :not(.cell-desc) { grid-column: 1 / -1; }
  .tx-table tr.is-flagged .col-desc > :not(.cell-desc) { grid-column: 2 / -1; }
  /* The date's and amount's own lines (an edit mark with Restore, an error)
     also take the row's width, under the entry, so they never push the
     date and balance line apart. */
  .tx-table .col-amount, .tx-table .col-date { display: contents; }
  .tx-table .col-amount > .cell-input { grid-area: amount; width: 116px; align-self: start; }
  .tx-table .col-date > .date-cell { grid-area: date; }
  .tx-table :is(.col-amount, .col-date) > :not(.cell-input, .date-cell) { grid-column: 1 / -1; justify-content: flex-start; text-align: left; }
  .tx-table tr.is-flagged :is(.col-amount, .col-date) > :not(.cell-input, .date-cell) { grid-column: 2 / -1; }
  .tx-table .col-date .cell-input { min-height: 32px; color: var(--ink-2); font-size: var(--text-footnote); }
  /* A finger's target, like every other control on a touch screen. */
  @media (pointer: coarse) { .tx-table .col-date .cell-input { min-height: 44px; } }
  .tx-table tr.is-flagged .col-date .cell-input { color: var(--ink); }
  .tx-table .col-balance { grid-area: balance; padding: 0 var(--s1) 0 var(--s2) !important; font-size: var(--text-footnote); }
  .tx-table .col-actions { grid-area: actions; }
  .tx-table .col-actions .icon-btn { width: 44px; height: 44px; }
  .tx-table td :is(input, textarea, button) { scroll-margin: 64px 0 88px; }
  .flag-note { margin: var(--s1) var(--s2) var(--s1); }
  .flag-actions { margin: 0 var(--s2) var(--s1); }
  .add-row { min-height: 44px; }
  .setup { padding-top: var(--s4); }
  .setup-lede { font-size: var(--text-body); }
  .setup-slots { grid-template-columns: minmax(0, 1fr); margin-top: var(--s6); }
  .setup-slot { min-height: 0; padding: var(--s4); }
  .setup-actions .btn { flex: 1; min-height: 44px; }
  dialog.sheet, dialog.sheet.sheet-pages { width: 100vw; max-width: 100vw; max-height: 92vh; margin: auto 0 0; border-width: 1px 0 0; }
  /* File names wrap rather than lose their dates at the end. */
  .export-files li { align-items: flex-start; }
  .export-name { overflow-wrap: anywhere; white-space: normal; }
  .export-name.cut { display: block; }
  .export-name .cut-head, .export-name .cut-tail { white-space: normal; }
}

/* Short windows (a phone on its side): bars scroll with the page instead of covering it. */
@media (max-height: 560px) {
  .topbar, .export-bar { position: static; }
  .tx-table th { top: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition: none !important; }
}
