/* ==========================================================================
   CALIBAN LEDGER — ledger-extra.css
   Loads after ledger.css and ledger-carry.css.

   A RULE IS ADDED HERE ONLY WHEN NO PRIMITIVE WOULD DO, AND EACH SAYS WHY —
   they are numbered in the order the screens that needed them were built.
   EVERYTHING ELSE THOSE SCREENS NEEDED WAS ALREADY
   SAID BY A PRIMITIVE. The brother's card, the zoom viewer and the
   cross-company confirmation are .lightbox + .card-panel (carried) filled with
   .plate-head / .roll / .rail.kv / .crew-stats / .chip / .field / .forge /
   .btn / .seal / .mark / .empty — no new look, no new colour, no new spacing.

   None of them is a look. They are hit targets the sheet draws below what a
   thumb needs, one position the sheet has no way to express, and one drag
   state it never drew.
   ========================================================================== */

/* 1 · A TARGET A THUMB CAN HIT.
   Owner's floor for these three surfaces: 44px, because a sergeant clears his
   desk one-handed on a phone. The sheet cannot say it: `.ledger .btn` pins
   every key to `calc(30px * var(--ui))` at (0,2,0), and `.btn-lg` — the only
   primitive that is taller — is written `.btn-lg:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))` at
   (0,1,0), so it loses to that cap on every screen and cannot be borrowed.
   `.seal` is the one key that is already big enough (38px, 48px on a phone) and
   is left alone. `.rail` has no height at all beyond its padding, which leaves
   a row in a picker about 34px tall — and in these three surfaces the rail IS
   the control: it is how a handle, a company and a sergeant are chosen.

   Written `.btn.btn-thumb` / `.rail.rail-thumb` (0,2,0) rather than with the
   bare `:where()` suffix (0,1,0) on purpose — at (0,1,0) the `.ledger .btn`
   cap would beat it and this rule would silently do nothing.

   Both are opt-in: nothing that is drawn today changes size. */
.btn.btn-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(44px*var(--ui));min-width:calc(44px*var(--ui))}
.rail.rail-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(44px*var(--ui))}

/* 1b · THE SAME FLOOR, FOR THE TWO OTHER CONTROLS THAT TAKE A PRESS.
   The audit screen's undrawn half is built out of two more primitives the
   sheet caps below a thumb, and both are controls there rather than ornament:
   the four ORDER chips (roster / latest / busiest / a–z) and the twelve
   SELECTS in the depth drawer, which are the only way to ask this tab the two
   questions Discord cannot answer at any price — a company and a squad.

   Neither can be said with what exists. `.views .chip` pins a chip to
   `calc(28px * var(--ui))` at (0,2,0) and `.field` pins a field to 34px; the
   only taller field in the sheet is `.field-code`, which is the 8-character
   sign-in box — 200px wide, centred, letter-spaced .3em — and borrowing it
   would drag that whole look onto a dropdown.

   Written `.chip.chip-thumb` / `.field.field-thumb` (0,2,0), not with the bare
   `:where()` suffix (0,1,0), for the same reason as the two rules above: at
   (0,1,0) `.views .chip` would beat it and the rule would silently do nothing.
   It ties with `.views .chip` at (0,2,0) and wins on order, this sheet being
   the last one linked.

   Height only. A chip's width is its word and a field's is the column it sits
   in, so pinning a min-width here would push "24h" and a twelve-up grid of
   dropdowns around for nothing. Opt-in: no drawn control changes size. */
.chip.chip-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),
.field.field-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(44px*var(--ui))}

/* 2 · THE ONE MESSAGE CHANNEL, LIFTED OFF THE PAGE.
   `.banner` is the primitive for a line the reader must see, and the toast
   keeps it: same mono type, same padding, same rhythm. What `.banner` cannot be
   is a toast — it is a full-bleed strip that sits in the document, and every
   write on every screen has to report over whatever is already on screen,
   including over an open .lightbox (z-index 70), which is the exact case where
   the report matters most: a credit refused from inside the brother's card.
   Nothing in either sheet positions anything over the page except .lightbox
   itself, and .lightbox is a modal veil — it would blank out the very screen
   the toast is reporting on.

   So: placement, elevation, and an opaque ground. The two overrides of
   `.banner` are both required, not taste — `.banner`'s bottom-only warn border
   and warn-tinted translucent fill would paint a successful credit in the
   colour of a problem and let the page show through the words. The state is
   said by the `.mark` inside instead, which is how the rest of the site says
   it. `rise` is the keyframe ledger-carry.css already carries.

   IT IS CENTRED WITH `left:0;right:0;margin:auto`, NOT WITH translateX(-50%),
   and that is not a preference. `rise` animates `transform`, so a toast centred
   by transform loses its centring for the whole 300ms the entrance runs and
   snaps sideways into place at the end -- measured, 6,663 pixels of difference
   between two renders of the same page. Centring by margin leaves `transform`
   to the keyframe alone. */
.toast:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:fixed;left:0;right:0;bottom:calc(26px*var(--ui));margin:0 auto;
  z-index:90;width:max-content;max-width:min(92vw,calc(560px*var(--ui)));
  border:1px solid var(--gold-line);background:var(--pop);
  box-shadow:0 14px 40px -10px rgba(0,0,0,.9);animation:rise .3s var(--ease-settle)}

@media (prefers-reduced-motion:reduce){
  .toast:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
}

/* --------------------------------------------------------------------------
   3 · "YOU MAY LET GO NOW" — THE ONE DRAG STATE THE SHEET NEVER DREW.
   Added for the filing screen. design/blocks/_lost.json calls this the single
   genuine casualty of that screen: every control in new-aar.js has a home in
   design/blocks/filing.html, but `.drop` has no drag-active look. The old
   sheet drew it (new-aar.js:95, `.na-drop:hover,:focus-visible,.over`);
   ledger.css:325 declares `.drop` and stops there. The four drag handlers and
   the drop target both survive — `w.dropClass` is already bound on the box —
   so without this rule the only thing missing is the feedback, and a dashed
   box that never answers a dragged file reads as a box that does not take one.

   No primitive says it. `.pick-on` is the sheet's own word for "this one is
   live under your hand", but it is a grid cell's chosen state, not a drop
   target's: taking its class would put a 1px gold border and a glow on a box
   that is 160px of dashed rule, and would mean "chosen" on a step where
   nothing has been chosen yet. So: the same two tokens `.pick-on` uses, on the
   two properties `.drop` already sets. Border stays DASHED — it is still an
   invitation, not a choice — and only its colour lifts. Nothing moves: no
   border width, no padding, no shadow, so the box cannot twitch under a file
   that is being dragged across it, which is the whole failure this replaces.

   `.drop.over` is (0,2,0) and so is ledger.css:619's `.ledger .drop` — this
   file loads after ledger.css, so the later rule takes the background. Written
   with the bare `:where()` suffix would have been (0,1,0) and lost it.

   Only feedback, so `prefers-reduced-motion` has nothing to answer: this
   animates nothing. Touch never fires it — a phone has no drag — which is why
   the tap path (the whole box is `role="button"`) is the one that must work,
   and does. */
.drop.over:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold-lit);color:var(--ink-2);background:var(--lift)}

/* --------------------------------------------------------------------------
   4 · THE FILING'S TWO × KEYS, AND THE PICTURE ONE OF THEM SITS ON.
   Both keys take something away — a screenshot, a brother — and both are drawn
   smaller than a finger. §1's `.btn-thumb` / `.rail-thumb` cannot be borrowed:
   neither key is a `.btn` or a `.rail`, and each needs a different answer.
   Three rules, in the order they depend on each other.

   4a · THE PICTURE UNDER THE KEY IS NOT THE SIZE IT LOOKS. Measured on
   design/reference/file-aar-reading.html before anything below: each `.thumb`
   renders 64×40 at 1600px and 25×16 at 390 — a postage stamp with a 20px ×
   on it, the "read" mark wider than the picture it marks. `<figure>` keeps the
   user agent's `margin:1em 40px`, so 80px of margin is taken out of every cell
   of a 4-up grid that is 599px wide. Nobody chose that: `.thumbs` sets its own
   `gap`, and ledger.css:268 draws `.shot` — the SAME figure, same 16:10, same
   overflow, same border and radius, in the AAR's gallery — with `margin:0` in
   it. `.thumb` is that rule copied without the line. So this is not a look I
   am adding, it is the one `.shot` already states, said again for the twin
   that lost it. Measured after: 144×90 on a desk, 105×66 on a phone — the only
   size at which the evidence is evidence, and the size at which 4b below has
   room to put a whole 44px target inside `overflow:hidden` (measured: 44×44
   surviving the clip at both widths; before this rule, 25×16).

   THIS ONE MOVES PIXELS, and it is the only thing on this screen that does.
   design/reference/file-aar.html — the measured state, step one — has no
   thumbnails and is untouched at 0. file-aar-reading.html gains the picture it
   was drawn to have. Deliberate.

   4b · `.thumb-x` is 20×20 at ledger.css:332, in the corner of the picture 4a
   has just given back its size. 20px is not a key a thumb can hit, and growing
   the DRAWN disc to 44 would cover a fifth of a 105px screenshot and contradict
   the disc file-aar-reading.html draws. So the disc stays exactly as Fable drew
   it and only the TARGET grows: a transparent ::after, 44px square. It paints
   nothing, it is out of flow, and it moves nothing — a hit target is the one
   shape that can grow when the drawing may not.

   IT GROWS INWARD, top:0;right:0, NOT OUTWARD FROM THE DISC'S CENTRE, and that
   is not a preference. `.thumb` is `overflow:hidden` (ledger.css:329), so a box
   centred on a disc sitting 4px from two edges is cut off at both of them — a
   44px target clipped back to about 36. Anchored into the corner it is whole,
   and it can be: the picture under it has no click of its own, the state mark
   is at the far corner (left:4px bottom:6px) and the progress `.bar` is the
   bottom edge, so nothing else lives in that corner to steal.

   Unconditional, not opt-in like §1: `.thumb-x` exists on exactly one screen,
   it is always a touch target there, and an invisible target harms nothing.

   4c · `.chip-x` (ledger.css:342) is a bare glyph with 4px of left padding
   inside a chip — the "take him out" key on the crew step, and the one control
   that can pay the wrong man by taking the right one off the run.

   The ::after trick of 4b is WRONG here, and the reason is worth writing down:
   chips wrap in `.chosen` with a 6px gap, so a 44px box hung off a chip's ×
   reaches about 14px past its own border and lands on the FIRST LETTERS OF THE
   NEXT MAN'S CHIP. A mis-tap there does not miss — it takes a different
   brother off. So this key grows honestly instead, inside its own border.

   §1b's `.chip-thumb` LOOKS like the answer and is not, and it was tried
   before this was written. It floors the CHIP at 44, not the key inside it:
   with the × stretched to the chip's content box, the chip's own vertical
   padding takes 6px back and the key measures 44×38 — under the floor on the
   axis a thumb actually misses. And §1b says height only, on purpose ("a
   chip's width is its word"), while here the × is not the word, it is the key.
   So the floor goes on the KEY, both ways: measured 44×44, inside its own
   border, with the man's name beside it inert. The chip becomes 50 tall, which
   is the padding doing what padding does.

   Nothing drawn changes: Fable never drew the crew step (design/README.md,
   "Further steps not drawn"). Opt-in on `.tap-thumb` like every size rule in
   this file — `.chip-x` is also the × on filter and tag chips, where the chip
   is chrome and 44px would be wrong. */
.thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin:0}
.thumb-x:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";position:absolute;top:0;right:0;
  width:calc(44px*var(--ui));height:calc(44px*var(--ui))}
.chip-x.tap-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;justify-content:center;
  min-width:calc(44px*var(--ui));min-height:calc(44px*var(--ui));padding-left:0}

/* --------------------------------------------------------------------------
   6 · A LIST THAT IS LONGER THAN THE DRAWER IT OPENS IN.
   design/blocks/companies.html has two pickers that answer with a list the
   page cannot show whole: the find-a-brother forge (a hit per brother, 629 of
   them) and the desks drawer's + discord role / + brother picker, which
   ui/app/index.html:16175,16183 caps at forty. At `.rail-thumb`'s 44px floor,
   forty rows is about 1,760px of rail — a drawer that pushes the seven
   companies off the bottom of the screen every time a GM opens it.

   THE SHEET SAYS THE SCROLL AND NOT THE STOP. `.scroll` (ledger-carry.css:2605)
   is `overflow:auto` and nothing else; with no height on the box there is no
   overflow, so it scrolls nothing. Neither sheet caps a list anywhere:
   `.cap-wrap` (ledger.css:400) is the authority grid's horizontal scroller,
   and every other list on the site is the whole plate and scrolls with the
   page. A drawer is the one place that cannot.

   The old markup said it with `style="max-height:220px"` on each of the three
   pickers, which the handoff forbids outright ("no inline styles anywhere") —
   this is that same measurement, said once, in the sheet.

   264px IS SIX 44px RAILS. Deliberately not a round number: the floor in §1 is
   what a row of this list is, and a cap that is an exact multiple of it would
   end flush with a border and read as the end of the list. Six rows and a
   sliver of the seventh says there is more below without a word of chrome.

   ONE DECLARATION, because the other half is already said: it is written on
   the markup as `.scroll.scroll-cap`, so `.scroll` keeps the overflow and this
   adds only the stop that makes the overflow exist. */
.scroll-cap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){max-height:calc(264px*var(--ui))}


/* ==========================================================================
   5-7 · THREE MORE, FOR THE AAR'S UNDRAWN CONTROLS. Everything else the aar needed
   was already said: the drawers are .plate.plate-lit + .plate-head + .field +
   btn/btn-bad/btn-esc/seal, the override menu is .rail-act + .rail-detail, the
   pickers are .rail.rail-thumb in a .roll, and the 44px floors are §1/§1b's
   own .btn-thumb / .rail-thumb / .field-thumb. These three are not looks.
   ========================================================================== */

/* 5 · A SIXTH ROW IN A MAN'S CARD.
   `.crew-card` is a grid with `grid-template-areas:"id state" "stats stats"
   "ask ask" "acts acts" "note note"` (ledger.css:151) and every child class it
   has is nailed to one of those six areas. Five controls belong to ONE man and
   have no area: the settled-man switches, the recorded refusal reason, and the
   refusal / escalation / points drawers. A sixth
   child with no placement auto-flows into the FIRST free cell, which on a
   two-column card is beside `id` or under `state` — it lands in the wrong half
   and takes the card's shape with it.

   `grid-row:-1` IS THE HALF THAT TOOK A MEASUREMENT TO FIND, and
   `grid-column:1/-1` alone is a bug. A named area is a reserved NAME, not an
   occupied cell: the reference card carries no `.crew-ask` (a man with no
   question asked about him), so row 4 is empty, and an auto-placed item walks
   the cursor down to the first free row and lands THERE — measured, the
   corrections row drew itself above the credit seal, and would have moved
   below it the moment somebody was sent up. `-1` is the last line of the
   explicit grid whatever the template says, so the row opens after `note` and
   stays there if Fable ever adds a sixth area. Nothing is added to the
   template itself, so a card with nothing to correct grows no row and no gap
   and renders exactly as the reference renders it.

   AND EVERY ONE OF THE FIVE MUST STAY CONDITIONAL, which is the half that was
   got wrong once and cost 97,680 visible px. "not on this run" was a sixth
   thing in here, gated on `offShown` — which is `canReviewMember`, true for
   every man a sergeant may judge. So the row was never shut, and because
   `.ledger .team-body` is `align-items:end` (ledger.css:477) a sixth row on
   ONE arch drops the other two arches and every section under the fireteam by
   its height. It is in the aar's muster drawer now. This rule opens a row when
   a man has something to correct; it is not a place to put a control that is
   always there.

   ONE WRAPPER, NOT ONE PER CONTROL, for the same reason: two auto-placed
   siblings would take two different rows, and two items both pinned to `-1`
   would sit on top of each other. The wrapper is a plain `.col.g5` and the six
   things stack inside it in the order the sheet already stacks anything.

   `width:100%` is the second half and is not decoration: the look centres the
   arch card's items (`.ledger .crew-card{justify-items:center}`, ledger.css
   :485), which shrink-wraps a drawer to its text and leaves its `.field` with
   no width to be. It is how ledger.css itself defeats that rule one line
   later — `.ledger .crew-acts{justify-content:center;width:100%}` (:505). */
.crew-extra:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-row:-1;grid-column:1/-1;width:100%}

/* 6 · A MAN'S OWN WORDS, AS HE TYPED THEM.
   The brother's dispatch is the original Discord post and the chronicle is
   what the auspex read out of a screen; both arrive with line breaks that ARE
   the content — a muster on its own line, a field per line. Every text
   primitive in ledger.css (.meta, .label, .kv-v, .crew-ask) leaves
   `white-space` at `normal`, which folds a fifteen-line post into one grey
   paragraph, and none of them may be given pre-wrap without doing it to every
   other place they are used. `overflow-wrap` goes with it and is not separate
   taste: pre-wrap will not break a pasted URL, and one link then makes the
   record scroll sideways. Opt-in; nothing drawn today wears it. */
.said:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){white-space:pre-wrap;overflow-wrap:anywhere}

/* 7 · THE RECORD'S ROWS, WHERE THE JUDGING ACTUALLY HAPPENS.
   The override key is `.on-hover` in the reference, and the phone block turns
   it off outright — `.ledger .cmp .on-hover{display:none}` (ledger.css:690) —
   so on a touch screen the one control the record exists for had no way to be
   pressed. The press is on the ROW now, which needs nothing new on a desktop:
   `.rail-act` is already a pointer and the reference's row height is a mouse
   target the reference measured. On a phone the row IS the control and
   `.cmp-row`'s own `min-height:calc(34px*var(--ui))` (ledger.css:298) is ten
   short of the owner's floor, so it is lifted there and only there — no
   desktop pixel moves. `.cmp .rail-act` is (0,2,0) deliberately: at the bare
   `:where()` suffix it would be (0,1,0), lose to `.cmp-row`'s cap, and do
   nothing at all.

   The detail's 88px left indent is measured for the roster's avatar column
   (ledger.css:140); the record has no avatar column, and on a phone plate 88px
   of nothing leaves an option row too narrow to read its own worth. */
@container ledger (max-width:720px){
  .cmp:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .rail-act{min-height:calc(44px*var(--ui))}
  .cmp:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .rail-detail{padding-left:var(--s6);padding-right:var(--s6)}
}

/* ==========================================================================
   THE CODEX — two rules, both for controls the redesign never drew.
   ========================================================================== */

/* CODEX a · A COLUMN CLASS FOR A TABLE WHOSE WIDTH IS IN THE DATA.
   WIRING.md's contract for a list row is `.rail.rail-grid` PLUS a column
   class, and it names every column class the sheet has: .roll-row, .rep-row,
   .kv, .cmp-row, .stat-row, .log-row, .desk-row, .scope-row, .named-row,
   .tariff. Each is a fixed shape. The eight measured victory-screen tables
   (docs/VICTORY-SCREENS.md, 3,281 scanned screens) are 3, 4 and 6 columns —
   the count is a property of the table, not of the screen — so none of the ten
   fits, and `.tariff`, the nearest, is two columns.

   ledger-carry.css DOES carry `.dcols{grid-template-columns:var(--dcols)}`,
   which is how the live view says this today — but only with a
   `style="--dcols:…"` on every row, and WIRING.md's first line is "no inline
   styles anywhere". So the count comes from auto-placement instead of from a
   hole: one explicit column for the row's name, and `grid-auto-flow:column`
   lets each measured cell make its own track. Three columns or six, the markup
   is the same and the sheet never learns the number.

   Written at `.rail-grid`'s own weight (0,1,0) and loading after it, so only
   the template is added — the grid, the alignment and the 9px gap stay
   `.rail-grid`'s. */
.xp-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(calc(132px*var(--ui)),1.7fr);grid-auto-flow:column;grid-auto-columns:minmax(calc(60px*var(--ui)),1fr)}

/* CODEX b · THE ONE ROW WHERE A MISPRESS RESCORES THE ARCHIVE, SIZED FOR A THUMB.
   `.seal` is already 48px on a phone (ledger.css 683). The other keys in the
   `.law` bar are not: `.ledger .btn` pins every key to 30px at (0,2,0) and the
   phone block never lifts that cap, so `discard` — which throws away every
   staged change — sits at 30px under a thumb. The owner's floor for a control
   a sergeant works one-handed is 44px.

   Scoped to `.law` and to the phone query on purpose: nothing on a desktop
   moves by a pixel, and no key outside this bar changes at all. `.btn-thumb`
   above cannot do it — that is opt-in per element, and `discard` is the
   reference's own markup, which this screen may not rewrite.

   IT COSTS ONE KEY'S OUTLINE AND NOT ONE ROW OF REFLOW, MEASURED. The bar's
   height on a phone is set by `.seal`, which ledger.css already stands at 48px
   (line 683), so a 44px `discard` grows inside height the bar already had:
   against design/reference/codex.html at 430x3200 the whole page is identical
   but for this key's own box — visible 422 px, and nothing below it moves.

   A SECOND LINE LIVED HERE AND IS GONE. `.law .lead{flex:1 1 100%}` gave the
   sentence its own row on a phone, and it was here only because the Codex once
   carried a THIRD key in this bar: with it, the sentence collapsed to one word
   per line and stood the bar 260px tall. That key is gone — `what changed` is
   now opened by pressing the sentence itself — so the rule had nothing left to
   pay for, and it was standing the phone page 24px down from the reference on
   every row. Removed; the reference's own phone layout is back. */
@container ledger (max-width:720px){
  .law .btn:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(44px*var(--ui))}
}

/* 4 · THE ODD TILE, ON A PHONE.
   `.tiles` folds to two columns under 720px, so a band of THREE leaves its
   last tile alone in the left half with a border down the middle of nothing.
   ledger.css already fixes this for the only odd band it draws —
   `.ledger .tiles-5 .tile:last-child{grid-column:1/-1}` — but there is no
   `.tiles-3` twin, because the sheet never drew a three. The roster's head
   band is three (the count, who is online, who is on vox), so it needs the
   same line the five already has. Same selector shape, same container query,
   same reason; `.tile:last-child` then drops the right border on its own. */
@container ledger (max-width:720px){
  .ledger .tiles-3 .tile:last-child{grid-column:1/-1}
}

/* AUTHORITY · A SWITCH A THUMB CAN HIT, WITHOUT REDRAWING THE SWITCH.
   The desks and the bot's two master switches are `.toggle`, and a `.toggle` is
   `width:34px;height:18px` — well under the 44px floor, and on those rows the
   toggle IS the control: the rail around it is not clickable.

   Neither `.btn-thumb` nor `.rail-thumb` above can say it. Raising the toggle's
   own box the way `.btn-thumb` raises a key would draw a 44px pill instead of
   the 34x18 switch the design drew — a look change, not a target change, and
   the look is the half that already matches the reference. So the target is a
   transparent `::before` centred on the switch: 44px of catch, nothing painted,
   zero pixels of difference. `.toggle` is already `position:relative` and its
   `::after` is the knob, so `::before` was free.

   `max(100%,44px)` rather than a flat 44px so a `--ui` past 1.29 — where the
   switch itself is already wider than 44px — widens the catch with it instead
   of shrinking it. The height is held at exactly 44px because `.rail-thumb`
   pins these rows to 44px too: the catch then ends on the row's own edge and
   never reaches into the switch above or below it.

   Opt-in, like the rest of this file: a bare `.toggle` is untouched. */
.toggle.toggle-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{content:"";position:absolute;top:50%;left:50%;
  transform:translate(-50%,-50%);width:max(100%,calc(44px*var(--ui)));height:calc(44px*var(--ui))}

/* 10 · THE TICK, PUT BACK.  [reports]
   Not taste and not new: index.html carries the pair
   `.pick-box{…color:transparent}` + `.pick-on{border-color:var(--gold);
   color:var(--gold);background:rgba(196,157,74,.12)}`, and only the first half
   was carried into ledger-carry.css. `.pick-on` DOES exist in ledger.css, but
   it is the modifier of the `.pick` primitive — it sets a border, a background
   and a glow and never touches `color`, which is the one declaration a
   `.pick-box` needs, because the box paints its "✓" in `color:transparent` and
   the ✓ is the whole control. ledger-carry.css also loads after ledger.css, so
   even a `.pick-on` that did set a colour would lose to `.pick-box` on order.

   Rendered: under these three sheets, ticking a report in recheck mode paints
   a gold border round an EMPTY box. Reproduced on reports.preview with
   `tickCls="pick-box pick-on"`, which is verbatim what index.html:13342
   returns.

   `.pick-box.pick-on` (0,2,0) beats `.pick-box:where(…)` (0,1,0) whatever the
   order. Colour only — the border and ground are already right from
   `.pick-on`. */
.pick-box.pick-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold)}

/* 8 · A MENU ROW A THUMB CAN HIT — OPT-IN, ON THE POPOVER.  [reports]
   Added for the reports screen's sort menu, which is the only home the two
   lost sorts (scanned, discrepancies) have, and the only sort control that
   exists at all under 720px — `.roll-hdr` hides four of the eight column heads
   there and the four that survive are 10px `.label` text.

   No primitive says it. `.pop-item` is `padding:5px var(--s4)` and lands near
   26px; `.rail-thumb` (rule 1) is the 44px row primitive but a `.rail` is the
   wrong body inside a `.pop` — it brings its own `var(--s8)` side padding and
   bottom hairline on top of the popover's `var(--s3)` padding and `var(--r2)`
   gap, and `.rail-act`'s inset gold bar is a LIST affordance, not a menu one.
   So the height alone, on the container, so one class on a `.pop` sizes every
   row in it and the rows keep being `.pop-item`.

   `.pop-thumb:where(…) .pop-item` (0,2,0) beats `.pop-item:where(…)` (0,1,0).
   Opt-in: the four filter menus keep the size they render today until their
   `popCls` binding adopts it — see design/blocks/reports.bindings.md C.9.
   Measured 0 px: every popover on every rendered reference is shut. */
.pop-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .pop-item{min-height:calc(44px*var(--ui));padding-block:0}

/* 9 · THE FILTER BAR, ON A PHONE ONLY.  [reports]
   The same 44px floor as rule 1, and this is where a sergeant meets it:
   `.chip` is `padding:calc(2px*var(--ui)) var(--s3)` around `--fs-1`, about
   18px tall, and `.field` is capped at 34px — and on the reports screen those
   seven controls ARE the screen. Written against `.head-acts` and not against
   `.chip` because `.views .chip` already takes a min-height of its own for the
   tab rows, and the tabs are not being changed.

   PHONE ONLY, and that is the point rather than a hedge. Fable drew the bar at
   desk width and it is right at desk width; the floor is a THUMB's floor. It
   also means the rule cannot move a pixel of any rendered reference — every
   one of them is shot at 1600px, far outside this query — so what already
   matches keeps matching, exactly. Measured: 0 px.

   The chips take side padding with the height, because a 44px box around
   `var(--s3)` of padding reads as a tall sliver rather than a key. */
@container ledger (max-width:720px){
  .head-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .chip{min-height:calc(44px*var(--ui));padding:0 var(--s5)}
  .head-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .field{min-height:calc(44px*var(--ui))}
}

/* 13 · THE SORT SENTENCE IS THE SORT CONTROL.  [reports]
   The reports list `.plate-head` already draws the order in words — repSortSaid
   plus the state, "newest first · awaiting word" — so the seven-key sort menu
   hangs off those words instead of off a new chip. That is what keeps the
   screen on the drawing (a fifth chip on `.head-acts` re-centres the whole
   centred bar: 3,652 px measured; a chip in this centred-column plate-head is
   a whole new stacked line: 54,615 px). What no primitive says is that a line
   of `.plate-head-meta` is now pressable.

   AT REST IT IS UNTOUCHED, ON PURPOSE. `cursor` and `:hover` do not exist in a
   screenshot, so the sentence paints exactly the colour and weight Fable drew
   and the measurement stays at 0 px; the lift to `--ink` only ever happens
   under a pointer or a focus ring, which is the moment a man needs to be told
   it answers. No underline, no glyph, no border: any of those would be ink the
   drawing does not have.

   The phone half is rule 1's 44px floor, reached the way rule 9 reaches it —
   inside the container query, where no rendered reference can see it. There
   the meta is already `width:100%`, so it only ever needed height.

   `.plate-head-meta[role=button]:where(…)` is (0,2,0), which ties `.ledger
   .plate-head-meta` in the LOOK section and wins on order; the attribute is
   the honest hook, since it is exactly the sentences that became buttons. */
.plate-head-meta[role="button"]:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:pointer}
.plate-head-meta[role="button"]:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)):is(:hover,:focus-visible){color:var(--ink)}
@container ledger (max-width:720px){
  .plate-head-meta[role="button"]:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){
    display:flex;align-items:center;justify-content:center;min-height:calc(44px*var(--ui))}
}

/* 11 · THE BOARD'S OWN LINE, AS A KEY.  [honours]
   The honours board's TYPE and DIFFICULTY questions are asked from the line
   that answers them — `.plate-head-meta` on the Operatives plate, "career ·
   PvE · by kills" — because the strip of two chips that held them before sat
   in the normal flow and pushed every plate under it 64px down the page:
   138,846 pixels between the rebuild and the reference, for two chips. The
   line was already the readout; making it the key costs no box at all.

   Two things no primitive will do for it, and both must cost nothing.

   `cursor`, at every width: a `.plate-head-meta` is type, not a control, so
   nothing in ledger.css says it can be pressed — and a cursor paints no
   pixels, so the affordance is free.

   Then the 44px floor, WITHOUT TAKING 44px OF PAGE. §1/§1b cannot be
   borrowed: `chip-thumb` and `btn-thumb` are `min-height` written against
   `.chip` and `.btn`, and a height on THIS line is the same mistake the strip
   made in miniature — it is a flex child that ledger.css:655 turns into a
   full-width line of its own on a phone, so 44px there pushes the whole board
   down 33px and 15,634 pixels off the reference at 420px. Measured, not
   assumed. So the target is grown where a target can be grown for free: an
   empty, transparent `::after` over the line, inset 17px above and below it,
   inside the plate head's own 22px/18px padding so it overlaps nothing and
   reaches nothing else — the head holds only the heading and this line. It
   paints not one pixel at any width.

   AND IT OPTS OUT OF §13's PHONE FLOOR, WHICH IS THE SAME RULE BOUGHT THE
   COSTLY WAY. §13 gives every `.plate-head-meta[role="button"]` a 44px
   `min-height` under 720px; on this screen that line is the last child of the
   Operatives plate head, so the floor pushes the whole board down 33px and the
   phone render lands 15,634 px off Fable's — measured, both ways. The catch
   above already gives the thumb its 44px there, so the floor is turned off for
   this one line rather than paid for twice. `.plate-head-meta.meta-thumb` is
   (0,2,0), tying §13 and winning on order; if §13 ever adopts the catch
   instead (the authority sheet's `.btn-catch` below is the same trick again),
   this block can go.

   Measured: honours visible 0 px at 1600px, and 0 px at 420px. */
.meta-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:pointer;position:relative}
.meta-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";position:absolute;left:0;right:0;top:calc(-17px*var(--ui));bottom:calc(-17px*var(--ui))}
@container ledger (max-width:720px){
  .plate-head-meta.meta-thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:block;min-height:0}
}

/* AUTHORITY · A KEY A THUMB CAN HIT, WITHOUT MOVING THE ROW IT SITS IN.
   The same 44px floor as rule 1, and the same answer as the switch above, for
   the keys that live INSIDE a row rather than in a bar of their own: the `×`
   that takes a named override off a brother, and the grant/revoke pair under
   the field. `.btn-thumb` is the wrong instrument there — it raises the key's
   own box to 44px, and a `.rail` has no height beyond its padding, so the row
   grows with the key and every row below it moves. Measured on the authority
   sheet: the two named rows and the field rail put the lower half of the page
   out by a row. That is a reflow, not a target.

   So the target is a transparent `::before` centred on the key: 44px of catch,
   nothing painted, zero pixels of difference — the switch's trick, moved from a
   `.toggle` to a `.btn`. `.btn` is not positioned, so the class sets
   `position:relative` itself; that changes no geometry and the key has no
   positioned child to reorder.

   `max(100%,44px)` on the width so a key already wider than the floor ("let him
   approve") keeps its own width, and only a short one (`×`) grows its catch.
   Opt-in: a bare `.btn` is untouched, and `.btn-thumb` is still the right rule
   for a key standing in a bar where 44px of height costs nothing.

   Measured: authority visible 1057 px before and after, every one of them the
   three re-hung tab chips and none of them a key. */
.btn.btn-catch:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative}
.btn.btn-catch:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{content:"";position:absolute;top:50%;left:50%;
  transform:translate(-50%,-50%);width:max(100%,calc(44px*var(--ui)));height:calc(44px*var(--ui))}

/* SHELL · A TARGET A THUMB CAN HIT, IN A BAR 55px TALL.
   The topbar's three controls are all drawn well under the owner's 44px floor:
   a `.navtab` is 23.7px of 10.5px mono with `var(--s2)` above and below, the
   `.avatar.avatar-sm` he presses to reach his own record is 22px, and `.brand`
   is the 26px sigil. Measured in the rendered reference at 1600px.

   NONE OF THE THREE CAN TAKE ITS 44px AS HEIGHT. The bar is 55px tall and
   `.topbar` is `align-items:center`, so a 44px `min-height` on a navtab does
   not grow the bar — but `.nav` is a flex row whose baseline the `.navtab-on`
   underline is drawn on, and raising one item's box moves that underline off
   the line Fable drew it on. `.avatar` is worse: its width and height are the
   same token, so a floor on the height is a floor on the circle.

   SO THE TARGET IS A TRANSPARENT ::after, CENTRED, PAINTING NOTHING — rule
   11's trick, generalised off `.plate-head-meta` so the shell's three controls
   and any later one can share it. `max(100%,44px)` on the width so a wide key
   ("battle honours") keeps its own width and only a narrow one grows its
   catch; 44px of height regardless. At 1600px every catch lands inside the
   55px bar (the navtab's spans y4.4–48.4 of 55, the avatar's y5–49), so it
   overhangs nothing and costs nothing: the topbar crop measures 0 px against
   design/reference/roster-brothers.html with all three carrying it.

   `.btn-catch` is NOT re-used for the navtab: it is written `.btn.btn-catch`
   at (0,2,0) deliberately, to beat the `.ledger .btn` height cap, and a
   `.navtab` is not a `.btn`. The ◉ in `.topbar-end` IS a `.btn` and keeps
   `.btn-catch`, unchanged.

   ONE PLACE IT IS CLIPPED, AND IT IS THE SHEET'S OWN CHOICE, NOT THIS RULE'S.
   Under `@container ledger (max-width:720px)` ledger.css:649 makes `.nav` a
   horizontal scroller (`overflow-x:auto`), and an overflow that is not
   `visible` on one axis computes to `auto` on the other, so on a phone a
   navtab's catch is cut to the scroller's own ~26px box. The fix would be
   vertical padding on the navtab itself, which grows `.nav`, which grows the
   topbar, which drops every screen below it — the costly way rule 11 already
   priced at 15,634 px. Left clipped, and said here rather than hidden. */
.tap-catch:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;cursor:pointer}
.tap-catch:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";position:absolute;top:50%;left:50%;
  transform:translate(-50%,-50%);width:max(100%,calc(44px*var(--ui)));height:calc(44px*var(--ui))}

/* SHELL · THE ONE TAB THAT IS NOT IN THE NAV.
   `me` — a brother's own record — has no `.navtab`; the avatar in
   `.topbar-end` IS its tab, and today it says so by taking a gold border when
   that route is open (index.html:12929, `meBd`). The sheet has no way to say
   it. `.navtab-on` is a gold border-BOTTOM and gold text on a mono word and
   does nothing legible on a 22px circle; `.avatar-officer` and `.avatar-sgt`
   are the same gold ring but they mean RANK, and putting "he is a Lieutenant"
   on the reader's own face to mean "you are on your own page" is a lie in the
   markup that the roster and the strategium would then be reading.

   So: the ring the two rank classes already draw, under a name that says what
   it means, plus the lit glow every other current thing in this sheet carries
   (`--glow-live`, the same one `.mark-live` and `.crew-mine` use). Opt-in and
   off at rest — the reference draws the roster, not `me`, so it costs 0 px. */
.avatar.avatar-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold);box-shadow:var(--sink),0 0 14px -4px var(--glow-live)}

/* SHELL · A BAND OF TWO.
   The figures band above a route is `.tiles`, and ledger.css draws it at four
   columns with a `.tiles-3` and a `.tiles-5` beside it — the three counts
   Fable's own screens needed. The strategium's band is TWO figures, by the
   owner's instruction ("I dint need the company strength or on vox in the
   stratigum at all"), and at `repeat(4,…)` two tiles sit in the left half of
   the bar with two empty tracks beside them. There is no way to say "as many
   columns as there are tiles" here: `auto-fit` would give a single figure the
   whole width, which is the strategium's own band on the day the desk is
   clear. So: the twin `.tiles-3` already is, for the count the sheet skipped.
   Drawn nowhere in the references, so it costs 0 px on every measured screen. */
.tiles-2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:repeat(2,minmax(0,1fr))}

/* SHELL · A GAP BESIDE A TAB'S WORD, WITHOUT LETTING THE TAB SHRINK.
   One navtab carries a count — the approvals waiting on the strategium's desk
   — and the sheet draws no navtab that carries anything but a word, so there
   is no gap between the two. `.row.g4` is the obvious borrow and it is WRONG
   HERE, for a reason that only shows on a phone: `.row` carries
   `min-width:0`, and under `@container ledger (max-width:720px)` ledger.css:649
   turns `.nav` into a horizontal SCROLLER. A min-0 flex item in an overflowing
   row shrinks instead of overflowing, so all seven tabs squeeze into the 420px
   strip and the words run together — ROSTERREPORTSSTRATEGIUBATTLE. Measured
   against Fable's own render at 420: 1,386 visible px, for a gap.

   So this says the gap and nothing else, and leaves `min-width` at `auto`
   where the sheet put it. It is on every tab rather than only the one with a
   count, because a tab that changes its own display when a number arrives is a
   tab that moves when the desk fills up. Measured with all seven carrying it:
   visible 0 px at 1600px and 0 px at 420px. */
.navtab.navtab-count:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s3)}

/* --------------------------------------------------------------------------
   THE ROOT HAS TO BE A COLUMN, OR EVERY UNCONVERTED SCREEN IS BLANK.

   Found live, within a minute of the shell shipping: the topbar drew perfectly
   and the whole page under it was empty, with one clipped line of text at the
   very top where the route should be.

   `.ledger` is `position:relative;overflow:hidden` (ledger.css:50) with
   `min-height:100vh` (:445) and no display of its own, so it is a block. The
   old markup underneath it is a FLEX CHAIN -- `<div class="col grow clip mh0">`
   holding the route slot, and inside that `<div class="grow rel mh0 scroll">`
   which is the scroller. `grow` is `flex:1` and means nothing without a flex
   parent, so both collapsed to content height, the scroller resolved to zero,
   and `overflow:hidden` on the root cropped what was left to the sliver
   visible at the top.

   The old `<div class="col grow clip mh0">` that used to be the flex parent is
   still there -- the shell splice did not delete it -- but it is no longer the
   thing filling the viewport, because the root now sits above it.

   So the root becomes the column the chain expects. This is not a compromise
   for the redesign's sake: the rebuilt screens stack a topbar and one `<main>`
   down the page, and a flex column with the default `stretch` is how they
   already lay out under block flow. Measured on the AAR preview after, which
   still reads visible 0 px against Fable's reference.

   It goes here rather than in ledger.css because it exists to carry the OLD
   layout through the cutover, and it should be deleted the day the last
   `.oldskool` wrapper goes -- not quietly inherited as part of the design.
   --------------------------------------------------------------------------
   IT IS SCOPED TO `.carrying-old`, AND THAT MATTERS. Applied to every
   `.ledger.page` it also reshapes the REBUILT screens, which lay out under
   block flow -- measured, the roster preview went from visible 0 px to 126,276
   against Fable's reference the moment this rule was unconditional. The root
   carries `carrying-old` only while unconverted routes are still wrapped in
   `.oldskool`; when the last wrapper goes, the class and this rule go with it
   and nothing else changes. */
/*
 * AND THE ROOT IS WHAT SCROLLS, WHICH NOTHING WAS.
 *
 * Measured on the live page: every route reported bodyH 1311 while the roster's
 * own `main` was 13,523px tall. Nine screens of roll inside a container that
 * clipped it, on every converted screen, and no way to reach any of it.
 *
 * `.ledger` is `overflow:hidden` (ledger.css:50) because it is the container
 * for the container queries, and `body` keeps `overflow:hidden` from the old
 * sheet -- html and body are deliberately unfenced. In Fable's design the
 * DOCUMENT scrolls; here the document cannot, and the old inner scroller only
 * wraps routes that are still unconverted. A converted `main` is a direct child
 * of this root, so it had nothing between it and the clip.
 *
 * So the root scrolls. It keeps `overflow-x:hidden` -- sideways is a fault, not
 * a feature -- and `overflow-y:auto` gives back the nine screens. When the last
 * route is converted this goes with `.carrying-old` and the document scrolls
 * the way the design intends.
 */
.ledger.page.carrying-old{display:flex;flex-direction:column;min-height:100vh;
  height:100vh;overflow-x:hidden;overflow-y:auto}
/*
 * AND THE WHOLE CHAIN, NOT JUST ITS FIRST LINK.
 *
 * `<div class="col grow clip mh0">` and the scroller inside it sit BETWEEN the
 * root and the routes -- inside `.ledger`, outside every `.oldskool` wrapper --
 * so the fenced sheet cannot reach them and they lost `display:flex` and
 * `overflow:auto` along with everything else. Giving the outer one `flex:1`
 * without `display:flex` fixed its own height and left the scroller inside it
 * with no flex parent, which is the same fault one level down: the reports
 * screen was still blank after the column landed.
 *
 * `body` keeps `overflow:hidden` from the old sheet -- html and body are
 * deliberately unfenced -- so this scroller is what scrolls the page. Deleting
 * it, which the shell's own notes call for, only becomes safe once the last
 * route is converted and the document itself scrolls.
 */
.ledger.page.carrying-old > .col.grow{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
.ledger.page.carrying-old .grow.scroll{flex:1 1 auto;min-height:0;overflow:auto}

/* --------------------------------------------------------------------------
   THE BOUNDARY HAS TWO SIDES AND I ONLY BUILT ONE.

   tools/fence-old.mjs keeps the OLD sheet out of `.ledger` and lets it back in
   at `.oldskool`. Nothing kept the NEW sheet out of `.oldskool` -- and an
   unconverted screen is inside `.ledger`, so every new rule reached it.

   The footer is what showed it. `ledger.css:412` draws `.candles` and `.candle`
   for the redesign: 7px wide, 30px tall, with a flame on a pseudo-element. The
   old quote strip is a 26px band of its own candles, and both rules matched the
   same elements. Where the old sheet set a property it won on source order, and
   where it did not -- `.candle` itself, which the old sheet never styled -- the
   new one simply took over. An old strip with new candles in it, on every page.

   So every anchor in all three sheets now carries
   `:not(:where(.oldskool,.oldskool *))`. Still zero weight: `:not()` takes its
   argument's specificity and `:where()` has none, so no rule changes rank
   against any other. It goes when the last hatch does.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   THE TITLE BLOCK IN A SCREEN HEAD.

   `head-title` is written on the AAR, the Codex and the Audit Log -- the title
   and its sub-line, beside whatever acts that screen offers -- and no sheet
   defines it. Fable used it as a hook and the layout came from `col g1 min0`
   sitting beside it, so it did nothing and nothing looked wrong.

   A class that means nothing is a class the next person will guess at, and
   `core/audit-man-ui.test.ts` flags any class the markup uses that no rule
   defines, which is how it surfaced. So it says the one thing it is actually
   for: the title takes the room that is going, and yields it before the acts
   do -- a long operation name must not push `read it again` off the row.
   -------------------------------------------------------------------------- */
.head-title:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1 1 auto;min-width:0}

/* --------------------------------------------------------------------------
   THE TWO CELLS OF A LOG LINE THAT THE SHEET NEVER NAMED.

   `log-time` and `log-actor` are written on every row of the audit feed and no
   sheet defines them -- the grid gave them their places and they carried no
   look of their own, so nothing appeared wrong. `core/audit-man-ui.test.ts`
   flags any class the markup uses that no rule defines, and with its slice
   mended (it had been truncating to a fifth of the screen) it found them.

   They say the two things a log line needs and the grid cannot: the time is a
   figure, so it lines up column-wise and never reflows the row; the actor is a
   name, so it takes the room going and yields it to the sentence rather than
   pushing it off the row.
   -------------------------------------------------------------------------- */
.log-time:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){
  font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--ink-5)}
.log-actor:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ==========================================================================
   THE EMBER FIELD -- what drifts behind every screen.

   Owner: "i want the embers back but i want them to look more like embers with
   dnamic floating paths and a little glitter looking... subtle in tehe
   background".

   THEY HAD NOT GONE ANYWHERE. Twenty-two of them were in the page the whole
   time, each carrying its own inline `animation:emberrise ...`, and every one
   was stopped dead by a single rule in ledger-carry.css that was meant to sit
   inside `@media (prefers-reduced-motion:reduce)` and did not. Its own comment
   calls it "THE REDUCED-MOTION GUARD". At top level it cancelled every inline
   animation inside `.ledger` for everybody, always -- the embers and the boot
   sequence with them -- and it did it silently, because a thing that does not
   move looks exactly like a thing that was never there. That is fixed where it
   lives; this is the field rebuilt on top of it.

   WHAT LIFTS IT CANNOT BE WHAT SWAYS IT. The old keyframe did both: a rise and
   one `--dx` shove, which is a straight diagonal. Twenty-two straight diagonals
   read as rain. The outer element owns the rise; the inner one owns the sway,
   on its own longer and deliberately un-harmonious period, so the two
   transforms compose into a path that wanders and does not repeat. The same
   separation the skull's flame needed, and for the same reason -- an animation
   beats an inline transform, so one element cannot do two jobs.

   SUBTLE MEANS MEASURED, NOT GUESSED. Peak opacity runs .10 to .34 and the dots
   are 1 to 2.4px with a soft halo rather than a hard shadow; at 1600px the
   field's brightest frame lifts the page ground by under two per cent. It sits
   at z-index 0 behind every route and takes no clicks.
   ========================================================================== */
/* THE FIRE ITSELF, on one canvas. ui/app/embers.js draws into this; it sits
   inside the ember field, above the glow it throws and below every plate, and
   catches nothing -- a click goes straight through to the page. Its colour is
   --ember-rgb, which each chapter answers for. */
.embers{position:fixed;inset:0;width:100%;height:100%;z-index:0;pointer-events:none}
@media (prefers-reduced-motion:reduce){.embers{opacity:.5}}
.ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:fixed;inset:0;z-index:0;overflow:hidden;
  pointer-events:none;contain:strict}

/* THE RISE. `bottom:-24px` starts it below the fold so nothing pops into
   existence at the edge, and 112vh carries it off the top the same way. */
.ember:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;bottom:-24px;width:0;height:0;
  animation:ember-rise var(--rise) linear var(--rise-in) infinite;
  will-change:transform}

/* THE SWAY, on the inner element and on its own clock. The periods are chosen
   not to divide into the rise, so an ember is never at the same point of its
   sway twice at the same height. */
.ember > i:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:0;bottom:0;display:block;
  width:var(--d);height:var(--d);border-radius:50%;
  background:radial-gradient(circle at 50% 50%,#FFE6A8 0%,#D8AE4A 45%,rgba(216,174,74,.35) 72%,transparent 100%);
  opacity:0;
  animation:ember-sway var(--sway) ease-in-out var(--sway-in) infinite,
            ember-live var(--rise) linear var(--rise-in) infinite;
  will-change:transform,opacity}

/* THE HALO IS A SECOND SHAPE AND NOT A box-shadow. A shadow is a hard ring at
   whatever radius it is given; a radial gradient falls off, and falling off is
   the whole of what makes a thing look lit. The candles at the foot of the page
   are drawn the same way. */
.ember > i::after:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){content:"";position:absolute;left:50%;top:50%;
  width:calc(var(--d) * 7);height:calc(var(--d) * 7);
  margin:calc(var(--d) * -3.5) 0 0 calc(var(--d) * -3.5);border-radius:50%;
  background:radial-gradient(circle,rgba(226,186,88,.46) 0%,rgba(204,146,62,.19) 40%,transparent 72%)}

/* Every ember its own size, height of travel, and pair of periods. */

/* THE GLITTER, AND IT IS NOT A SMALLER EMBER. A mote does not travel: it hangs
   in the dark and catches the light, on a period short enough to read as a
   glint rather than a pulse. Embers rise and go out; glitter stays and blinks.
   Given the same motion they would have been the same thing twice. */
.mote:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;width:1px;height:1px;border-radius:50%;
  background:#F2DCA6;opacity:0;
  box-shadow:0 0 2px .5px rgba(242,220,166,.45);
  animation:mote-glint var(--twin) ease-in-out var(--twin-in) infinite}

/* EACH ONE NUMBERED IN THE MARKUP RATHER THAN COUNTED IN THE SHEET.
   The motes were keyed on `:nth-of-type`, which counts every `div` in the
   field and not every mote -- so `mote:nth-of-type(1)` matched an EMBER,
   every mote's `--twin` stayed unset, `animation:mote-glint var(--twin)`
   was invalid, and the glitter never ran at all. Nothing said so: an
   animation with an unresolvable duration is simply no animation. Keyed on
   its own class now, which also means nothing between them can renumber
   them. */
.mote-1:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:4.76%;top:29.57%;--twin:6.8s;--twin-in:-0.7s;--peak:0.331}
.mote-2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:23.6%;top:52.77%;--twin:8.5s;--twin-in:-2.3s;--peak:0.45}
.mote-3:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:70.94%;top:91.33%;--twin:5.9s;--twin-in:-0.9s;--peak:0.401}
.mote-4:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:80.39%;top:25.66%;--twin:5.3s;--twin-in:-2.7s;--peak:0.362}
.mote-5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:94.98%;top:31.64%;--twin:5.8s;--twin-in:-4.4s;--peak:0.323}
.mote-6:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:52.75%;top:29.88%;--twin:4.5s;--twin-in:-2.8s;--peak:0.348}
.mote-7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:63.43%;top:12.06%;--twin:3.8s;--twin-in:-0.2s;--peak:0.226}
.mote-8:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:94.73%;top:41.92%;--twin:5.6s;--twin-in:-0.1s;--peak:0.305}
.mote-9:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:58.46%;top:22.89%;--twin:3.7s;--twin-in:-1.9s;--peak:0.261}
.mote-10:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:50.13%;top:19.89%;--twin:4.1s;--twin-in:-0.6s;--peak:0.328}
.mote-11:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:36.82%;top:73.72%;--twin:3.6s;--twin-in:-2.2s;--peak:0.46}
.mote-12:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:79.44%;top:12.37%;--twin:4.4s;--twin-in:-2.8s;--peak:0.192}
.mote-13:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:61.12%;top:56.27%;--twin:4.3s;--twin-in:-1.2s;--peak:0.21}
.mote-14:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:77.56%;top:19.78%;--twin:6.7s;--twin-in:-3.5s;--peak:0.194}

.ember-1:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:2.72%;--d:1.23px;--rise:23.3s;--rise-in:-2.5s;--sway:10.5s;--sway-in:-10.2s;--peak:0.282;--amp:33.5px}
.ember-2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:6.76%;--d:1.72px;--rise:21.4s;--rise-in:-19.4s;--sway:8.7s;--sway-in:-1.8s;--peak:0.265;--amp:10.1px}
.ember-3:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:11.01%;--d:1.67px;--rise:30.5s;--rise-in:-16.5s;--sway:5.7s;--sway-in:-1.3s;--peak:0.544;--amp:9.7px}
.ember-4:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:15.03%;--d:2.7px;--rise:26.3s;--rise-in:-20.8s;--sway:6.1s;--sway-in:-0.1s;--peak:0.3;--amp:33.7px}
.ember-5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:21.26%;--d:2.01px;--rise:30.6s;--rise-in:-11.6s;--sway:7.1s;--sway-in:-0.5s;--peak:0.295;--amp:11.6px}
.ember-6:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:25.62%;--d:2.07px;--rise:27.3s;--rise-in:-20.4s;--sway:11.3s;--sway-in:-6.6s;--peak:0.492;--amp:16.8px}
.ember-7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:29.44%;--d:2.06px;--rise:18.8s;--rise-in:-1.1s;--sway:8.1s;--sway-in:-7.1s;--peak:0.593;--amp:13.3px}
.ember-8:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:34.07%;--d:2.08px;--rise:29.7s;--rise-in:-24.9s;--sway:5.5s;--sway-in:-3.0s;--peak:0.489;--amp:29.1px}
.ember-9:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:38.64%;--d:2.07px;--rise:17.9s;--rise-in:-16.7s;--sway:8.5s;--sway-in:-0.7s;--peak:0.61;--amp:21.5px}
.ember-10:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:41.7%;--d:1.74px;--rise:21.3s;--rise-in:-3.1s;--sway:9.0s;--sway-in:-5.5s;--peak:0.569;--amp:31.2px}
.ember-11:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:47.99%;--d:2.66px;--rise:28.1s;--rise-in:-25.7s;--sway:9.9s;--sway-in:-2.3s;--peak:0.431;--amp:34.3px}
.ember-12:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:52.62%;--d:2.48px;--rise:26.5s;--rise-in:-2.6s;--sway:9.3s;--sway-in:-4.6s;--peak:0.589;--amp:29.9px}
.ember-13:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:56.56%;--d:1.44px;--rise:19.0s;--rise-in:-10.0s;--sway:5.6s;--sway-in:-0.7s;--peak:0.43;--amp:35.4px}
.ember-14:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:62.48%;--d:2.37px;--rise:17.9s;--rise-in:-16.2s;--sway:8.0s;--sway-in:-1.7s;--peak:0.348;--amp:23.7px}
.ember-15:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:65.52%;--d:2.23px;--rise:20.1s;--rise-in:-11.4s;--sway:7.6s;--sway-in:-4.9s;--peak:0.494;--amp:34.1px}
.ember-16:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:70.34%;--d:2.73px;--rise:24.4s;--rise-in:-17.0s;--sway:7.0s;--sway-in:-3.5s;--peak:0.436;--amp:18.6px}
.ember-17:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:74.36%;--d:1.87px;--rise:23.6s;--rise-in:-19.5s;--sway:8.3s;--sway-in:-8.3s;--peak:0.407;--amp:35.6px}
.ember-18:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:81.18%;--d:2.67px;--rise:19.4s;--rise-in:-14.0s;--sway:7.5s;--sway-in:-5.5s;--peak:0.236;--amp:29.4px}
.ember-19:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:84.92%;--d:1.95px;--rise:17.4s;--rise-in:-17.1s;--sway:10.9s;--sway-in:-9.1s;--peak:0.58;--amp:37.2px}
.ember-20:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:89.95%;--d:1.99px;--rise:19.0s;--rise-in:-4.1s;--sway:10.4s;--sway-in:-10.3s;--peak:0.596;--amp:26.5px}
.ember-21:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:94.48%;--d:1.95px;--rise:26.3s;--rise-in:-17.8s;--sway:8.1s;--sway-in:-1.9s;--peak:0.467;--amp:35.7px}
.ember-22:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:96.63%;--d:1.27px;--rise:28.8s;--rise-in:-4.9s;--sway:11.2s;--sway-in:-6.7s;--peak:0.531;--amp:15.2px}

@keyframes ember-rise{
  from{transform:translate3d(0,0,0)}
  to{transform:translate3d(0,-112vh,0)}}

/* The sway is a full there-and-back, so the ember is travelling sideways at
   every height rather than arriving and stopping. */
@keyframes ember-sway{
  0%,100%{transform:translate3d(calc(var(--amp) * -.5),0,0)}
  50%{transform:translate3d(calc(var(--amp) * .5),0,0)}}

/* LIT, THEN OUT, over the length of the rise -- the same clock, so an ember
   always dies at the top and never blinks out halfway up. It also carries the
   brightness wobble, because a coal does not burn evenly. */
@keyframes ember-live{
  0%{opacity:0;filter:brightness(.7)}
  7%{opacity:var(--peak,.2)}
  38%{filter:brightness(1.2)}
  62%{opacity:var(--peak,.2);filter:brightness(.85)}
  88%{opacity:0}
  100%{opacity:0;filter:brightness(.7)}}

@keyframes mote-glint{
  0%,64%,100%{opacity:0;transform:scale(.6)}
  78%{opacity:var(--peak,.3);transform:scale(1)}}

/* A THING THAT DOES NOT MOVE IS STILL A THING THAT IS LIT. The field holds its
   embers at a low steady glow rather than going dark, which is the candles'
   own rule at the foot of the page. */
@media (prefers-reduced-motion:reduce){
  .ember:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
  .ember > i:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none;opacity:calc(var(--peak,.2) * .8);
    transform:translate3d(0,-38vh,0)}
  .mote:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none;opacity:calc(var(--peak,.3) * .7)}
}

/* ==========================================================================
   THE EXECUTIVE LINE -- the page's figures, said once, small.

   Owner: "we dont need the tombstones on the top ... just nice simple headers
   with executive reports small clean".

   It replaces `.totals`, which gave every figure a bay of its own with a 34px
   numeral in it and a note under that. Five of those is a band about 96px
   deep across the full width, before the screen has said anything -- on a
   laptop it put the roll, the log and the operatives board below the fold on
   four different screens.

   ONE ROW, READING SIZE, TABULAR FIGURES so the columns of digits still line
   up against each other the way the bays used to. The note each bay printed
   underneath is on `title` now: a fact worth having when you go looking and
   not worth 30px of every page when you are not. The separators are the
   sheet's own hairline, not borders on the cells, so the first and last cell
   sit flush with the page's gutter.
   ========================================================================== */
.exec:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-wrap:wrap;align-items:baseline;
  gap:var(--s3) 0;padding:var(--s4) var(--s7);
  border-bottom:1px solid var(--hair-3)}

.exec-cell:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:baseline;gap:var(--s3);
  padding:0 var(--s6);border-left:1px solid var(--hair-3)}
.exec-cell:first-child:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-left:0;border-left:0}

.exec-k:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);
  letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--ink-5);
  white-space:nowrap}

.exec-v:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-4);font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--ink-2);white-space:nowrap}

/* ON A PHONE THE SEPARATORS GO AND THE CELLS WRAP INTO A BLOCK. A vertical
   rule between two cells that have wrapped onto different lines points at
   nothing. */
@media (max-width:720px){
  .exec:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s2) var(--s5);padding:var(--s4) var(--s5)}
  .exec-cell:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:0;border-left:0}
}

/* ==========================================================================
   A FIRE BURNING SOMEWHERE OUT OF SIGHT.

   Owner: "the background is this gold orange green beautioful thing goin on
   can we add a little movement to it? like a glowing gothic fire burning in
   the distance slowloy changing the color of the glas a little?"

   The ground is three static radial gradients on `body` -- a green wash from
   above, an amber one from below, a cool one off to the left. They are good
   and they do not move. This puts TWO more behind everything, very large and
   very faint, on periods long enough that nothing ever appears to start or
   stop: the amber one swells from below like a forge door opening somewhere
   past the end of the hall, and the green one breathes across it from the
   other side. Where they cross, the tint of the whole page shifts a few
   degrees and comes back.

   THE COLOUR CHANGES BY CROSS-FADE AND NOT BY hue-rotate. A filter would tint
   the text and the gold rules along with the air, which is the opposite of
   subtle; two coloured layers breathing against each other on different clocks
   change the MIXTURE, and only the mixture. It also costs nothing: opacity and
   transform are the two things a compositor does without repainting, so this
   animates on the GPU and never touches layout.

   PERIODS CHOSEN NOT TO AGREE. 71s and 94s share no useful factor, so the pair
   takes over an hour to return to the same relative phase and the eye never
   finds the loop. Both sit on `.ember-field`'s own pseudo-elements rather than
   on a new element: it is already the fixed, click-through, z-index 0 layer
   behind every screen, and a second one would be a second thing to keep in
   step with it.
   ========================================================================== */
.ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before,
.ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";position:absolute;pointer-events:none;
  will-change:transform,opacity}

/* THE FORGE, low and wide, from below.

   MEASURED AND MOVED. `bottom:-30%;height:95%` put this layer's bottom edge
   1.3 screens below the fold, and the gradient's centre is ON that edge -- so
   the only part of it anyone ever saw was its transparent outer fringe. It is
   drawn up to sit just under the foot of the page now, and carries half again
   the light it did, so the warm ground is a thing on the screen rather than a
   thing off it. */
.ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{left:-20%;right:-20%;bottom:-12%;height:82%;
  background:radial-gradient(ellipse 58% 96% at 50% 100%,
    rgba(214,138,46,.105) 0%,rgba(196,112,34,.05) 38%,transparent 73%);
  animation:forge-breathe 41s ease-in-out infinite}

/* AND THE COLD LIGHT IT IS SEEN AGAINST, off the other shoulder. */
.ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{left:-30%;right:-10%;top:-25%;height:105%;
  background:radial-gradient(ellipse 55% 85% at 32% 8%,
    rgba(63,140,102,.10) 0%,rgba(46,104,80,.05) 42%,transparent 74%);
  animation:hall-breathe 59s ease-in-out infinite}

/* NOTHING MAJOR. Owner, on the first cut: "nothing major in the distant fire
   just simple lit subtle movement in the light change." It travelled 4% and
   swelled 14%, which reads as a thing moving across the room. The drift is
   down to a per cent and the swell to four, so what changes is the LIGHT and
   the movement is only what keeps the light from looking painted on. */
@keyframes forge-breathe{
  0%,100%{opacity:.58;transform:translate3d(-1%,0,0) scale(1)}
  38%    {opacity:1;  transform:translate3d(1%,-1%,0) scale(1.04)}
  67%    {opacity:.76;transform:translate3d(0,0,0) scale(1.02)}}

@keyframes hall-breathe{
  0%,100%{opacity:.86;transform:translate3d(1%,-1%,0) scale(1.03)}
  45%    {opacity:.46;transform:translate3d(-1%,1%,0) scale(1)}
  74%    {opacity:.68;transform:translate3d(0,0,0) scale(1.05)}}

/* STILL LIT WHEN NOTHING MAY MOVE, which is the candles' own rule and the
   embers'. The light is the point; the drift is the flourish. */
@media (prefers-reduced-motion:reduce){
  .ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{animation:none;opacity:.8}
  .ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{animation:none;opacity:.6}
}

/* ==========================================================================
   THE GLASS IN THE ARCHED HEADS, AND ONLY ITS COLOUR MOVES.

   The frame draws two gothic arches across the top of the page with a gold
   wash behind them. These two lie over that wash, one warm and one cold, and
   cross-fade on 83s and 109s -- periods with no useful common factor, so the
   pair is over forty minutes from repeating a mixture. Opacity ONLY: no
   transform, no filter, nothing that could move a line of the frame or tint
   the type under it. The arch simply reads amber for a while, then a little
   green, the way glass does with a fire somewhere behind it.
   ========================================================================== */
.glass-lit:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:26px;right:26px;top:0;height:140px;
  pointer-events:none;will-change:opacity}

.glass-warm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:radial-gradient(ellipse 90% 100% at 50% 0,
    rgba(222,158,64,.085) 0%,rgba(196,124,40,.035) 45%,transparent 76%);
  animation:glass-warm-lit 83s ease-in-out infinite}

.glass-cold:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:radial-gradient(ellipse 80% 100% at 44% 0,
    rgba(84,168,126,.062) 0%,rgba(52,120,92,.026) 48%,transparent 78%);
  animation:glass-cold-lit 109s ease-in-out infinite}

@keyframes glass-warm-lit{0%,100%{opacity:.35}41%{opacity:1}72%{opacity:.6}}
@keyframes glass-cold-lit{0%,100%{opacity:.9}37%{opacity:.25}68%{opacity:.62}}

@media (prefers-reduced-motion:reduce){
  .glass-warm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none;opacity:.7}
  .glass-cold:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none;opacity:.5}
}

/* ==========================================================================
   ONE SCREEN GIVING WAY TO THE NEXT, ON THE DESIGNER'S CLOCK.

   It shipped on the owner's first guess -- "fade out for over .3 sec blank for
   .1 sec then fade in new view over .3 sec?" -- which is 700ms of transition
   between two tabs. He then asked the designer what the motion should be and
   sent the answer back:

     "Between tabs -- a hard cut with a settle. The topbar never moves. The old
      screen drops 6px and fades out over 120ms; the new one arrives from 6px
      below, fading in over 220ms on --ease-settle. Sections don't animate
      individually -- the whole main moves as one slab, like a plate being
      swapped into a frame. Reduced motion -- all of it collapses to an opacity
      crossfade of 120ms, or nothing."

   SO 340ms WHERE THERE WERE 700, and a 6px move where there was none. It is
   the faster clock that makes the slab read as a slab: at 300ms a fade is a
   dissolve and the eye waits it out; at 120 it is a cut with a settle behind
   it, which is the difference between changing a page and changing a plate.
   go() holds the clock -- 120 out, then the swap, then 220 of arrival.

   TRANSFORM AND OPACITY, WHICH COMPOSITE. Six pixels is small enough that no
   scrollbar, sticky filter bar or fixed console has to be reasoned about, and
   nothing here can touch layout on six thousand nodes.

   THE TOPBAR IS NOT IN THIS, as the note says. The class goes on the scroller,
   which holds every route and nothing else -- the strip across the top, the
   quote at the foot and the servo skull all stay lit while the middle changes.
   ========================================================================== */
.screen-going:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){opacity:0;transform:translate3d(0,6px,0);
  transition:opacity .12s ease,transform .12s var(--ease-settle)}
.screen-coming:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:screen-in .22s var(--ease-settle) both}
@keyframes screen-in{from{opacity:0;transform:translate3d(0,6px,0)}
  to{opacity:1;transform:none}}

/* "all of it collapses to an opacity crossfade of 120ms, or nothing." go()
   does not start the clock at all under this preference, so these are belt and
   braces for a reader who changes the setting mid-transition -- and no
   transform in either, because the move is the part that is being asked for. */
@media (prefers-reduced-motion:reduce){
  .screen-going:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){opacity:1;transform:none;transition:none}
  .screen-coming:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
}

/* THE ROSTER'S STANDING LINE. It was three arched plates; it is the `.exec`
   strip every other screen wears, so this section needs nothing of its own but
   the space between it and the roll beneath. */
.r-standing:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .exec{border-bottom:0;padding-left:0;padding-right:0}
.r-standing:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .exec-cell .exec-v{font-family:var(--serif);font-size:var(--fs-6);
  font-weight:600;letter-spacing:.02em;color:var(--gold-warm)}

/* ==========================================================================
   THE MEN ON A REPORT, NAMED.

   Owner: "reports I want to see the names of the men like i used to. with
   their discord icons. JUST the names and discord."

   The cell drew up to six overlapping initial circles and a number, which
   answers "how many" and never "who". It is a short column of face-and-name
   now, four at most, the rest said as a figure. A name that will not fit
   ellipsises rather than wrapping, because a row of this list is one line of
   fact per man and a wrapped name turns the whole table ragged.
   ========================================================================== */
.rep-men:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s2);min-width:0;
  padding:var(--s3) 0}

.rep-man:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s3);min-width:0}

.rep-man-name:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-3);color:var(--ink-3);min-width:0}

.rep-man-more:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-left:calc(22px * var(--ui) + var(--s3))}

/* ==========================================================================
   "S t a t s   p a g e", ONE LETTER PER LINE.

   The filing's what-to-send list borrows `.kv`, whose columns are
   `128px | minmax(0,1fr) | auto`: a key, a value, and whatever sits at the
   end. That is right for a key and a value. It is wrong for THIS row, which is
   a number, a short name and a long sentence -- because `auto` on the last
   column takes as much as the sentence wants, `minmax(0,1fr)` is allowed to
   collapse to nothing, and "Stats page" was squeezed to a single character
   wide and wrapped down the page one letter at a time.

   So the row states its own shape: the number is as wide as a number, the name
   takes what it needs, and the SENTENCE is the part that gives. Nothing else
   on the site uses `.kv` this way, which is why this is a second class rather
   than a change to the first.
   ========================================================================== */
.kv-need:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:calc(24px * var(--ui)) max-content minmax(0,1fr)}
.kv-need:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .meta{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

@media (max-width:720px){
  /* A phone has no room for the sentence beside the name; it goes under it. */
  .kv-need:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:calc(24px * var(--ui)) minmax(0,1fr)}
  .kv-need:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .meta{grid-column:2;white-space:normal}
}

/* ==========================================================================
   AND THE KEYS THAT WERE BELOW THE FOOTER.

   `.filing-foot` is the wizard's own bar -- cancel, back, and the seal that
   goes on. It sat in ordinary flow at the end of the screen, so on a filing
   with a few pictures in it the keys were further down the page than the quote
   strip and the candles, which are page furniture and sit at the bottom of the
   window. The owner's words: "need to fix the bubvmission page the buttons are
   below the footer."

   Stuck to the foot of the scroller instead. It already carried an opaque
   ground and a lit top edge -- it was drawn to be a bar and was simply never
   told to stay -- so this is one declaration and a z-index above the sheet it
   floats over.
   ========================================================================== */
.filing-foot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:sticky;bottom:0;z-index:5}

/* ==========================================================================
   THE GLOW WANDERS, AND NOW IT CAN BE SEEN DOING IT.

   Owner, twice: "the moving fire in the background isnt enough. I wanna see
   that orange glow shift around a little bit random its not doing that", and
   then "you told me you could make the background look like there was a fire
   in the background being blured by glass. I want the organge glow to shft and
   move."

   HE WAS RIGHT AND IT WAS NOT A MATTER OF TASTE. Two frames of the live page
   twenty-six seconds apart differ, over the whole background, by a MEAN OF
   UNDER ONE UNIT IN 255 -- and most of that is the embers, which are three
   pixels each. The animation ran the whole time; `document.getAnimations()`
   said running for every layer. Nothing was broken. The light simply was not
   there to move:

     * every warm layer put its gradient CENTRE below the bottom of the window
       -- this one's was at 135% of the viewport height -- so the only part of
       any of them on screen was the transparent skirt, and
     * the brightest stop was .105 alpha, which over a #0A110E ground is about
       two units of red at its peak and nothing at all by the time it reaches
       the fold.

   A tenth of a per cent of orange, moving, is still a tenth of a per cent.

   SO: THE CORE COMES INSIDE THE GLASS. The hotspot now sits around four fifths
   of the way down the window and peaks at .22, which composites to a warm
   (60,48,24) against the ground -- a glow you can point at. It is still a
   gradient with an eighty-per-cent falloff, so there is no edge anywhere in
   it; what you see is light through glass, not a shape behind it.

   THEN TONED BACK. Owner, on the first version that could be seen at all:
   "too much orange. tone it down just a little and give it more motion." The
   peak came off by a third -- .22 to .14 behind the page, .055 to .034 in
   front -- and every clock was roughly halved while the travel grew by half
   again. Less light, moving more, which is the right trade: what made the old
   one read as painted on was never its brightness.

   AND IT WANDERS BY SUM, WHICH IS WHY IT DOES NOT REPEAT. Two nested elements
   drift on 53s and 37s in different directions -- a transform on the outer, a
   transform on its own pseudo-element -- so the centre of the orange is the
   sum of two travels and swings across better than a third of the window.
   Brightness runs on a third clock at 29s. 53, 37 and 29 are all prime: the
   three do not return to the same relative phase for a day and a half, which
   is the difference between a fire and a lamp on a track. One keyframe on one
   element, however long its period, is a loop -- and a loop is the one thing
   the eye is good at catching.

   STILL FREE. Only `transform` and `opacity` animate, which a compositor does
   without repainting; the layer is `position:absolute` inside the fixed,
   click-through ember field, so nothing here can touch layout or the hit test.
   ========================================================================== */
.ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){isolation:isolate}

.ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .forge-wander{position:absolute;left:-30%;right:-30%;top:-15%;bottom:-25%;
  pointer-events:none;will-change:transform;
  animation:forge-wander 53s ease-in-out infinite}

.ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .forge-wander::before{content:"";position:absolute;inset:0;
  pointer-events:none;will-change:transform,opacity;
  background:radial-gradient(ellipse 34% 40% at 50% 72%,
    rgba(236,158,58,.14) 0%,rgba(212,118,30,.072) 34%,
    rgba(148,72,16,.028) 60%,transparent 80%);
  animation:forge-drift 37s ease-in-out infinite,
            forge-flare 29s ease-in-out infinite}

/* IRREGULAR STOPS ON PURPOSE. Evenly spaced keyframes read as a metronome
   whatever the duration; 17/41/63/86 does not. */
@keyframes forge-wander{
  0%  {transform:translate3d(-15%,3%,0) scale(1)}
  17% {transform:translate3d(11%,-2%,0) scale(1.09)}
  41% {transform:translate3d(-8%,5%,0) scale(1.02)}
  63% {transform:translate3d(17%,-1%,0) scale(1.13)}
  86% {transform:translate3d(-12%,-3%,0) scale(1.04)}
  100%{transform:translate3d(-15%,3%,0) scale(1)}}

/* THE SECOND TRAVEL, CROSSWISE AND ON A SHORTER CLOCK. Its stops are chosen
   not to land on the first animation's -- 22/48/71/91 against 17/41/63/86 --
   so the two are never briefly still together. */
@keyframes forge-drift{
  0%  {transform:translate3d(9%,-5%,0)}
  22% {transform:translate3d(-11%,3%,0)}
  48% {transform:translate3d(6%,7%,0)}
  71% {transform:translate3d(-8%,-6%,0)}
  91% {transform:translate3d(12%,2%,0)}
  100%{transform:translate3d(9%,-5%,0)}}

@keyframes forge-flare{
  0%,100%{opacity:.42}
  23%    {opacity:1}
  52%    {opacity:.6}
  79%    {opacity:.88}}

/* THE SAME FIRE, ON THE NEAR SIDE OF THE GLASS.

   The ember field is fixed at z-index 0, BEHIND every plate, so however bright
   the wander gets it can only ever show in the gutters -- and a 1,720px page
   in a wide window is mostly plate. Half the light was landing where nobody
   was looking.

   SO THE LIGHT IS DRAWN TWICE, once behind the page and once in front of it.
   The front copy is a click-through veil at a twentieth of the alpha: enough
   that the pane goes a degree warmer where the fire happens to be, not enough
   to lift a hairline or fog a word. It drifts on 47s and 31s -- two more
   primes, sharing no phase with the 53/37/29 behind it -- so the near light
   and the far light never quite agree about where the fire is, which is what
   a window does to one.

   z-index 8: over the routes and the topbar, UNDER the lightbox at 70 and the
   tooltip at 90. Light falls on the page; it does not fall on an open piece of
   evidence or on something being explained. No blend mode, and nothing here is
   painted per row -- one composited layer moving on transform and opacity. */
.forge-glass:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:fixed;inset:0;z-index:8;pointer-events:none;overflow:hidden;
  animation:glass-fire-a 47s ease-in-out infinite}

.forge-glass:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > i{position:absolute;left:-25%;right:-25%;top:-20%;bottom:-20%;display:block;
  will-change:transform,opacity;
  background:radial-gradient(ellipse 36% 44% at 50% 74%,
    rgba(238,162,62,.034) 0%,rgba(214,120,32,.017) 36%,
    rgba(150,74,18,.007) 62%,transparent 82%);
  animation:glass-fire-b 31s ease-in-out infinite,
            forge-flare 29s ease-in-out infinite}

@keyframes glass-fire-a{
  0%  {transform:translate3d(-13%,2%,0) scale(1)}
  19% {transform:translate3d(10%,-3%,0) scale(1.08)}
  44% {transform:translate3d(-6%,5%,0) scale(1.01)}
  68% {transform:translate3d(15%,-1%,0) scale(1.11)}
  88% {transform:translate3d(-9%,-2%,0) scale(1.04)}
  100%{transform:translate3d(-13%,2%,0) scale(1)}}

@keyframes glass-fire-b{
  0%  {transform:translate3d(8%,-4%,0)}
  26% {transform:translate3d(-10%,5%,0)}
  53% {transform:translate3d(5%,6%,0)}
  77% {transform:translate3d(-7%,-5%,0)}
  100%{transform:translate3d(8%,-4%,0)}}

/* STILL LIT WHEN NOTHING MAY MOVE, which is the candles' rule and the embers'.
   The light is the point; the wandering is the flourish. */
@media (prefers-reduced-motion:reduce){
  .forge-glass:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
  .forge-glass:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > i{animation:none;opacity:.72}
  .ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .forge-wander{animation:none}
  .ember-field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .forge-wander::before{animation:none;opacity:.72}
}

/* ==========================================================================
   ONE LIST, AND IT IS A PANE OF GLASS SET INTO THE PLATE.

   Owner: "I want 1 single list style as well. this is not it. supposed to look
   like glass. little more sunken in tiny dividers between the names lighter
   dividers."

   THERE WERE TWO LOOKS AND HE COULD SEE BOTH. Every row carried a stripe --
   two per cent of white on the even ones -- which on the roster reads as
   banding and on Reports reads as slabs, because a report's row is two and
   three lines tall where a brother's is one. The same declaration, two
   different lists. It is gone. What separates a row from the one under it is
   a LINE now, on all four rolls, and the line is what he asked to be able to
   see: one pixel at nine per cent of the pale green where it was six, which
   is visible against the glass and still weighs nothing beside the type.

   GLASS IS A SURFACE WITH LIGHT ON IT, not a transparency. Three things make
   it and none of them is a blur: the pane is darker and greener than the plate
   it sits in; it carries a sheen down its first sixth and a shadow up its
   last, so the eye reads a curved face rather than a flat hole; and the recess
   is deeper than it was -- fourteen pixels of shadow falling from the lip
   instead of nine, with the lip itself a hard dark line and a thread of gold
   caught on its edge. Sunken, so the rows sit BEHIND the plate's face.

   ROWS ARE STILL NOT CARDS. No radius, no outline, no shadow per row -- five
   hundred shadows is five hundred objects, and this is one object with rows
   in it. The hover lift is the one exception and it lasts as long as the hand.
   ========================================================================== */
/* ==========================================================================
   SMOKED GLASS, AND ONE PANE FOR EVERY LIST ON THE SITE.

   Owner: "in fact reports roster and battle honors all looked fucked they need
   ot look like glass they have a funny mesh behind them,mm glear smoked glass
   please. and gie them all some depth..."

   THE MESH IS THE PHOSPHOR AND IT IS NOT BEHIND THEM. `.phosphor` is the CRT
   wash -- a 1px-on, 2px-off scanline at 5.5% black, fixed at z-index 60 over
   the entire app. Over type and over a plate face it is invisible, which is
   what it was tuned against; over a large dark READING SURFACE it stops being
   a wash and starts being a weave, and that is the only place on the site that
   has one. It is taken down to 2.8% below, which is still a screen and is no
   longer a fabric.

   AND THE PANE IS GLASS RATHER THAN A HOLE. What it had was `--recess-bg`, a
   flat three-stop dark gradient: a recess, correctly, but a recess cut into
   the plate rather than a sheet laid over it. Smoked glass is four things and
   none of them is a texture --

     the GROUND is dark and translucent, so the fire behind the page comes
     through it rather than stopping at it -- and only just dark, because
     "clear" is the word and a murky pane is not glass, it is a filter;
     the LIP is a hard dark line across the top with a short shadow falling off
     it, which is what the underside of an edge looks like from below;
     the FAR EDGE is LIT, one pixel of pale green along the foot, because in a
     recess the light that misses the near wall lands on the far one;
     and nothing casts OUTWARD. That was the first cut of this and it was
     wrong: a shadow under the pane lifts it onto the plate, and owner --
     "when i say depth i mean then need to be sunken in glass clear clean."
     Sunken is the light on the far edge and the shadow on the near one, which
     is the same pair a raised thing wears the other way up.

   WRITTEN `.ledger .roll`, WHICH IS EVERY ROLL. The last version of this named
   four hosts and one of them -- `.reports` -- matched nothing, so Reports had
   the old recess while the Roster had the new pane and the two screens
   disagreed. Naming no host cannot make that mistake twice, and one list style
   everywhere is what he asked for to begin with. (0,2,0) to answer ledger.css's
   own `.ledger .roll` at the same weight, from the later sheet.
   ========================================================================== */
.ledger .roll{position:relative;
  background:linear-gradient(180deg,
    rgba(4,10,8,.50) 0,
    rgba(9,19,15,.30) 8%,
    rgba(9,19,15,.26) 88%,
    rgba(4,10,8,.42) 100%);
  border:1px solid rgba(190,214,190,.08);
  border-top-color:rgba(0,0,0,.50);
  border-radius:var(--r2);
  box-shadow:
    inset 0 1px 0 0 rgba(0,0,0,.62),
    inset 0 3px 12px -3px rgba(0,0,0,.72),
    inset 0 -1px 0 0 rgba(226,240,230,.07),
    inset 0 -10px 18px -16px rgba(0,0,0,.40)}

/* THE SCANLINE COMES OFF THE READING SURFACES. Half of what it was, which on
   a plate face is still a screen and on a list is no longer a weave. */
.phosphor:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:
    repeating-linear-gradient(180deg,rgba(0,0,0,.028) 0 1px,transparent 1px 3px),
    radial-gradient(120% 100% at 50% 50%,transparent 55%,rgba(0,0,0,.38) 100%)}

/* THE DIVIDER HE ASKED TO SEE. ledger.css draws it at `--edge-line`, six per
   cent, which against the deeper pane is nothing at all; nine reads as a line
   without reading as a rule. Written `.ledger :is(.cmp,.roll) .rail` because
   that is the weight of the rule it answers (0,3,0) and this sheet is loaded
   after it -- the colour alone is taken, the padding beside it is left. */
.ledger :is(.cmp,.roll) .rail{border-bottom-color:rgba(190,214,190,.09)}

/* AND NOT `:last-child`, WHICH TOOK THE LINE OFF EVERY ROSTER ROW.
   Owner: "reports correctly has lines between entries roster does not."
   Measured, and the two lists are not the same shape underneath: a report is
   `.roll > .rail.rep-row`, a brother is `.roll > .roll-item > .rail.roll-row`.
   So every brother is an ONLY child of his own wrapper, every brother matched
   `:last-child`, and 629 dividers went transparent while the Reports lost
   exactly the one it was meant to. The rule is gone rather than taught about
   wrappers: the pane has its own dark foot, and a hairline sitting on it was
   never visible enough to be worth a selector that can do this. */

/* AND THE INDENT THE NUMBER USED TO MAKE. Owner: "delete the number column on
   the left. after you do that there wont be padding so add some padding in
   place of the numbers". The track and its column gap are written back as left
   padding -- the same arithmetic at both widths, so the head row and every
   body row stay on one line as they did. (0,3,0) to answer ledger.css's own
   `.ledger :is(.cmp,.roll) .rail` padding at the same weight, from the later
   sheet; the narrow rule repeats the selector so the container query still
   wins inside itself. */
.ledger :is(.cmp,.roll) .roll-row{padding-left:calc(var(--s7) + 30px*var(--ui) + var(--s5))}
@container ledger (max-width:720px){
  .ledger :is(.cmp,.roll) .roll-row{padding-left:calc(var(--s7) + 24px*var(--ui) + var(--s4))}
}

/* THE COLUMN HEADS ARE THE LIP OF THE RECESS. */
.roll-hdr:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:linear-gradient(180deg,rgba(0,0,0,.38),rgba(0,0,0,.14));
  box-shadow:inset 0 -1px 0 0 rgba(201,164,92,.18)}

/* AND A ROW UNDER THE HAND COMES UP OUT OF IT. The lift is a light edge at the
   top and a shadow beneath, which is what "raised" means; a background change
   alone reads as a highlight, not as a surface. */
.roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .roll-row.rail-act:hover,
.roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .rep-row.rail-act:hover,
.roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .roll-item > .roll-row.rail-act:hover{
  background:linear-gradient(180deg,rgba(226,240,230,.075),rgba(226,240,230,.028));
  box-shadow:inset 0 1px 0 0 rgba(226,192,121,.18),
             0 3px 12px -5px rgba(0,0,0,.7);
  position:relative;z-index:1}

/* ==========================================================================
   THE REPORTS HEAD, AS THE OWNER DREW IT.

   His mock-up, against what the screen had: a magnifier in the search field;
   + NEW AAR as a filled gold key rather than an outline; the tiles in a boxed
   band with the ACTIVE one lit, a dot beside its label and a line under its
   figure; and the filter row led by the word SHOW with its set chip reading
   STATE · AWAITING WORD rather than STATE.

   THE TILES ARE A CONTROL NOW, so they are drawn as one: a hand over them
   lifts, the lit one carries the gold, and the one that is not a door (seals
   recorded, which counts two states at once) takes no pointer at all.
   ========================================================================== */
/* THE SEARCH FIELD WAS FOUR HUNDRED AND NINETY-FIVE PIXELS OF NOTHING.

   `flex:1 1 auto` on the wrapper and `width:100%` on the field inside it meant
   the search took every pixel the heading and the two keys did not -- measured
   at 495px on Reports and 536px on the roster, against the 220px the design
   draws. A field five times wider than the longest thing anyone types in it
   reads as an empty slab, and the magnifier ends up marooned at the far end of
   it. The wrapper carries the width now and the title takes the slack, which
   is what the reference does; the mark sits in the field's own padding. */
.field-search:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:flex;align-items:center;flex:0 1 auto;
  width:calc(268px * var(--ui));min-width:calc(150px * var(--ui))}
.field-search:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .field-find{width:100%;padding-left:calc(26px * var(--ui))}
.field-search-mark:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:calc(9px * var(--ui));pointer-events:none;
  font-size:var(--fs-4);color:var(--gold-dim);line-height:1}

/* AND THE FIELD HAS AN EDGE AGAIN. The recess work gave every `.field` a
   `--recess-line` border -- six per cent of pale green -- so on the page ground
   a field is a black rectangle with no perceptible boundary. The sunken
   treatment is right and stays; the border goes back to the gold hairline the
   design draws, which is what says "you may type here". (0,2,0) to answer
   `.ledger .field` in ledger.css at the same weight, from the later sheet. */
.ledger .field{border-color:var(--gold-line)}
.ledger .field:hover{border-color:var(--gold-dim)}

/* THE BAND IS A BOX, not five figures on the page ground. */
.tiles:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border:1px solid var(--hair-3);background:rgba(0,0,0,.16);
  box-shadow:inset 0 1px 0 0 rgba(226,192,121,.05)}

.tile:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;transition:background var(--t),box-shadow var(--t)}
.tile-act:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:pointer}
.tile-act:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)):hover{background:rgba(190,214,190,.04)}
.tile-act:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)):focus-visible{outline:1px solid var(--gold);outline-offset:-2px}

/* THE LIT ONE. A dot before the label is how this sheet marks a live state
   everywhere else, so the tile borrows it rather than inventing a marker. */
/* THE LIT TILE, AS THE DROP DRAWS IT ON ALL THREE SCREENS.

   Owner: "i REALLY need this consistency between ALL pages!!" -- with the
   Roster, Battle Honours and Reports heads from the drop side by side, and the
   same treatment on the lit tile in each: a gold line along the TOP edge and a
   warm fall beneath it. I had it, then replaced it with a rule under the
   figure on the strength of his earlier mock-up. Three pages agreeing beats
   one drawing, so it goes back, and it goes back on all three at once. */
.tile-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:linear-gradient(180deg,rgba(201,164,92,.11),rgba(201,164,92,.02) 72%);
  box-shadow:inset 0 1px 0 0 var(--gold)}
.tile-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .label{color:var(--gold-warm)}
.tile-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .tile-n{color:var(--gold-warm)}
.tile-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .label::before{content:"";display:inline-block;width:5px;height:5px;
  border-radius:50%;background:var(--gold-warm);margin-right:var(--s3);
  vertical-align:middle}

/* HIS OWN SHARE, under the figure. */
.tile-sub:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);color:var(--ink-5);
  letter-spacing:var(--ls-2)}

/* THE RANGE STANDS AT THE RIGHT OF THE ROW, WITH A DASH BETWEEN ITS ENDS.

   The drop puts the chips at the left margin of the chip row and ONE quiet
   thing at the right of it -- the range on Reports, a link on Battle Honours,
   where you stand on the Roster. It read "a range of your own" packed in
   against the chips, which is a caption in the middle of a row of controls.
   The caption is gone; the two fields are a pair now, held together by a dash
   and pushed to the far end where the eye expects them. */
.filt-range:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s3);margin-left:auto}
.filt-dash:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-5);font-family:var(--mono)}

/* THE TWO DATE FIELDS WERE THE BRIGHTEST THING ON THE SCREEN.

   Owner, on the Reports head: "search date field exec summary look etc".

   `<input type="date">` is a browser control, and Chrome paints its own: white
   mm/dd/yyyy at full contrast and a white calendar glyph, both louder than the
   chips beside them and the only two objects in the header wearing a different
   design. The old sheet had already answered this -- there is a
   `::-webkit-calendar-picker-indicator` filter at the top of this file -- but
   fence-old.mjs scoped that rule to everything OUTSIDE `.ledger`, so the one
   place it was needed was the one place it could not reach.

   THE PARTS ARE STYLED, THE CONTROL IS NOT REPLACED. `color-scheme:dark` makes
   the picker that drops out of it dark; the edit region goes to the same dim
   mono the placeholders use, so an empty field reads as a prompt rather than
   as a value; and the glyph is filtered to gold and held at just over half
   opacity until the hand is on it. A set range lights its own field, because
   a bound list should say so where the bound was made. */
.ledger .field-date{width:calc(132px * var(--ui));color-scheme:dark;
  font-family:var(--mono);font-size:var(--fs-3);letter-spacing:var(--ls-2)}
.ledger .field-date::-webkit-datetime-edit{color:var(--ink-5)}
.ledger .field-date:focus::-webkit-datetime-edit{color:var(--ink)}
.ledger .field-date:not([value=""])::-webkit-datetime-edit{color:var(--gold-warm)}
.ledger .field-date:not([value=""]){border-color:var(--gold)}
.ledger .field-date::-webkit-calendar-picker-indicator{opacity:.5;cursor:pointer;
  filter:invert(76%) sepia(34%) saturate(560%) hue-rotate(358deg) brightness(92%)}
.ledger .field-date::-webkit-calendar-picker-indicator:hover{opacity:1}

/* A BIGGER KEY FOR THE ONE ACT THIS SCREEN EXISTS TO START. */
.btn.btn-lg:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(38px * var(--ui));padding:0 var(--s7);
  font-size:var(--fs-4);letter-spacing:var(--ls-2)}

/* THE GOLD KEY WAS NOT GOLD, and it is a specificity fault rather than a
   missing rule. `.btn-gold` in ledger.css paints a gold gradient at (0,1,0);
   `.btn` in ledger-carry.css paints the ordinary dark one at the SAME weight
   and is loaded after it, so the later rule took every gold key on the site
   back to the default. Two classes here is (0,2,0) and settles it -- and it is
   worth noticing that the whole sheet is deliberately built at (0,1,0), which
   is what makes source order decide, and what made this silent. */
.btn.btn-gold:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:#6D5220;
  background:linear-gradient(180deg,var(--gold-lit),#A9832A);
  color:var(--ink-on-gold);
  box-shadow:inset 0 1px 0 rgba(255,236,180,.55),0 1px 0 var(--edge-dark)}
.btn.btn-gold:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)):hover{background:linear-gradient(180deg,#F0D48C,#BE9433)}

/* AND THE FILTER ROW THAT LEADS WITH A WORD STARTS AT THE LEFT. A centred row
   led by SHOW puts the word in the middle of the plate with nothing before it,
   which reads as a caption rather than as the head of a list. Only the row
   that carries the lead word -- the roster's bar has none and stays centred. */
.head-acts.head-acts-lead:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){justify-content:flex-start}

/* ==========================================================================
   THE COMPANY SHEET SHARES ITS WIDTH TOO, AND ITS HEAD ROW STOPS COLLIDING.

   Owner: "every column has this problem ... ALL need to be spaced with the dom
   size % not dedicated spaceing." He named the roster, reports and battle
   honours; this is the same fault on the screen he was looking at when he said
   "fis this right in settings".

   `.muster-roll` was `flex:0 0 330px`. At 1,720px of page that is a fifth of
   the sheet for the list of companies, and "8 places 792 aars 4,590 pts
   DISCORD" does not fit in it -- so the line wrapped and ran under the "93
   BROTHERS" block beside it. Not an overflow, which is why nothing measured as
   one: a flex child wrapping into the space under its neighbour.

   A SHARE, WITH A FLOOR. The roll may grow with the page and may not shrink
   past the width its own longest line needs, and the tree takes the rest.
   ========================================================================== */
.muster-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:0 1 clamp(calc(330px * var(--ui)), 26%, calc(560px * var(--ui)))}
.muster-one:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1 1 auto;min-width:0}

/* THE DESK CARD'S HEAD. A flex row of a title, a note and a holder count, and
   the count is `nowrap` -- so when the note is long the two meet. The note is
   the part that gives, as it is everywhere else on the site. */
.desk-card-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:0}
.desk-card-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .col,
.desk-card-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .grow{min-width:0}
.desk-card-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .meta,
.desk-card-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .mono{min-width:0;overflow:hidden;text-overflow:ellipsis}

/* ==========================================================================
   AND THE GAP ABOVE THE COMPANY SHEET.

   Settings' own `main.screen` holds ONE child -- the head plate -- because the
   company sheet is a route gate of its own that renders after it, shared with
   nothing now but reachable from two screens. `.screen` carries 250px of
   bottom padding to clear the console, and with the sheet following, that
   padding became a 250px hole between the strip and THE COMPANIES.

   The tail is for the LAST thing on the page. When something follows, it is
   not the last thing.
   ========================================================================== */
.screen.screen-tail-none:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-bottom:var(--s7)}

/* ==========================================================================
   THE FILING'S KEYS SIT UNDER THE PICTURES, NOT AT THE FOOT OF THE WINDOW.

   Owner: "please bring this next and cancel button up a lot under the
   pictures row... stupid down there..."

   Both of his complaints about this bar are the same measurement from
   opposite ends. It was in ordinary flow, so with a long filing the keys ended
   up BELOW the quote strip -- fixed by sticking them to the foot of the
   scroller. With a SHORT filing, two thumbnails and nothing else, sticky then
   pinned them to the bottom of the window with a thousand pixels of nothing
   between.

   Sticky does exactly the right thing in both cases IF the box it sticks
   inside is the height of its own content. `.screen` carries 250px of bottom
   padding to clear the console, which on every other screen is right and here
   made the wizard a full page tall whatever was in it. Take the tail off and
   sticky settles where the content ends -- and still catches the bar at the
   foot of the window the moment the filing is longer than the scroller.

   THE CONSOLE IS NOT AN ISSUE HERE. It is bottom-right and the wizard's keys
   run along a bar with its own opaque ground; the 250px was for a ROLL whose
   last rows would otherwise sit under the machine.
   ========================================================================== */
.ledger.filing .screen:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-bottom:var(--s7);
  /* AND IT DOES NOT GROW. The filing mounts its own full-height
     `.ledger page filing` column and the screen inside it was `flex:1 1 0%`,
     so it filled 1,190px whatever it held -- two thumbnails and a drop box
     ending at 228px, then nine hundred pixels of nothing, then the keys. That
     is the measurement behind "stupid down there". Content height, and sticky
     does the rest: under the pictures on a short filing, at the foot of the
     scroller on a long one. */
  flex:0 0 auto;align-self:start}

/* ==========================================================================
   THE QUOTES STAND THREE ABREAST.

   Owner: "quotes needs to be 3 columns ...".

   They were one column in a flex plate-body -- a hundred lines of one or two
   sentences each, run down a page that is 1,720px wide, which is a column of
   type about eleven words long beside two thirds of a screen of nothing.
   `.quote-grid` exists in the carried sheet and is an auto-fill of 280px
   minimums, which resolves to five or six columns at this width; he asked for
   three and three is a better measure for a sentence anyway.

   THREE, THEN TWO, THEN ONE, on the container and not the viewport, because
   this plate is as wide as the page and the page is what narrows.
   ========================================================================== */
.quote-cols:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;align-items:start;
  gap:var(--s6) var(--s7);grid-template-columns:repeat(3,minmax(0,1fr))}

@container ledger (max-width:1080px){
  .quote-cols:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:repeat(2,minmax(0,1fr))}
}
@container ledger (max-width:720px){
  .quote-cols:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,1fr)}
}

/* ==========================================================================
   THE AUDIT FILE SAYS WHAT AND TO WHOM, IN THAT ORDER, IN BOTH COLUMNS.

   Owner: "the audit log is not clear at all i like the layout but its hard to
   tell WHY action they performed to WHO and WHO performed what action to them
   make the who and what a lot more clear in both columns."

   THE LAYOUT IS RIGHT AND THE HIERARCHY WAS INVERTED. A row read: the date in
   ash, then a sentence at body size carrying a small state chip, then -- if
   the ledger could resolve him -- the other man as a quiet BUTTON at the far
   end of it. So the two facts a file is read for were the third thing and the
   fourth, one of them in the smallest type on the row and the other in the
   dimmest.

   They lead now. The act keeps its chip, because the colour is what says
   whether this was a demotion, a role gained or an ordinary change, and the
   other man is set in the display face in gold beside it -- the same weight
   the roster gives a brother's name, which is what he is. `by` and `to` are
   the preposition before him: this is a file with two panels and the panels
   differ by exactly that word, so it belongs on every line of both rather than
   once in a heading above fifty of them.

   WHAT HAPPENED IN DETAIL DROPS TO A SECOND LINE in the meta face -- the old
   value and the new, the roles moved, the reason somebody typed. It is the
   evidence for the headline, not the headline.

   THE DATE GOES TO THE RIGHT MARGIN, where a date is read down a column
   rather than in the middle of a sentence.
   ========================================================================== */
.audit-line:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:calc(3px * var(--ui))}

.audit-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:baseline;flex-wrap:wrap;
  gap:calc(4px * var(--ui)) var(--s4);min-width:0;width:100%}

.audit-act:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none}

/* (0,2,0), AND THE ONE PLACE IN THIS SHEET IT IS NEEDED.

   Measured on the live page: the name came out system-ui in ink while its
   WEIGHT and SIZE arrived. That split is the signature of a rule that sets
   only the other two -- `.ledger button,.ledger input{font-family:inherit;
   color:inherit}`, which is (0,1,1) against this sheet's deliberate (0,1,0),
   so it wins on specificity and hands the button its parent's face. Everything
   in here is written at (0,1,0) precisely so source order decides; a bare
   class cannot answer a rule that names an element as well. Two classes can,
   and this sheet is loaded after the one that carries it. */
.ledger .audit-who{min-width:0;font-family:var(--serif);font-size:var(--fs-5);
  font-weight:600;letter-spacing:var(--ls-1);color:var(--gold-warm);
  text-align:left;white-space:normal}

/* THE PREPOSITION IS NOT PART OF THE NAME. Same line, ash, mono -- so the eye
   takes "by" and then the man, and never reads the word as a first name. */
.audit-lead:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);font-weight:400;
  letter-spacing:var(--ls-key);text-transform:uppercase;color:var(--gold-ash)}

.ledger button.audit-who{cursor:pointer;border-bottom:1px solid transparent;
  transition:color var(--t),border-color var(--t)}
.ledger button.audit-who:hover{color:var(--gold-lit);border-bottom-color:var(--gold-line)}
.ledger button.audit-who:focus-visible{outline:1px solid var(--gold);outline-offset:2px}

/* A MAN THE LEDGER CANNOT OPEN IS STILL NAMED. He simply does not underline,
   because nothing happens when you press him. */
.ledger .audit-who-flat{color:var(--ink-2)}

.audit-when:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:auto;flex:none;white-space:nowrap}

.audit-detail:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:0;color:var(--ink-3);font-size:var(--fs-3);
  line-height:1.5}
.audit-detail:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)):empty{display:none}

/* ==========================================================================
   A FIGURE A MAN MAY NOT CHANGE IS A FIGURE, NOT A BOX THAT REFUSES HIM.

   Owner: "the codex should look editable to someone who cant edit it...", then
   "to someone who can edit it make it editable."

   The Codex drew every tariff to a reader as `<span class="field field-n">` --
   the same bordered, inset, 56px box the editor gets, with the same ground and
   the same recessed shadow, and no way to type in it. So the rulebook told
   every brother in the chapter that he could set the price of an Absolute and
   then did nothing when he pressed it.

   IT KEEPS THE COLUMN AND LOSES THE CHROME. Same width, same centring, same
   weight and colour as the figure inside the input, so the two views of this
   screen are one layout and a reader can be shown either without the page
   moving under him. What goes is every cue that says "type here": the border,
   the ground, the inset shadow and the focus ring.
   ========================================================================== */
.ledger .figure-read{display:inline-flex;align-items:center;justify-content:center;
  width:calc(56px*var(--ui));min-height:calc(30px*var(--ui));padding:0;
  font-family:var(--mono);font-size:var(--fs-5);font-weight:700;
  color:var(--gold-warm);background:none;border:0;box-shadow:none}

/* ==========================================================================
   A MENU THAT OPENS PAST THE EDGE OF A PHONE, ANSWERED ONCE FOR ALL THREE BARS.

   `.pop` is `position:absolute;left:0;min-width:230px` under the chip that was
   pressed. At 420px that is half the screen wide, hung off a chip which may
   itself be two thirds of the way across -- so it opens past the right edge
   and `.ledger`'s own overflow:hidden takes the rest off.

   Battle Honours had a whole plate of its own built to avoid this, drawn from
   the same `filtOpen` the pop uses, so one press opened two filters for one
   question. The plate is gone; this is the thing it was working around, said
   in four declarations that every bar on the site gets.

   FULL WIDTH AND PINNED TO THE ROW, not to the chip: `left:0;right:auto` on
   the `.row.rel` the chip sits in would still be the chip's box, so the width
   comes off the container instead -- the pop is as wide as the filter row and
   starts where the row starts. It keeps its own scroll, and `.pop-thumb`
   keeps its 44px rows.
   ========================================================================== */
@container ledger (max-width:720px){
  .head-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .row.rel{position:static}
  .head-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative}
  .pop:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:0;right:0;min-width:0;width:100%;max-height:min(60vh,340px)}
}

/* ==========================================================================
   THE DESK, IN THE CELL THAT ALREADY SAYS WHO MAY.

   Owner: "1 grid not multiple. the cabability grid is what people can do on
   the site ... the desks are the jobs people in those positions have."

   A capability is (job, rank) -> he MAY. A desk is (job, Discord role) -> it
   LANDS on him. Two sentences about the same pair, which is why they were two
   tables on two tabs and why one of them could be deleted. The cell answers
   both now: the square is gold when the rank carries the job, and a hollow
   ring sits in its corner when the job is that rank's to do.

   A RING AND NOT A FILL, because it has to be legible on a cell that is gold
   and on a cell that is not -- those two are different statements and both are
   real. A rank with the desk and without the capability is a rank that may act
   only because the desk says so, which permissions.ts honours and which is
   worth being able to see at a glance.

   IT TAKES NO CLICKS. The square under it is the button; this is a mark on the
   button's face, so a press anywhere in the cell still flips the capability.
   ========================================================================== */
/* THE CELL HAS TO BE THE ORIGIN. ledger.css lays every cell out as a flex box
   and positions none of them, so an absolute mark inside one would hang itself
   off the plate instead. One declaration, and it moves nothing: a relative box
   with no offsets sits exactly where a static one does. */
.ledger .cap > span{position:relative}

.ledger .cap .desk-mark{position:absolute;right:3px;top:3px;
  width:calc(7px * var(--ui));height:calc(7px * var(--ui));
  border-radius:50%;border:1px solid var(--gold-warm);
  background:none;pointer-events:none;
  box-shadow:0 0 6px -1px var(--glow-live)}

/* ==========================================================================
   A PHONE IS 390 PIXELS WIDE AND WE WERE SPENDING 76 OF THEM ON NOTHING.

   Owner: "For mobile view only, remove the rank badges and emblems that take
   up too much room and can't se the text", "Also make more room onbthe left
   and right", "Only mobile."

   MEASURED ON A 390px PHONE, before any of this:

       viewport        390
       plate           x 38, w 314      <- 38px of gutter on each side
       row padding     8px 16px 8px 48px
       the name        x 135, w 62      <- sixty-two pixels for a man's name

   Two thirds of the row was spent before his name began. The 48px indent is my
   own -- it stands where the rank column used to, and the arithmetic that
   replaced it was written for a desk and never trimmed for a phone.

   THE BADGES GO FIRST. A role icon is 22px square and a man may wear four;
   beside a name in a 314px plate they are most of the line, and they are
   ornament -- every one of them is a role he also wears in the guild, said in
   words on his own card. They are drawn at ten places in the page and this
   takes them off every one at once, on the phone only: the desk has room for
   them and the owner asked twice for them to stay there.

   THEN THE ROOM ITSELF. The page gutter comes down from 28px to 10px and the
   roll's indent from 48 to 14 -- the rank column it was standing in for is
   gone, and what is left is the gap a row needs from its own edge.
   ========================================================================== */
@container ledger (max-width:720px){
  /* (0,2,0), AND THE SECOND TIME TODAY. `.ledger img{display:block}` is
     (0,1,1) and beats a bare class however late the sheet is loaded -- the
     same shape of rule that took the face off `.audit-who` this afternoon.
     One class cannot answer a rule that names an element as well. */
  .ledger .role-badge,
  .ledger .role-badge-lg{display:none}

  /* THE GUTTER IS ON THE SCROLLPORT, AND A CONTAINER CANNOT STYLE ITSELF.
     `--gut` is declared on `.ledger`, which is also the element carrying
     `container-name: ledger` -- and an @container rule only ever matches
     DESCENDANTS of its container, never the container. So redeclaring the
     token here was a rule that could not match anything, which is why the
     plate sat at x 38 on a 390px phone after the first attempt. `.gutter` is
     a descendant, and taking its padding directly is what the query can
     actually reach. */
  .ledger .gutter{padding-left:calc(10px * var(--ui));padding-right:calc(10px * var(--ui))}

  /* AND THE ROLL'S INDENT, which was standing in for a column that is gone. */
  .ledger :is(.cmp,.roll) .rail{padding-left:var(--s5);padding-right:var(--s5)}
  .ledger :is(.cmp,.roll) .roll-row{padding-left:calc(14px * var(--ui))}
}

/* ==========================================================================
   A REPORT ON A PHONE SAYS WHO RAN IT.

   Owner: "in mobile only reports I can't see who ran the runs. Remove the
   worth and filed and add the brothers that ran."

   The phone rule hid `rep-men` along with the id, the difficulty and the filer
   -- four columns dropped to fit three across, and the one that got cut is the
   one the screen is read for. What stayed was the WORTH and the date FILED: a
   number you can get by opening it and a date the list is already sorted by.

   THE BROTHERS TAKE THE WHOLE WIDTH, on their own line under the mission,
   because names do not fit in a column on a 390px screen and stacking them is
   what the desk already does. `rep-by` stays hidden: the man who filed it is
   almost always one of the men on it.
   ========================================================================== */
@container ledger (max-width:720px){
  .ledger .rep-row{grid-template-columns:minmax(0,1fr)}
  .ledger .rep-state{grid-column:1/-1}
  .ledger .rep-men{display:flex;flex-direction:column;align-items:flex-start;
    gap:var(--s2);grid-column:1/-1;min-width:0}
  .ledger .rep-row > .pts,
  .ledger .rep-row > .rep-filed{display:none}
}
