/* ============================================================================
   THE ONE SHEET.

   Owner, 2026-09-22: "I would like to consolidate the style sheets down to
   one". Five linked files, the page runtime's atomic classes and 292 KB of
   CSS inline in index.html, in EXACTLY the order the browser applied them --
   the cascade decides every tie by order, so the order is the design:

     1-5  skull-chat, ledger, ledger-carry, ledger-extra, gothic (were <link>s)
     6    the chapter's palette -- core/hosted.ts puts ui/app/chapter/<key>.css
          in the slot below when it serves this file, so each site still gets
          only its own colours
     7    the runtime's atomic classes (support.js ATOMIC_CSS), which it
          injected after the links; the helmet no longer asks it to
     8    the sheet that was inline in index.html's helmet, mounted last

   Nothing in any part was changed in the merge. Where a later part restates
   an earlier rule, the later one wins, exactly as before -- the next job is
   to delete the losers, page by page, and never to add a sixth part.
   ========================================================================== */

/* ============================================================================
   1. skull-chat.css -- the chat panel
   ========================================================================== */
.skull-chat{position:fixed;z-index:72;right:max(16px,var(--gut,16px));bottom:max(16px,var(--gut,16px));
  width:min(430px,calc(100vw - 32px));max-height:min(680px,calc(100dvh - 32px));display:flex;flex-direction:column;
  box-sizing:border-box;padding:18px;border:1px solid var(--gold-line,#8a6b35);border-radius:3px;
  background:var(--chat-ground,linear-gradient(145deg,rgba(var(--hue-57,19,34,27),.98),rgba(7,16,13,.99)));box-shadow:0 18px 48px rgba(0,0,0,.58),inset 0 1px rgba(219,181,91,.16);
  color:var(--ink,rgb(var(--hue-67,228,235,226)));font-family:var(--mono,ui-monospace,monospace)}
.skull-chat[hidden]{display:none}
.skull-say-chat{max-width:391px}
.skull-say-chat .skull-say-v{font-size:calc(var(--fs-4) * 1.15);color:var(--ink-2);line-height:1.5}
.aar-console-chat-trigger{position:absolute;left:15%;top:12%;width:70%;height:82%;padding:0;border:0;background:transparent;cursor:pointer;pointer-events:auto;border-radius:8px}
.aar-console-chat-trigger:focus-visible{outline:1px solid #C9A45C;outline-offset:3px}
.skull-guide-target{outline:1px solid #d1ab5b!important;outline-offset:7px;box-shadow:0 0 0 3px rgba(209,171,91,.12),0 0 24px var(--chat-glow,rgba(var(--hue-85,111,184,126),.38))!important;scroll-margin:80px;animation:skull-guide-pulse 1.35s ease-in-out infinite}
@keyframes skull-guide-pulse{0%,100%{outline-color:#d1ab5b;box-shadow:0 0 0 3px rgba(209,171,91,.12),0 0 18px rgba(var(--hue-85,111,184,126),.26)}50%{outline-color:rgb(var(--hue-89,158,208,167));box-shadow:0 0 0 5px rgba(var(--hue-86,126,194,139),.16),0 0 31px rgba(var(--hue-85,111,184,126),.55)}}
@media (prefers-reduced-motion:reduce){.skull-guide-target{animation:none}}
.skull-chat-next{align-self:flex-start;margin:6px 0}
.skull-chat-next[hidden]{display:none}
.skull-chat-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;border-bottom:1px solid var(--line-2,rgba(151,126,72,.3));padding-bottom:12px}
.skull-chat-head-actions{display:flex;align-items:center;gap:8px}
.skull-chat-kicker{margin:0 0 3px;color:var(--good,rgb(var(--hue-2,127,180,137)));font-size:10px;letter-spacing:.14em}
.skull-chat h2{margin:0;font-family:var(--serif,Georgia,serif);font-size:17px;line-height:1.2;letter-spacing:.04em;font-weight:500}
.skull-chat-close{border:0;background:transparent;color:var(--ink-3,rgb(var(--hue-19,142,158,147)));font:28px/20px var(--mono,monospace);cursor:pointer;padding:0 2px}
.skull-chat-close:hover,.skull-chat-close:focus-visible{color:var(--ink,rgb(var(--hue-67,228,235,226)))}
.skull-chat-starters{display:flex;flex-wrap:wrap;gap:6px;padding:12px 0 8px}
.skull-chat-starter{border:1px solid var(--line-2,rgba(151,126,72,.3));border-radius:2px;background:var(--chat-them-soft,rgba(var(--hue-87,53,75,57),.26));color:var(--ink-2,rgb(var(--hue-21,169,188,175)));font:11px/1.2 var(--mono,monospace);padding:6px 8px;cursor:pointer}
.skull-chat-starter:hover,.skull-chat-starter:focus-visible{border-color:var(--good-2,rgb(var(--hue-15,110,158,122)));color:var(--ink,rgb(var(--hue-67,228,235,226)));background:var(--chat-them-lit,rgba(var(--hue-88,75,119,86),.3))}
.skull-chat-transcript{overflow-y:auto;min-height:72px;max-height:min(300px,36dvh);padding:4px 2px 2px;scrollbar-color:var(--good-2,rgb(var(--hue-15,110,158,122))) transparent;display:flex;flex-direction:column;gap:8px}
.skull-chat-line{width:fit-content;max-width:86%;box-sizing:border-box;margin:0;padding:8px 10px;border:1px solid var(--line-2,rgba(151,126,72,.3));border-radius:3px;font-size:12px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}
.skull-chat-user{align-self:flex-end;color:var(--ink,rgb(var(--hue-67,228,235,226)));background:rgba(104,81,36,.3);border-color:rgba(199,166,90,.5)}
.skull-chat-user::before{content:"YOU";display:block;margin-bottom:3px;color:var(--gold,#c7a65a);font-size:9px;letter-spacing:.1em}
.skull-chat-assistant{align-self:flex-start;color:var(--ink-2,rgb(var(--hue-21,169,188,175)));background:var(--chat-them,rgba(var(--hue-87,53,75,57),.36));border-color:var(--chat-them-line,rgba(var(--hue-85,111,184,126),.42))}
.skull-chat-assistant::before{content:"BLIPITUS";display:block;margin-bottom:3px;color:var(--good,rgb(var(--hue-2,127,180,137)));font-size:9px;letter-spacing:.1em}
.skull-chat-status{min-height:18px;margin:6px 0;color:var(--ink-4,rgb(var(--hue-14,109,125,112)));font-size:11px;line-height:1.35}
.skull-chat-status[data-kind="error"]{color:var(--bad-lit,#c4685f)}
.skull-chat-label{display:block;margin-bottom:5px;color:var(--ink-4,rgb(var(--hue-14,109,125,112)));font-size:10px;letter-spacing:.1em;text-transform:uppercase}
.skull-chat-compose{display:flex;gap:8px}
.skull-chat-input{min-width:0;flex:1;border:1px solid var(--gold-line,#8a6b35);background:var(--chat-field,rgba(4,12,9,.78));color:var(--ink,rgb(var(--hue-67,228,235,226)));padding:9px;font:12px var(--mono,monospace);outline:none}
.skull-chat-input:focus{border-color:var(--good,rgb(var(--hue-2,127,180,137)));box-shadow:0 0 0 1px var(--chat-focus,rgba(var(--hue-2,127,180,137),.25))}
.skull-chat-send{border:1px solid var(--gold-line,#8a6b35);background:rgba(104,81,36,.38);color:var(--ink,rgb(var(--hue-67,228,235,226)));padding:8px 10px;font:11px var(--mono,monospace);cursor:pointer}
.skull-chat-send:hover,.skull-chat-send:focus-visible{background:var(--chat-them-lit,rgba(var(--hue-2,127,180,137),.22));border-color:var(--good,rgb(var(--hue-2,127,180,137)))}
.skull-chat-send:disabled{cursor:wait;opacity:.55}
.skull-chat[data-feedback]>.skull-chat-starters,.skull-chat[data-feedback]>.skull-chat-transcript,.skull-chat[data-feedback]>.skull-chat-status,.skull-chat[data-feedback]>.skull-chat-form,.skull-chat[data-feedback]>.skull-chat-next{display:none}
.skull-feedback{overflow-y:auto;min-height:0;font-size:12px;line-height:1.5;scrollbar-color:var(--good-2,rgb(var(--hue-15,110,158,122))) transparent}
.skull-feedback[hidden],.skull-feedback-form[hidden]{display:none}
.skull-feedback-prompt{color:var(--ink-2,rgb(var(--hue-21,169,188,175)));white-space:pre-wrap}
.skull-feedback-kinds,.skull-feedback-actions{display:flex;flex-wrap:wrap;gap:8px}
.skull-feedback-form{display:flex;flex-direction:column;gap:8px}
.skull-feedback-form textarea{box-sizing:border-box;width:100%;resize:vertical;min-height:90px;max-height:160px;line-height:1.5}
.skull-feedback-form .skull-chat-send{align-self:flex-end}
.skull-feedback-review{white-space:pre-wrap;overflow-wrap:anywhere}
.skull-feedback-review p{padding:8px 10px;background:var(--chat-them-soft,rgba(var(--hue-87,53,75,57),.26));border-left:2px solid var(--good,rgb(var(--hue-2,127,180,137)))}
.skull-feedback-context{color:var(--ink-4,rgb(var(--hue-14,109,125,112)));font-size:10px;overflow-wrap:anywhere}
.skull-feedback-status{color:var(--ink-2,rgb(var(--hue-21,169,188,175)));white-space:pre-wrap}
.skull-feedback-status[data-kind="error"]{color:var(--bad-lit,#c4685f)}
.skull-feedback button:disabled{opacity:.55;cursor:wait}
@media (max-width:520px){.skull-chat{right:10px;bottom:10px;width:calc(100vw - 20px);max-height:calc(100dvh - 20px);padding:14px}.skull-chat h2{font-size:15px}.skull-chat-compose{align-items:stretch}.skull-chat-send{padding:8px}.skull-chat-transcript{max-height:31dvh}}

/* ============================================================================
   2. ledger.css -- the redesign
   ========================================================================== */
/* ==========================================================================
   CALIBAN LEDGER — ledger.css — the shared layer.
   One stylesheet, one set of names. Every screen is assembled from these
   primitives. A screen that needs its own CSS is a bug in this file.

   0 tokens · 1 base · 2 layout · 3 type · 4 plate · 5 rail · 6 crew
   7 atoms · 8 evidence · 9 shell · 10 motion

   Class names are the live site's. New primitives added here:
   .seal .stat .tiles/.tile .discrep .worth .obj .kv .perch .console .candle
   ========================================================================== */

/* 0 · TOKENS — everything is a multiple of --ui ----------------------------- */
:where(.ledger){
  --ui:1;
  /* HOW WIDE A SCREEN IS ALLOWED TO GET, IN ONE PLACE.

     Owner: "plenty of apce to make this entire app a little wider", and then
     "the sides of the app we can take up a lot more space the gaps are huge".
     He is right: every screen was capped at 1260px, so on his 2528px monitor
     the app used half the glass and put 630px of empty page down either side.

     It is a token rather than four numbers because four numbers is how the
     roster, the AAR, the codex and the filing came to be capped at 1260, 1180,
     1260 and 1080 in the first place, and three of those differences were not
     decisions. The two that ARE decisions keep their own line below and say
     why.

     NOT `none`. A table can use every pixel it is given; a paragraph cannot,
     and this app has both on the same screens. 1720 is about 150 characters of
     the roll's own type -- wide for prose, right for eleven columns of figures
     -- and it is the number to change if he wants more. */
  --page-w:calc(1720px * var(--ui));
  /* ink, brightest to faintest */
  --ink:rgb(var(--hue-67,228,235,226));--ink-2:rgb(var(--hue-21,169,188,175));--ink-3:rgb(var(--hue-19,142,158,147));--ink-4:rgb(var(--hue-14,109,125,112));--ink-5:rgb(var(--hue-12,95,112,102));--ink-6:rgb(var(--hue-36,79,95,84));--ink-on-gold:#120E06;
  /* WHAT THE FIRE THROWS OFF, as bare channels because ui/app/embers.js
     builds an rgba() from it per particle. Caliban's forge is orange. */
  --ember-rgb:240,150,60;
  /* the chapter's metal */
  --gold:#C9A45C;--gold-lit:#D8AE4A;--gold-warm:#E2C079;--gold-dim:#8A6A32;--gold-ash:#8C7A52;--gold-line:rgba(201,164,92,.35);
  /* the four states + the machine spirit + escalation */
  --good:rgb(var(--hue-2,127,180,137));--good-2:rgb(var(--hue-15,110,158,122));--warn:#B5705A;--warn-2:#C98B74;--bad:#9B2C24;--bad-lit:#C4685F;--cyan:rgb(var(--hue-4,59,200,178));--esc:#7A92C4;--esc-lit:#9DB4E0;
  /* ground and surfaces */
  --bg:#0B1210;--bg-deep:#07100D;--row-alt:rgb(var(--hue-75,16,30,24));
  --panel:rgba(var(--hue-72,9,18,14),.62);--panel-2:rgba(11,18,16,.7);--panel-3:rgba(8,16,13,.6);--panel-4:rgba(var(--hue-53,19,35,27),.55);--panel-on:rgb(var(--hue-25,23,44,35));--pop:rgb(var(--hue-76,13,26,20));
  --tile:linear-gradient(150deg,rgba(var(--hue-35,70,100,80),.4),rgba(var(--hue-73,19,30,24),.75));--tile-edge:rgba(var(--hue-1,178,201,177),.22);
  --line:rgb(var(--hue-27,26,42,32));--line-2:rgb(var(--hue-11,43,58,38));--line-3:rgba(var(--hue-1,178,201,177),.13);--hair:rgba(var(--hue-1,178,201,177),.10);--hair-2:rgba(var(--hue-1,178,201,177),.16);
  --rule:rgba(var(--hue-1,178,201,177),.10);--rule-head:rgba(201,164,92,.22);
  /* light: what every bevel, rivet and lamp is made of */
  --edge-lit:rgba(255,228,158,.20);--edge-dark:rgba(0,0,0,.48);--rivet:rgba(222,206,164,.72);--rivet-shade:rgba(0,0,0,.62);--lift:rgba(74,52,20,.32);
  --glow-live:rgba(216,174,74,.36);--glow-read:rgba(var(--hue-5,121,196,141),.32);--glow-owed:rgba(180,101,58,.36);--glow-esc:rgba(122,146,196,.36);--glow-spent:rgba(0,0,0,0);
  --cast:0 16px 34px -16px rgba(0,0,0,.9);
  --sink:inset 0 2px 8px rgba(0,0,0,.6);
  --bevel:inset 0 1px 0 var(--edge-lit),inset 0 -1px 0 var(--edge-dark),0 1px 0 var(--edge-dark);
  --face:linear-gradient(168deg,rgba(255,255,255,.04),rgba(255,255,255,0) 36%,rgba(0,0,0,.16) 100%);
  /* type */
  --serif:'Cormorant Garamond',Georgia,serif;--mono:'JetBrains Mono',ui-monospace,Menlo,monospace;--sans:system-ui,-apple-system,'Segoe UI',sans-serif;
  --fs-1:calc(9px*var(--ui));--fs-2:calc(10px*var(--ui));--fs-3:calc(11px*var(--ui));--fs-4:calc(12px*var(--ui));--fs-5:calc(13px*var(--ui));
  --fs-6:calc(14px*var(--ui));--fs-7:calc(16px*var(--ui));--fs-8:calc(20px*var(--ui));--fs-9:calc(26px*var(--ui));--fs-10:calc(44px*var(--ui));
  --ls-1:.1em;--ls-2:.14em;--ls-3:.2em;--ls-head:.26em;--ls-key:.18em;--ls-ctl:.12em;--ls-meta:.06em;--ls-brand:.22em;
  /* space */
  --s1:calc(2px*var(--ui));--s2:calc(4px*var(--ui));--s3:calc(6px*var(--ui));--s4:calc(8px*var(--ui));--s5:calc(10px*var(--ui));
  --s6:calc(12px*var(--ui));--s7:calc(16px*var(--ui));--s8:calc(20px*var(--ui));--s9:calc(26px*var(--ui));
  --gut:clamp(12px,2.1vw,34px);--r1:2px;--r2:3px;
  /* motion */
  --t:.16s ease;--ease-settle:cubic-bezier(.2,.8,.2,1);--ease-thunk:cubic-bezier(.7,0,.3,1);--dur-in:.22s;--dur-screen:.4s;
}

/* 1 · BASE — .ledger is the site root (put it on <body>) --------------------- */
:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before,:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{box-sizing:border-box}
.ledger{position:relative;overflow:hidden;min-width:0;container-type:inline-size;container-name:ledger;font-family:var(--sans);font-size:var(--fs-5);line-height:1.4;color:var(--ink);background:var(--bg);
  -webkit-font-smoothing:antialiased;text-shadow:0 1px 1px rgba(0,0,0,.55);--gut:calc(28px*var(--ui))}
.ledger a{color:var(--gold);text-decoration:none}.ledger a:hover{color:var(--gold-warm)}
.ledger button,.ledger input{font-family:inherit;color:inherit}
.ledger button{margin:0}
/* AND A BARE BUTTON KEEPS THE BROWSER'S OWN CHROME UNLESS SOMETHING TAKES IT.
   Settings' tab strip is `<button class="subtab">`, and `.subtab` sets colour,
   padding and a bottom rule but never background, border or appearance -- the
   old sheet had a global button reset and the fence keeps it out of `.ledger`.
   So seven tabs drew as light-grey system buttons wearing this sheet's pale
   ink: white on white, unreadable, on a screen an officer opens to decide who
   may do what.

   TYPE SELECTOR ONLY, INSIDE :where(), SO IT WEIGHS (0,0,1). `.ledger button`
   above is (0,1,1) and would outrank every `.btn` rule in this sheet, which are
   deliberately (0,1,0); this has to lose to all of them and beat only the user
   agent. Padding is left alone on purpose -- it is the one thing a bare button
   in this design sometimes still wants from the UA. */
:where(.ledger) button{-webkit-appearance:none;appearance:none;background:none;
  border:0;color:inherit;font:inherit;text-align:inherit}
.ledger :focus-visible{outline:1px solid var(--gold);outline-offset:2px}
.ledger img{display:block;max-width:100%}
.ledger p{margin:0}

/* 2 · LAYOUT — one gap scale, never per-element margins ---------------------- */
.row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;min-width:0}.col:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;min-width:0}
.row-base:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-items:baseline}.row-top:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-items:flex-start}
.g1:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s1)}.g2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s2)}.g3:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s3)}.g4:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s4)}.g5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s5)}.g6:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s6)}.g7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s7)}.g8:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s8)}
.grow:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1 1 0;min-width:0}.grow-0:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none}.min0:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:0}.wrap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex-wrap:wrap}.spacer:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1}
.ellipsis:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.nowrap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){white-space:nowrap}
.right:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){text-align:right}.end:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){justify-content:flex-end}.between:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){justify-content:space-between}.center:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){justify-content:center}

/* 3 · TYPE — two faces, ten steps, one ink ramp ------------------------------ */
.heading:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-4);font-weight:600;letter-spacing:var(--ls-head);text-transform:uppercase;color:var(--gold);
  text-shadow:0 1px 0 rgba(0,0,0,.55),0 0 14px rgba(216,180,90,.28)}
.label:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);font-weight:600;letter-spacing:var(--ls-key);text-transform:uppercase;color:var(--gold-ash)}
.meta:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);letter-spacing:var(--ls-meta);color:var(--ink-4);line-height:1.5}
.meta-ash:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);letter-spacing:var(--ls-meta);color:var(--gold-ash)}
.lead:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-5);line-height:1.55;color:var(--ink-2)}
.mono:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-variant-numeric:tabular-nums}
.serif:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif)}
.caps:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){text-transform:uppercase;letter-spacing:var(--ls-2)}
.t1:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-1)}.t2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-2)}.t3:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-3)}.t4:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-4)}.t5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-5)}
.t6:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-6)}.t7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-7)}.t8:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-8)}.t9:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-9)}.t10:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-10)}
.c-ink:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink)}.c-2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-2)}.c-3:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-3)}.c-4:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-4)}.c-5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-5)}
.c-gold:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold)}.c-warm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-warm)}.c-good:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--good)}.c-warn:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--warn-2)}.c-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--bad-lit)}
.c-read:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--cyan)}.c-esc:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--esc-lit)}

/* 4 · PLATE — the panel. Every boxed region on every screen is one. --------- */
.plate:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:flex;flex-direction:column;min-width:0;
  background:var(--panel-4);background-image:var(--face);
  border:1px solid var(--line-3);border-top-color:rgba(var(--hue-8,210,225,210),.17);border-radius:var(--r2);
  box-shadow:var(--bevel),var(--cast)}
.plate:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{content:"";position:absolute;inset:5px;pointer-events:none;border-radius:1px;z-index:1;
  background:
    radial-gradient(circle at 0 0,var(--rivet) 0 1.4px,var(--rivet-shade) 1.4px 2.6px,transparent 2.7px),
    radial-gradient(circle at 100% 0,var(--rivet) 0 1.4px,var(--rivet-shade) 1.4px 2.6px,transparent 2.7px),
    radial-gradient(circle at 0 100%,var(--rivet) 0 1.4px,var(--rivet-shade) 1.4px 2.6px,transparent 2.7px),
    radial-gradient(circle at 100% 100%,var(--rivet) 0 1.4px,var(--rivet-shade) 1.4px 2.6px,transparent 2.7px)}
.plate-bare:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){--rivet:transparent;--rivet-shade:transparent;background:transparent;border-color:transparent;box-shadow:none}
.plate-lit:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold-line);box-shadow:var(--bevel),var(--cast),0 0 26px -12px var(--glow-live)}
.plate-owed:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--warn);box-shadow:var(--bevel),var(--cast),0 0 26px -12px var(--glow-owed)}
.plate-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:flex;align-items:center;flex-wrap:wrap;gap:var(--s3) var(--s5);
  padding:calc(18px*var(--ui)) var(--s8) calc(9px*var(--ui));border-bottom:1px solid var(--rule-head)}
.plate-head-sub:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:max(10px,var(--fs-3));color:var(--ink-3);min-width:0}
.plate-head-meta:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:auto;flex:none;font-family:var(--mono);font-size:max(10px,var(--fs-3));letter-spacing:var(--ls-meta);color:var(--ink-3)}
.plate-head-sm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s5) 0 var(--s2);gap:var(--s2) var(--s4);border-bottom-color:var(--hair)}
.plate-body:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s6);padding:var(--s7) var(--s8) var(--s8)}
.plate-body-flush:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:0;gap:0}

/* 5 · RAIL — one row in any list. 600 brothers or 6 desks, same rail. ------- */
.rail:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:flex;align-items:center;gap:var(--s4);min-width:0;padding:var(--s4) var(--s8);
  border-bottom:1px solid var(--rule);transition:background var(--t),box-shadow var(--t)}
.rail-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;align-items:center;gap:9px}
.rail-sm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s2) var(--s3);gap:var(--s3);border-bottom-color:transparent}
.rail-act:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:pointer}
.rail-act:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--lift);box-shadow:inset 3px 0 0 var(--gold-dim)}
.rail-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--panel-3);box-shadow:inset 3px 0 0 var(--gold-lit),0 0 20px -12px var(--glow-live)}
.rail-you:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--panel-3);box-shadow:inset 3px 0 0 var(--gold)}
.rail-owed:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:inset 3px 0 0 var(--warn)}
.rail-spent:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-5)}
.rail-you.rail-act:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:inset 3px 0 0 var(--gold-lit)}
.rail-group:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:baseline;gap:var(--s4);padding:var(--s6) var(--s8) var(--s3);border-bottom:1px solid var(--hair)}
.rail-state:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none;display:flex;align-items:center;justify-content:flex-end;gap:var(--s3);min-width:calc(96px*var(--ui))}
.rail:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .on-hover{opacity:0;transition:opacity var(--t)}
.rail:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .on-hover,.rail:focus-within:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .on-hover{opacity:1}
/* the key/value rail the filing, the auspex and the record all read as */
.kv:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:calc(128px*var(--ui)) minmax(0,1fr) auto;gap:var(--s5);min-height:calc(40px*var(--ui))}
.kv-4:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:calc(128px*var(--ui)) minmax(0,1fr) auto auto}
.kv-v:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-5);color:var(--ink);line-height:1.4;overflow-wrap:anywhere}
/* the roll — the roster's rail: standing · face · brother · company · points · reports · evidence */
/* SEVEN CELLS STILL, AND THREE OF THEM SAY SOMETHING ELSE. The roll drew
   standing, face, brother, company, points, reports and four verdict
   tallies; it draws standing, face, brother, company, last online, last
   post and on vox. The tallies' 300px block is gone and the three that
   replaced it are even, because none of them is more important than the
   others -- they are one question asked three ways. */
/* ==========================================================================
   EVERY TABLE SHARES THE WIDTH IT HAS, RATHER THAN CLAIMING A FIXED SLICE.

   Owner, with a screenshot of the roster's LAST ONLINE chip sitting on top of
   LAST POST: "every column has this problem, roster reports battle honors ALL
   need to be spaced with the dom size % not dedicated spaceing."

   He is right, and the fault is not that the tracks were too narrow -- it is
   that they were FIXED. `calc(132px * var(--ui))` is 132px at every window
   width, so a chip reading "OFFLINE · 41 MINUTES AGO" overflowed its own
   column and printed across the one beside it; and at 1720px the same tracks
   left the page half empty while still overlapping.

   PROPORTIONAL TRACKS, AND minmax(0, ...) ON EVERY ONE OF THEM. The zero floor
   is the half that actually stops the overlap: a bare `1fr` floors at
   min-content, which for a nowrap chip is the chip's whole width, so the track
   refuses to shrink and the content escapes. `minmax(0,1fr)` lets the track be
   as narrow as the layout needs and the cell clip instead -- which is why the
   rule below giving every grid cell `min-width:0` is part of the same fix and
   not a tidy-up.

   WHAT STAYS FIXED, AND ONLY THIS: a standing number and an avatar circle are
   PHYSICAL sizes -- a 28px face is 28px on any monitor -- so those two tracks
   keep their pixels. Everything that holds words is a share.
   ========================================================================== */
.rail-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > *{min-width:0}

/* AND A CELL OF WORDS ENDS IN AN ELLIPSIS RATHER THAN IN THE NEXT COLUMN. */
.rail-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .meta,
.rail-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .mono,
.rail-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .kv-v{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.roll-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:calc(28px*var(--ui)) minmax(0,3fr) minmax(0,1fr) minmax(0,1.6fr) minmax(0,1.2fr) minmax(0,1.3fr);column-gap:var(--s5);min-height:calc(46px*var(--ui))}
.roll-hdr:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:0;padding-top:var(--s4);padding-bottom:var(--s3);border-bottom-color:var(--rule-head)}
.roll-hdr:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .tallies .label{text-align:right;letter-spacing:.04em;font-size:var(--fs-1)}
.roll-item:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:contents}
.roll-name:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-7);font-weight:600;color:var(--ink);line-height:1.1}
.rail-you:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .roll-name{color:var(--gold-warm)}
.rank-n:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);font-variant-numeric:tabular-nums;color:var(--ink-4);text-align:right}
.rank-top:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold);font-weight:700}
.pts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-6);font-weight:500;font-variant-numeric:tabular-nums;color:var(--gold-warm);text-align:right}
.tallies:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s3)}
.tally:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;justify-content:flex-end;gap:var(--s2);font-family:var(--mono);font-size:var(--fs-3);font-variant-numeric:tabular-nums;color:var(--ink-3)}
.tally:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{content:"";flex:none;width:5px;height:5px;border-radius:50%;background:var(--lamp,var(--ink-5));box-shadow:0 0 6px var(--lamp,transparent)}
.tally-read:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){--lamp:var(--good)}.tally-wait:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){--lamp:var(--ink-4)}.tally-owed:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){--lamp:var(--warn-2)}.tally-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){--lamp:var(--bad-lit)}
.tally-zero:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-6)}.tally-zero:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{opacity:.3;box-shadow:none}
.rail-detail:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s5);padding:var(--s5) var(--s7) var(--s6) calc(88px*var(--ui));border-bottom:1px solid var(--rule);background:rgba(0,0,0,.12)}
.stats-6:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:repeat(6,minmax(0,1fr))}
.objs:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s2);padding:var(--s5) var(--s8);border-bottom:1px solid var(--rule)}
.obj:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;justify-content:space-between;gap:var(--s5);padding:var(--s1) 0;border-bottom:1px dashed var(--hair);
  font-family:var(--mono);font-size:var(--fs-2);color:var(--ink-3)}
.obj:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) b{font-weight:500;color:var(--ink-2);font-variant-numeric:tabular-nums}

/* 6 · CREW — a person. Whose he is must read before his name does. --------- */
.crew-group:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:baseline;gap:var(--s4);padding:var(--s3) 0;border-bottom:1px solid var(--hair)}
.crew-list:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s5)}
.crew-card:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto;
  grid-template-areas:"id state" "stats stats" "ask ask" "acts acts" "note note";
  gap:var(--s5) var(--s6);padding:var(--s7) calc(18px*var(--ui)) calc(17px*var(--ui));
  background:var(--panel-3);background-image:var(--face);border:1px solid var(--line-3);border-radius:var(--r2);box-shadow:var(--bevel);
  transition:border-color var(--t),box-shadow var(--t),transform var(--t)}
.crew-id:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:id;display:flex;align-items:center;gap:var(--s5);min-width:0}
.crew-name:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-7);font-weight:600;line-height:1.1;color:var(--ink);overflow-wrap:anywhere}
.crew-place:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){}
.crew-state:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:state;display:flex;flex-direction:column;align-items:flex-end;gap:var(--s2);text-align:right;min-width:0}
.crew-state:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .stamp{max-width:calc(260px*var(--ui))}
.crew-stats:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:stats;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;
  background:var(--hair);border:1px solid var(--hair);border-radius:var(--r1);overflow:hidden;box-shadow:var(--sink)}
.stat:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:2px;padding:var(--s3) var(--s4);background:var(--bg);min-width:0}
.stat-k:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-1);text-transform:uppercase;color:var(--ink-4);line-height:1.2}
.stat-v:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-5);font-variant-numeric:tabular-nums;color:var(--ink-2)}
.stat-total:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .stat-v{color:var(--gold-warm);font-weight:500}
.crew-ask:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:ask;display:flex;gap:var(--s4);padding:var(--s4) var(--s5);border-left:2px solid var(--esc);
  background:rgba(122,146,196,.08);font-size:var(--fs-4);line-height:1.5;color:var(--ink-2)}
.crew-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:acts;display:flex;align-items:center;flex-wrap:wrap;gap:var(--s4)}
.crew-pts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:auto;display:flex;align-items:center;gap:var(--s3)}
.crew-note:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:note;display:flex;gap:var(--s4)}
.crew-note:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .field{flex:1}
.crew-mine:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold-line);box-shadow:var(--bevel),var(--cast),0 0 34px -14px var(--glow-live)}
.crew-mine:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";position:absolute;left:-1px;top:-1px;bottom:-1px;width:3px;background:var(--gold);border-radius:var(--r2) 0 0 var(--r2);box-shadow:0 0 10px var(--glow-live)}
.crew-other:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){opacity:.72;filter:saturate(.55)}
/* THE CONTROLS FOLLOW THE CAPABILITY, NOT THE SQUAD. This read
   `.crew-other .crew-acts,.crew-other .crew-note` -- Fable's model, where a
   card outside your own squad is not yours to act on. The owner's rule is
   different and he stated it twice: "ALL aars are approved by the company
   sgt but anyone sgt or higher can approve an aar". The card still NAMES
   the sergeant it is addressed to -- that is `.crew-other` and the band
   over the arch, and both stay -- but a lieutenant reading it may act, and
   with this rule on the squad he could not: three buttons rendered into a
   `display:none` row, which is why the owner could see no approve or
   reject button anywhere on the site.
   `.crew-locked` is set from the server's own approve_aar answer, so a
   brother who holds nothing loses them on every card -- including his own
   squad's, where they used to draw and then refuse him. */
.crew-locked:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-acts,.crew-locked:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-note,.crew-credited:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-acts,.crew-escalated:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-acts{display:none}
.crew-you:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-name{color:var(--gold-warm)}
.crew-credited:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .stamp{color:var(--good)}
.crew-escalated:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .stamp{color:var(--esc-lit)}
.add-man:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:auto}
.standing:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:calc(28px*var(--ui));align-items:end;padding:calc(40px*var(--ui)) var(--s8) var(--s6)}

/* 7 · ATOMS ------------------------------------------------------------------ */
.btn:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:var(--s3);
  min-height:calc(34px*var(--ui));padding:0 var(--s6);border:1px solid var(--line-3);border-radius:var(--r1);
  background:linear-gradient(180deg,rgba(255,255,255,.04),rgba(0,0,0,.12));
  font-family:var(--mono);font-size:var(--fs-2);font-weight:500;letter-spacing:var(--ls-ctl);text-transform:uppercase;
  color:var(--ink-2);text-align:center;white-space:nowrap;box-shadow:var(--bevel);
  transition:background var(--t),box-shadow var(--t),color var(--t),border-color var(--t),transform .08s var(--ease-thunk)}
.btn:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink);border-color:var(--gold-line);background:linear-gradient(180deg,rgba(255,236,180,.08),rgba(0,0,0,.14))}
.btn:active:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transform:translateY(2px);box-shadow:var(--sink)}
.btn:disabled:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:not-allowed;opacity:.45;transform:none;box-shadow:none}
.btn-sm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(26px*var(--ui));padding:0 var(--s4);font-size:var(--fs-1);letter-spacing:var(--ls-1)}
.btn-lg:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(46px*var(--ui));padding:0 calc(34px*var(--ui));font-size:var(--fs-6);letter-spacing:var(--ls-3)}
.btn-quiet:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:transparent;background:transparent;box-shadow:none;color:var(--ink-3)}
.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-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:rgba(155,44,36,.7);color:var(--bad-lit);background:linear-gradient(180deg,rgba(155,44,36,.14),rgba(0,0,0,.3))}
.btn-bad:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--bad);color:#E0857C}
.btn-bad:active:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transform:translateY(3px)}
.btn-esc:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:rgba(122,146,196,.5);color:var(--esc-lit)}
.btn-outline:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold-line);color:var(--gold)}
/* THE SEAL — crediting a brother. Travel, a seated thunk, light that answers. */
.seal:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:var(--s3);
  min-height:calc(44px*var(--ui));padding:0 var(--s8);border:1px solid #6D5220;border-radius:var(--r1);
  background:linear-gradient(180deg,var(--gold-warm),var(--gold-lit) 40%,#A9832A);
  font-family:var(--mono);font-size:var(--fs-3);font-weight:700;letter-spacing:var(--ls-head);text-transform:uppercase;
  color:var(--ink-on-gold);text-shadow:0 1px 0 rgba(255,236,180,.35);
  box-shadow:inset 0 1px 0 rgba(255,236,180,.6),inset 0 -2px 0 rgba(0,0,0,.18),0 4px 0 #5A4118,0 12px 20px -8px rgba(0,0,0,.85),0 0 28px -8px var(--glow-live);
  transition:transform .09s var(--ease-thunk),box-shadow .09s var(--ease-thunk),filter var(--t)}
.seal:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){filter:brightness(1.06)}
.seal:active:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transform:translateY(4px);
  box-shadow:inset 0 1px 0 rgba(255,236,180,.3),inset 0 2px 6px rgba(0,0,0,.35),0 0 0 #5A4118,0 2px 4px -2px rgba(0,0,0,.8),0 0 40px -6px var(--glow-live)}
.chip:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;gap:var(--s2);border:1px solid var(--gold-line);border-radius:var(--r1);
  padding:calc(2px*var(--ui)) var(--s3);font-family:var(--mono);font-size:var(--fs-1);font-weight:500;letter-spacing:var(--ls-ctl);
  text-transform:uppercase;color:var(--ink-3);background:transparent;white-space:nowrap}
.chip-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--panel-on);color:var(--ink)}
.chip-you:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold);color:var(--ink-on-gold);background:var(--gold);text-shadow:none}
.chip-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--bad-lit);border-color:var(--bad)}
.chip-dim:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--line-2);color:var(--ink-4)}
.views:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-wrap:wrap;gap:var(--s3)}
.views:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .chip{cursor:pointer;min-height:calc(28px*var(--ui));padding:0 var(--s5)}
.field-find:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(220px*var(--ui))}
.you-stand:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:auto}
.only-phone:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:none}
.mark:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;gap:var(--s3);flex:none;padding:var(--s2) var(--s4);
  border:1px solid var(--line-3);border-radius:var(--r1);font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:var(--ls-2);text-transform:uppercase;white-space:nowrap;color:var(--ink-3)}
.mark-sm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:0 var(--s2)}
.mark-live:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-warm);border-color:var(--gold-line);background:var(--lift);box-shadow:0 0 14px -6px var(--glow-live)}
.mark-read:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--good);border-color:var(--good-2);box-shadow:0 0 14px -6px var(--glow-read)}
.mark-owed:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--warn-2);border-color:var(--warn);box-shadow:0 0 14px -6px var(--glow-owed)}
.mark-esc:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--esc-lit);border-color:var(--esc);box-shadow:0 0 14px -6px var(--glow-esc)}
.mark-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--bad-lit);border-color:var(--bad)}
.mark-spent:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-5);border-color:var(--line-3)}
.mark-lamp:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{content:"";flex:none;width:6px;height:6px;border-radius:50%;background:currentColor;box-shadow:0 0 7px currentColor}
.avatar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;justify-content:center;flex:none;width:calc(28px*var(--ui));height:calc(28px*var(--ui));
  border-radius:50%;overflow:hidden;background:var(--tile);border:1px solid var(--tile-edge);
  font-family:var(--mono);font-size:var(--fs-1);color:var(--ink-2);box-shadow:var(--sink)}
.avatar-sm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(22px*var(--ui));height:calc(22px*var(--ui))}
.avatar-lg:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(44px*var(--ui));height:calc(44px*var(--ui));font-size:var(--fs-4)}
.avatar-officer:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold)}.avatar-sgt:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold-dim)}
.avatar-me:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(64px*var(--ui));height:calc(64px*var(--ui));font-size:var(--fs-8);border-color:var(--gold-line)}
.field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(34px*var(--ui));border:1px solid var(--gold-line);background:var(--bg);padding:0 var(--s4);
  font-family:var(--mono);font-size:var(--fs-4);color:var(--ink);outline:none;border-radius:var(--r1);box-shadow:var(--sink)}
.field:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::placeholder{color:var(--ink-5)}
.field:focus:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold)}
.field-wide:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:100%}
.tiles:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(4,minmax(0,1fr));border-bottom:1px solid var(--line)}
.tile:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s2);padding:var(--s6) var(--s8);border-right:1px solid var(--line);min-width:0}
.tile:last-child:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-right:0}
.tiles-3:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:repeat(3,minmax(0,1fr))}
.tile-n:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-9);font-weight:500;font-variant-numeric:tabular-nums lining-nums;color:var(--ink);line-height:1.1}
.tile-n:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) small{font-size:var(--fs-2);color:var(--ink-4);letter-spacing:var(--ls-meta);font-weight:400}
.tile-word:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-6);letter-spacing:var(--ls-2);text-transform:uppercase;padding-top:calc(7px*var(--ui))}
.empty:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;justify-content:center;padding:var(--s8);text-align:center;
  font-family:var(--mono);font-size:var(--fs-2);letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--ink-5);
  border:1px dashed var(--line-3);border-radius:var(--r1)}
.discrep:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;gap:var(--s5);padding:var(--s6) var(--s7);border:1px solid var(--warn);border-radius:var(--r1);
  background:rgba(181,112,90,.08);box-shadow:0 0 24px -14px var(--glow-owed);color:var(--ink-2);font-size:var(--fs-5);line-height:1.55}
.discrep:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .label{color:var(--warn-2);flex:none;padding-top:2px}
.worth:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s6)}
.worth-n:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:calc(56px*var(--ui));font-weight:600;line-height:1;color:var(--gold-warm);text-shadow:0 0 24px rgba(216,180,90,.3)}
.working:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-items:baseline}
.working:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .mono{flex:none;min-width:calc(24px*var(--ui))}

/* 8 · EVIDENCE --------------------------------------------------------------- */
.gallery:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(auto-fill,minmax(calc(240px*var(--ui)),1fr));gap:var(--s6)}
.shot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;aspect-ratio:16/10;overflow:hidden;cursor:pointer;margin:0;border:1px solid var(--hair-2);border-radius:var(--r1);
  background:#0B1310;box-shadow:var(--sink);transition:border-color var(--t),transform var(--t),box-shadow var(--t)}
.shot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) img{width:100%;height:100%;object-fit:cover;filter:saturate(.9) brightness(.92);transition:filter var(--t),transform .5s var(--ease-settle)}
.shot:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold);transform:translateY(-2px);box-shadow:var(--cast)}
.shot:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) img{filter:none;transform:scale(1.03)}
.shot-tags:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;top:var(--s4);left:var(--s4);display:flex;gap:var(--s3)}
.shot-badge:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:calc(3px*var(--ui)) var(--s3);font-family:var(--mono);font-size:var(--fs-1);font-weight:500;letter-spacing:var(--ls-1);
  text-transform:uppercase;border-radius:var(--r1);color:var(--gold-warm);border:1px solid var(--gold-line);background:rgba(10,16,13,.85)}
.shot-badge-read:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--good);border-color:var(--good-2)}

/* 9 · SHELL — nav, the screen grid, the console, the candles, the perches --- */
.topbar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;z-index:5;display:flex;align-items:center;gap:calc(30px*var(--ui));padding:var(--s6) var(--gut);
  background:rgba(9,15,12,.9);border-bottom:1px solid var(--hair)}
.brand:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s5)}
.sigil:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(26px*var(--ui));height:calc(26px*var(--ui));object-fit:contain}
.wordmark:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-6);font-weight:600;letter-spacing:var(--ls-brand);text-transform:uppercase;color:var(--ink);white-space:nowrap}
.nav:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;gap:calc(22px*var(--ui))}
.navtab:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:calc(10.5px*var(--ui));letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--ink-4);
  padding:var(--s2) 0;border-bottom:1px solid transparent;cursor:pointer;white-space:nowrap;transition:color var(--t)}
.navtab:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-2)}
.navtab-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold);border-bottom-color:var(--gold)}
.topbar-end:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:auto;display:flex;align-items:center;gap:var(--s5)}
.aar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:grid;gap:var(--s7);padding:var(--s8) var(--gut) calc(250px*var(--ui));align-items:start;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  grid-template-areas:"head head" "team team" "filing auspex" "evidence record" "notes notes"}
.a-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:head}.a-team:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:team}.a-filing:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:filing}.a-auspex:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:auspex}
.a-evidence:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:evidence}.a-record:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:record}.a-notes:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:notes}
/* the record as one table: what was filed beside what the auspex read, one row per field */
.a-cmp:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:cmp;display:none}
.cmp-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(calc(64px*var(--ui)),calc(92px*var(--ui))) minmax(0,1fr) minmax(0,1fr) calc(100px*var(--ui));min-height:calc(34px*var(--ui))}
.cmp-hdr:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:0;padding-top:var(--s4);padding-bottom:var(--s2);border-bottom-color:var(--rule-head)}
.cmp-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .kv-v{font-size:var(--fs-4);overflow-wrap:normal;word-break:normal}
.cmp-diff:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .kv-v{color:var(--warn-2)}
.cmp-diff:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .kv-v.c-read{color:var(--cyan)}
.cmp-foot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s5);padding:var(--s6) var(--s8) 0}
.head-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;flex-wrap:wrap;gap:var(--s4);padding:var(--s5) var(--s8)}
.roster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:grid;gap:var(--s7);padding:var(--s8) var(--gut) calc(250px*var(--ui));align-items:start;
  grid-template-columns:minmax(0,1fr);grid-template-areas:"head" "standing" "roll"}
.r-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:head}.r-standing:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:standing}.r-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-area:roll}
.companies:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s7);align-items:start}
/* every other screen: one grid, sections in source order, the head across the top */
.screen:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:grid;gap:var(--s7);padding:var(--s8) var(--gut) calc(250px*var(--ui));align-items:start;grid-template-columns:minmax(0,1fr)}
.screen-2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
.screen-wizard:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,1.5fr) minmax(0,1fr)}
.s-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.span-2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-column:1/-1}
.tiles-5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:repeat(5,minmax(0,1fr))}
.field-date:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(110px*var(--ui))}
/* reports */
/* THE BROTHERS COLUMN HOLDS NAMES NOW and takes the width for it. It was
   120px of overlapping initials; it is 260px of face-and-name, which is
   what the owner asked to read. The page got wider in the same pass, so
   nothing else gave anything up for it. */
.rep-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,1.7fr) minmax(0,1fr) minmax(0,3fr) minmax(0,1.1fr) minmax(0,2.4fr) minmax(0,.6fr) minmax(0,.7fr) minmax(0,1.2fr);column-gap:var(--s5);min-height:calc(46px*var(--ui))}
.faces:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center}
.faces:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .avatar{margin-left:calc(-8px*var(--ui));box-shadow:0 0 0 2px var(--bg)}
.faces:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .avatar:first-child{margin-left:0}
.faces:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .meta{margin-left:var(--s3)}
/* the filing wizard */
.steps:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-wrap:wrap;gap:var(--s3)}
.radio:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none;width:calc(14px*var(--ui));height:calc(14px*var(--ui));border-radius:50%;border:1px solid var(--gold-line);box-shadow:var(--sink)}
.radio-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--gold);border-color:var(--gold);box-shadow:0 0 10px var(--glow-live)}
.drop:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s3);min-height:calc(160px*var(--ui));padding:var(--s7);
  border:1px dashed var(--gold-line);border-radius:var(--r2);background:rgba(0,0,0,.18);color:var(--ink-2);text-align:center;
  font-family:var(--mono);font-size:var(--fs-3);letter-spacing:var(--ls-2);text-transform:uppercase}
.thumbs:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s4)}
.thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;aspect-ratio:16/10;overflow:hidden;border:1px solid var(--hair-2);border-radius:var(--r1);background:#0B1310}
.thumb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) img{width:100%;height:100%;object-fit:cover;filter:saturate(.9) brightness(.85)}
.thumb-queued:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) img{opacity:.35}
.thumb-x:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;top:4px;right:4px;display:flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;cursor:pointer;
  border:1px solid var(--line-3);background:rgba(10,16,13,.85);color:var(--ink-2);font-family:var(--mono);font-size:11px}
.thumb-state:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:4px;bottom:6px}

/* ============================ THE AUSPEX SWEEP ============================
 * A PICTURE BEING READ SAYS SO BY BEING SWEPT, which is the one thing on this
 * screen that is true while it happens.
 *
 * What stood here was a progress bar pinned at width:50% -- the template's
 * own comment admitted the number claims nothing, because no sub-picture
 * figure exists anywhere in the payload. A bar that never moves reads as a
 * bar that is stuck, and on the cards a read is slow enough to stare at.
 * Owner: "show the screenshots scanning with that older scanning thing we
 * did..."
 *
 * So the older sweep, which was already in the sheet and used by nothing:
 * `scanv` runs a band of warm light down the frame. It claims no percentage,
 * only that the auspex is on this one. The reduced-motion block already names
 * scanv, so a man who asked for stillness gets a lit frame and no travel.
 * ========================================================================= */
.thumb-lit:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold-line);
box-shadow:0 0 0 1px rgba(255,236,180,.18),0 0 14px -4px rgba(255,236,180,.5)}
.thumb-lit:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) img{filter:saturate(1) brightness(1)}
.thumb-scan:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:0;right:0;top:0;
height:22%;pointer-events:none;
background:linear-gradient(180deg,rgba(255,236,180,0) 0%,rgba(255,236,180,.10) 38%,
rgba(255,236,180,.42) 50%,rgba(255,236,180,.10) 62%,rgba(255,236,180,0) 100%);
animation:scanv 2.4s linear infinite}
.bar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:0;right:0;bottom:0;height:2px;background:rgba(0,0,0,.6)}
.bar-fill:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){height:100%;background:var(--cyan);box-shadow:0 0 8px var(--cyan)}
.picks:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(auto-fill,minmax(calc(170px*var(--ui)),1fr));gap:var(--s3)}
.pick:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s3);min-width:0;padding:var(--s3) var(--s4);cursor:pointer;border:1px solid var(--hair);border-radius:var(--r1);background:rgba(0,0,0,.14)}
.pick:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold-line)}
.pick-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold);background:var(--lift);box-shadow:0 0 14px -6px var(--glow-live)}
.chosen:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-wrap:wrap;gap:var(--s3)}
.chip-x:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:pointer;color:var(--ink-4);padding-left:var(--s2)}
.wiz-nav:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s4);padding-top:var(--s5);border-top:1px solid var(--hair)}
.auspex-total:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s5);padding-top:var(--s6);border-top:1px solid var(--rule-head)}
/* the filing route: no nav, a slim bar, the two plates, a foot with one key */
.filing:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;min-height:calc(760px*var(--ui))}
.filing-bar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){justify-content:flex-start;gap:var(--s5)}
.filing:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .screen{flex:1;width:100%;padding-bottom:var(--s9);align-content:start}
.filing-foot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s5);padding:var(--s5) var(--gut);border-top:1px solid var(--gold-line);background:rgba(9,15,12,.94)}
/* A STATS CARD ON THE FILING'S AUSPEX PANEL: the man, then the crew card's own
   nine cells. Owner: "part of the auspex is all the stats." */
.filing-card:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s3);padding-bottom:var(--s5);border-bottom:1px solid var(--hair)}
.seal:disabled:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:not-allowed;filter:saturate(.35) brightness(.7);transform:none;box-shadow:inset 0 1px 0 rgba(255,236,180,.2),0 4px 0 #3E2C10}
/* strategium */
.tree:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s4)}
.tree-l:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s4);margin-left:calc(22px*var(--ui));padding-left:calc(18px*var(--ui));border-left:1px solid var(--gold-line)}
.post:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s4);min-width:0;padding:var(--s4) var(--s5);border:1px solid var(--line-3);border-radius:var(--r2);background:var(--panel-3)}
.post-name:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-6);font-weight:600;color:var(--ink);line-height:1.1}
.post-title:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--gold-ash)}
.post-desk:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold-line);box-shadow:0 0 20px -10px var(--glow-live)}
.grip:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none;color:var(--ink-5);font-family:var(--mono);font-size:var(--fs-4);cursor:grab;letter-spacing:-.2em}
.grip:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{content:"⋮⋮"}
.days:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(52px*var(--ui));justify-content:center;display:inline-flex;align-items:center}
.desk-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,1.2fr) minmax(0,1fr) auto}
/* battle honours */
.honours:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s5)}
.honour:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s3);min-width:0;padding:var(--s6) var(--s6) var(--s5);border:1px solid var(--line-3);border-radius:var(--r2);background:var(--panel-3)}
.honour-name:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-7);font-weight:600;color:var(--gold-warm);line-height:1.1}
.honour-quest:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-3);line-height:1.45;color:var(--ink-3)}
.honour-holder:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s3);min-width:0;padding-top:var(--s3);border-top:1px solid var(--hair);font-family:var(--mono);font-size:var(--fs-2);color:var(--ink-2)}
.honour-open:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){opacity:.55}
.honour-open:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .honour-name{color:var(--ink-3)}
.stat-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,2.9fr) minmax(0,1fr) repeat(8,minmax(0,.78fr)) minmax(0,1fr);column-gap:var(--s4);min-height:calc(42px*var(--ui))}
/* The campaigns' board carries one more figure than the Eternal War's: gene-seed. Same weight as the rest, so a phone's own three-column rule still wins. */
.stat-pve:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,2.9fr) minmax(0,1fr) repeat(10,minmax(0,.78fr)) minmax(0,1fr)}
/* the codex */
.codex-n:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-10);font-weight:400;line-height:1;color:var(--gold);opacity:.8}
.tariff:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,1fr) auto;min-height:calc(40px*var(--ui))}
.field-n:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;justify-content:center;width:calc(56px*var(--ui));min-height:calc(30px*var(--ui));padding:0;color:var(--gold-warm);font-weight:700;font-size:var(--fs-5)}
.law:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;flex-wrap:wrap;gap:var(--s4);padding:var(--s5) var(--s7);border:1px solid var(--warn);border-radius:var(--r1);background:rgba(181,112,90,.08);box-shadow:0 0 24px -14px var(--glow-owed)}
.law:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .lead{color:var(--ink)}
.rule-foot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-wrap:wrap;align-items:center;gap:var(--s4) var(--s7);padding:var(--s5) var(--s7);border-top:1px solid var(--hair)}
.proofs:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;flex-wrap:wrap;gap:var(--s3)}
/* authority */
.cap-wrap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){overflow-x:auto}
.cap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:minmax(calc(180px*var(--ui)),1.6fr) repeat(8,minmax(calc(64px*var(--ui)),1fr));min-width:max-content}
.cap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > *{display:flex;align-items:center;justify-content:center;gap:var(--s2);min-height:calc(40px*var(--ui));padding:0 var(--s3);border-bottom:1px solid var(--rule);border-right:1px solid var(--hair)}
.cap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > *:nth-child(9n+1){justify-content:flex-start;padding-left:var(--s7)}
.cap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > *:nth-child(9n){border-right:0}
.cap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .cap-h{min-height:0;padding-top:var(--s4);padding-bottom:var(--s3);border-bottom-color:var(--rule-head);text-align:center}
.yes:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:8px;height:8px;border-radius:50%;background:var(--gold);box-shadow:0 0 8px var(--glow-live)}
.yes-named:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--esc-lit);box-shadow:0 0 8px var(--glow-esc)}
.no:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:6px;height:1px;background:var(--ink-6)}
.no-named:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:8px;height:8px;border-radius:50%;border:1px solid var(--bad-lit);background:transparent}
.toggle:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;flex:none;width:calc(34px*var(--ui));height:calc(18px*var(--ui));cursor:pointer;border-radius:999px;border:1px solid var(--gold-line);background:rgba(0,0,0,.4);box-shadow:var(--sink)}
.toggle:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";position:absolute;top:2px;left:2px;width:calc(12px*var(--ui));height:calc(12px*var(--ui));border-radius:50%;background:var(--ink-4);transition:left var(--t),background var(--t)}
.toggle-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{left:calc(100% - 14px*var(--ui) - 2px);background:var(--gold);box-shadow:0 0 8px var(--glow-live)}
/* AND THE SAME FAULT WAITING IN THE SCOPE ROW: 96 fixed pixels for a word
   the chapter chooses -- "his company", "his command", "the chapter". Words
   get max-content; only figures get a number. */
.scope-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,1fr) max-content minmax(0,1.6fr)}
/* THE RANK MARK IS AS WIDE AS THE RANK, NOT 84px. It was a fixed track and
   COMPANY MASTER wants 97 of them, so the mark printed 13px into "by rank"
   beside it -- "COMPANY MASTEBy rank" on the Inner Circle. A word that is
   whatever length the chapter gave it cannot have a number assigned to it;
   `max-content` is the honest track, and the name beside it gives the room.
   The last column is the × and stays `auto` -- it is a glyph. */
.named-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:minmax(0,2fr) minmax(0,1.2fr) max-content minmax(0,1fr) auto}
/* audit */
.log-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:calc(112px*var(--ui)) minmax(0,1.1fr) calc(128px*var(--ui)) minmax(0,1.1fr) minmax(0,1.4fr);column-gap:var(--s5);min-height:calc(44px*var(--ui))}
.quote:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s4) var(--s6);border-left:2px solid var(--gold-dim);font-family:var(--serif);font-size:var(--fs-6);font-style:italic;line-height:1.4;color:var(--ink-2)}
/* the gate */
.signin:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;place-items:center;min-height:calc(720px*var(--ui));padding:var(--s9) var(--gut) calc(200px*var(--ui))}
.gate:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;align-items:center;gap:var(--s6);width:min(100%,calc(360px*var(--ui)));text-align:center}
.shield:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(120px*var(--ui));height:auto;filter:drop-shadow(0 18px 30px rgba(0,0,0,.9))}
.field-code:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(200px*var(--ui));min-height:calc(44px*var(--ui));text-align:center;letter-spacing:.3em;font-size:var(--fs-7)}
.gate-or:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s5);width:100%}
.gate-or:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before,.gate-or:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";flex:1;height:1px;background:var(--hair-2)}
.company-open:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-column:1/-1}
.team-body:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s5)}
.console:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;right:calc(64px*var(--ui));bottom:0;width:calc(210px*var(--ui));pointer-events:none;z-index:2}
.console:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) img{width:100%;height:auto;filter:brightness(.6) saturate(.55) contrast(1.05);
  -webkit-mask-image:linear-gradient(180deg,#000 80%,transparent);mask-image:linear-gradient(180deg,#000 80%,transparent)}
.console-glass:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:21%;top:17.5%;width:58%;height:28%;border-radius:4px;
  background:radial-gradient(ellipse at 30% 20%,rgba(var(--hue-39,120,150,140),.14),rgba(0,0,0,.7) 60%);box-shadow:inset 0 0 20px rgba(0,0,0,.8)}
.candles:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;right:calc(18px*var(--ui));bottom:calc(10px*var(--ui));display:flex;align-items:flex-end;gap:var(--s4);z-index:2;pointer-events:none}
.candle:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:block;width:calc(7px*var(--ui));height:calc(30px*var(--ui));border-radius:2px 2px 1px 1px;
  background:linear-gradient(180deg,#EFE3C8,#C9B48E 70%,#9B8B6C)}
.candle:nth-child(2):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){height:calc(22px*var(--ui))}
.candle:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{content:"";position:absolute;left:50%;top:calc(-13px*var(--ui));width:calc(7px*var(--ui));height:calc(14px*var(--ui));
  margin-left:calc(-3.5px*var(--ui));border-radius:50% 50% 50% 50%/65% 65% 35% 35%;
  background:radial-gradient(circle at 50% 75%,#FFF4C2 0,#F0B24A 45%,rgba(226,160,58,0) 75%);
  box-shadow:0 0 22px 8px rgba(226,160,58,.32);transform-origin:50% 100%;animation:flicker 1.7s ease-in-out infinite alternate}
.candle:nth-child(2):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{animation-duration:2.3s;animation-delay:-.8s}
/* where the servo-skull lands. Markers only — the solver is yours. */
.perch:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:none;position:absolute;z-index:6;align-items:center;gap:var(--s2);padding:3px 7px;border:1px dashed rgba(var(--hue-4,59,200,178),.65);border-radius:999px;
  background:rgba(5,11,9,.85);font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--cyan);pointer-events:none;text-shadow:none}
.show-perches:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .perch{display:inline-flex}
.perch:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;box-shadow:0 0 6px currentColor}
.perch-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){top:-9px;right:calc(140px*var(--ui))}
.perch-team:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){top:-9px;left:42%}
.perch-gallery:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){top:-9px;right:calc(40px*var(--ui))}
.perch-console:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){top:5%;left:-6%}

/* states the wiring needs: loading, outage, forbidden ------------------------ */
.skeleton:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:block;height:calc(12px*var(--ui));border-radius:2px;background:linear-gradient(90deg,rgba(var(--hue-1,178,201,177),.06),rgba(var(--hue-1,178,201,177),.14),rgba(var(--hue-1,178,201,177),.06));background-size:200% 100%;animation:shimmer 1.4s linear infinite}
.skeleton-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(46px*var(--ui));gap:var(--s5)}
.skeleton-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .skeleton{flex:1}
.banner:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s5);padding:var(--s4) var(--gut);border-bottom:1px solid var(--warn);background:rgba(181,112,90,.10);color:var(--ink);font-family:var(--mono);font-size:var(--fs-3)}
.banner-esc:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-bottom-color:var(--esc);background:rgba(122,146,196,.10)}
.empty-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--bad);color:var(--bad-lit)}

/* ==========================================================================
   THE LOOK — the agreed direction, folded onto the primitives above.
   Flat plates with a gold hairline and a centred serif head; three depth
   states (raised / recessed / lit); tables in a recess; men as arched cards.
   ========================================================================== */
html,body{margin:0}
body.ledger,.ledger.page{min-height:100vh}
.ledger .crew-credited .crew-state .mark{width:calc(62px*var(--ui));height:calc(62px*var(--ui));border-radius:50%;justify-content:center;padding:0;
  font-size:calc(7.5px*var(--ui));letter-spacing:.08em;border:2px solid #6A4A12;color:var(--ink-on-gold);text-shadow:none;
  background:radial-gradient(circle at 35% 30%,#F3DA9A,#C9A45C 45%,#8A6A32 82%,#6A4A12);
  box-shadow:0 4px 10px rgba(0,0,0,.65),inset 0 0 0 4px rgba(255,236,180,.28),inset 0 0 0 5px rgba(90,60,10,.55);transform:rotate(-8deg)}
.ledger .crew-credited .crew-state .mark-lamp::before{display:none}
.ledger{--panel-4:transparent;--face:none;--line-3:transparent;--edge-lit:transparent;--edge-dark:transparent;--rivet:transparent;--rivet-shade:transparent;
  --cast:none;--rule-head:rgba(201,164,92,.35);--r1:0;
  background:#0A110E;
  background-image:radial-gradient(ellipse 40% 60% at 50% 0,rgba(226,192,121,.11),transparent 60%),
    linear-gradient(90deg,transparent 22%,rgba(226,192,121,.035) 34%,transparent 46%),
    linear-gradient(90deg,transparent 54%,rgba(226,192,121,.03) 66%,transparent 78%),
    radial-gradient(ellipse 60% 40% at 50% 100%,rgba(196,138,58,.12),transparent 60%)}
/* THE AAR IS TWO COLUMNS OF READING and keeps its own, narrower cap: the
   record and the evidence are read across, not scanned down. */
.ledger .aar{max-width:calc(1500px * var(--ui));margin:0 auto;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  grid-template-areas:"head head" "team team" "filing auspex" "evidence evidence" "record record" "notes notes";
  gap:calc(44px*var(--ui)) calc(48px*var(--ui));padding-top:calc(44px*var(--ui))}
.ledger .plate{box-shadow:none;border:0;border-top:1px solid var(--gold-line);border-radius:0;background:transparent}
.ledger .plate-head{flex-direction:column;align-items:center;text-align:center;gap:var(--s3);padding:calc(22px*var(--ui)) 0 calc(18px*var(--ui));border-bottom:0}
.ledger .plate-head-meta{margin-left:0}
.ledger .plate-head::after{content:"";width:calc(56px*var(--ui));height:1px;background:var(--gold);margin-top:var(--s3);box-shadow:0 0 12px var(--glow-live)}
.ledger .heading{display:block;font-size:var(--fs-9);font-weight:400;letter-spacing:var(--ls-3);color:var(--ink);text-shadow:0 0 30px rgba(226,192,121,.25)}
.ledger .heading::first-letter{color:var(--gold-warm)}
.ledger .a-head .heading{font-size:var(--fs-10);letter-spacing:var(--ls-2)}
.ledger .a-head .plate-head{padding-top:0}
.ledger .plate-body{padding:var(--s6) 0 0}
/* THE PICTURES GET THEIR OWN GUTTERS, because the plate has none to give.
   The rule above takes the plate body's side padding to ZERO inside .ledger,
   which is right for a full-bleed roll and wrong for a strip of pictures: the
   first one sat hard against the left edge and the last against the right.
   Measured on a probe carrying all four sheets and the real nesting: 0px
   either side, 10px above, 8px between. Owner: "these pictures need more
   padding on all deployments." The same shape as the rail's gutters two
   hundred lines down -- a recess gives them back, and nothing else does. */
.ledger .thumbs{padding:var(--s5) var(--s7) var(--s7);gap:var(--s5)}
.ledger .plate-body-flush{padding:0}
.ledger .rail,.ledger .objs{padding-left:0;padding-right:0}
.ledger .tiles{border-bottom:0}
.ledger .tile{align-items:center;text-align:center;border-right-color:var(--gold-line)}
.ledger .tile-n{font-family:var(--serif);font-size:calc(34px*var(--ui));font-weight:500}
.ledger .head-acts{justify-content:center;padding-left:0;padding-right:0}
.ledger .head-acts .spacer{display:none}
.ledger .team-body{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:calc(28px*var(--ui));align-items:end;padding-top:calc(40px*var(--ui))}
.ledger .crew-group{display:none}
.ledger .crew-list{display:contents}
.ledger .crew-list-mine .crew-card{order:2;transform:translateY(calc(-18px*var(--ui)))}
.ledger .crew-list-other .crew-card:first-child{order:1}
.ledger .crew-list-other .crew-card:last-child{order:3}
/* THE EMPTY STATE IS A SENTENCE, NOT A MAN, SO IT DOES NOT TAKE A MAN'S COLUMN.
   `.crew-list` is display:contents, so this div becomes a direct child of the
   three-column team grid and was being placed in ONE of those columns -- a
   stubby dashed box in the left third with two columns of dark beside it, and
   the words centred inside the third rather than under the panel. It spans the
   row like `.add-man` below, which is the same case: a thing that belongs to the
   whole fireteam rather than to one place in it. `order:3` puts it after the
   card it is explaining instead of competing with it for column one. */
/* ORDER 4, BEHIND THE CARDS, AND THAT IS THE WHOLE POINT OF THE NUMBER.
   At order:3 this sat BETWEEN the ordered cards -- others are 1 and 3, yours is
   2 -- and a full-row item cannot share a row, so grid auto-placement broke the
   row at the empty and put every card on a line of its own: three arches
   stacked down the left third with a full-width dashed box among them. `.add-man`
   below is order:4 for exactly this reason. A sentence about the fireteam comes
   after the fireteam. */
/* The pictures' names that are not on the run: a full-width band under the cards. */
.ledger .team-pictures{grid-column:1/-1;order:5;align-self:start}
.ledger .crew-list .empty{grid-column:1/-1;order:4;align-self:center;justify-self:stretch;
  min-height:calc(200px*var(--ui))}
.ledger .add-man{grid-column:1/-1;justify-self:center;order:4}
.ledger .crew-card{grid-template-columns:minmax(0,1fr);grid-template-areas:"id" "state" "stats" "ask" "acts" "note";justify-items:center;text-align:center;
  padding:calc(48px*var(--ui)) calc(22px*var(--ui)) calc(22px*var(--ui));border-radius:calc(160px*var(--ui)) calc(160px*var(--ui)) 0 0;
  background:linear-gradient(180deg,rgba(226,192,121,.07),rgba(11,18,16,.85) 30%,rgba(7,16,13,.96));background-image:none;
  border:1px solid var(--gold-line);box-shadow:none}
.ledger .crew-card::before{content:attr(data-band);position:absolute;top:calc(-30px*var(--ui));left:0;right:0;text-align:center;
  font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-head);text-transform:uppercase;color:var(--gold-ash)}
.ledger .crew-mine::after{display:none}
.ledger .crew-mine{border-color:var(--gold);box-shadow:0 0 70px -20px var(--glow-live),inset 0 1px 0 rgba(255,228,158,.25)}
.ledger .crew-mine::before{color:var(--gold-warm)}
.ledger .crew-other{opacity:.62}
.ledger .crew-id{flex-direction:column;text-align:center}
.ledger .crew-id .col{align-items:center}
.ledger .crew-id .avatar{width:calc(72px*var(--ui));height:calc(72px*var(--ui));font-size:var(--fs-8);border-color:var(--gold-line)}
.ledger .crew-id .row{justify-content:center}
.ledger .crew-name{font-size:var(--fs-8);font-weight:500}
.ledger .crew-state{align-items:center;text-align:center}
.ledger .crew-stats{grid-template-columns:repeat(3,minmax(0,1fr));background:transparent;border:0;box-shadow:none;gap:var(--s2) var(--s5);width:100%}
.ledger .stat{background:transparent;padding:var(--s2) 0;align-items:center;border-bottom:1px solid var(--hair)}
.ledger .stat-v{font-family:var(--serif);font-size:var(--fs-7)}
.ledger .crew-ask{width:100%;text-align:left}
.ledger .crew-acts{justify-content:center;width:100%}
.ledger .crew-pts{margin-left:0;width:100%;justify-content:center}
.ledger .seal{border-radius:999px;background:transparent;color:var(--gold-warm);border:1px solid var(--gold);
  font-family:var(--serif);font-size:var(--fs-7);font-weight:600;letter-spacing:var(--ls-2);min-width:calc(210px*var(--ui));text-shadow:none;
  box-shadow:0 0 24px -10px var(--glow-live)}
.ledger .seal:hover{background:var(--gold);color:var(--ink-on-gold);filter:none}
.ledger .seal:active{transform:translateY(2px);box-shadow:inset 0 2px 6px rgba(0,0,0,.4)}
.ledger .btn{border-radius:999px;background:transparent;box-shadow:none;border-color:var(--line-2)}
.ledger .btn:hover{border-color:var(--gold-line)}
.ledger .field{background:rgba(7,16,13,.8);border-radius:0;border-color:var(--line-2)}
.ledger .crew-note{width:100%}
.ledger .topbar{background:transparent;border-bottom-color:var(--gold-line);justify-content:center}
.ledger .topbar-end{margin-left:0}
.ledger .gallery{grid-template-columns:repeat(4,minmax(0,1fr))}
.ledger .worth{justify-content:center;text-align:left}
.ledger .discrep{border:0;border-left:2px solid var(--warn);border-radius:0;background:transparent;box-shadow:none}
.ledger .empty{border-radius:0}
.ledger .mark{border-radius:999px}
.ledger .aar{gap:calc(22px*var(--ui)) calc(26px*var(--ui));padding-top:var(--s8);max-width:calc(1560px * var(--ui))}
.ledger .heading{font-size:var(--fs-7);letter-spacing:var(--ls-2)}
.ledger .a-head .heading{font-size:var(--fs-8)}
.ledger .plate-head{padding:calc(12px*var(--ui)) 0 calc(9px*var(--ui));gap:var(--s2)}
.ledger .plate-head::after{width:calc(40px*var(--ui));margin-top:var(--s2)}
.ledger .plate-body{padding-top:var(--s5);gap:var(--s5)}
.ledger .tile{padding:var(--s5) var(--s6)}
.ledger .tile-n{font-size:calc(22px*var(--ui))}
.ledger .kv{min-height:calc(34px*var(--ui))}
.ledger .kv-v{font-size:var(--fs-4)}
.ledger .team-body{gap:var(--s7);padding-top:calc(28px*var(--ui))}
/* THE FIRETEAM'S OWN GUTTERS. The plate body gives none (padding 0 at the sides,
   by design), so the first tombstone sat hard against the plate's left edge.
   Owner, 2026-09-26: "please fix reports and stratigium paddings." */
.ledger .team-body{padding-left:calc(36px*var(--ui));padding-right:calc(36px*var(--ui));padding-bottom:calc(36px*var(--ui));column-gap:calc(40px*var(--ui))}
/* YOUR CHAIN AND YOUR NEXT PROMOTION, side by side: the chain keeps its own
   width, the promotion takes the rest. One column on a phone. */
.ledger .chain-body{display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--s8);align-items:start}
.ledger .promo{display:flex;flex-direction:column;gap:var(--s4);max-width:calc(460px*var(--ui));padding:0 var(--s8) var(--s7) 0;justify-self:end;width:100%}
.ledger .promo-row{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s5)}
.ledger .promo-rank{font-family:var(--serif);font-size:calc(19px*var(--ui));color:var(--ink)}
.ledger .promo-rank-next{color:var(--gold-warm);text-shadow:0 0 14px var(--glow-live)}
.ledger .promo-meter{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--s2) var(--s4);align-items:baseline;color:var(--gold-lit)}
.ledger .promo-meter .promo-bar{grid-column:1/-1;display:block}
.ledger .promo-k{font-family:var(--mono);font-size:max(10px,var(--fs-3));letter-spacing:var(--ls-meta);text-transform:uppercase;color:var(--ink-3)}
.ledger .promo-v{font-family:var(--mono);font-variant-numeric:tabular-nums;color:var(--ink-2)}
.ledger .promo-say{font-family:var(--mono);font-size:max(11px,var(--fs-3));color:var(--ink-3)}
.ledger .promo-ready{color:var(--good);text-shadow:0 0 12px var(--glow-read)}
.ledger .promo-steps{list-style:none;margin:var(--s3) 0 0;padding:var(--s4) 0 0;border-top:1px solid var(--hair);display:flex;flex-direction:column;gap:var(--s3)}
.ledger .promo-steps li{display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--s4);align-items:baseline;color:var(--ink-2)}
.ledger .promo-n{font-family:var(--serif);color:var(--gold-warm);font-size:calc(16px*var(--ui))}
.ledger .promo-due{grid-template-columns:minmax(0,1fr) auto;align-items:center}
.ledger .promo-rung .field-n{width:calc(78px*var(--ui))}
.ledger .promo-save{padding:var(--s5) var(--s8) var(--s6)}
.ledger .crew-card{padding:calc(26px*var(--ui)) var(--s7) var(--s7);border-radius:calc(84px*var(--ui)) calc(84px*var(--ui)) 0 0;gap:var(--s4)}
/* ROOM INSIDE THE TOMBSTONE. Owner, 2026-09-27: "can we get some padding?" --
   the note box and the four keys sat close to the card's edges and foot. */
.ledger .crew-card{padding:calc(28px*var(--ui)) calc(28px*var(--ui)) calc(28px*var(--ui));gap:var(--s5)}
.ledger .crew-card::before{top:calc(-22px*var(--ui))}
.ledger .crew-list-mine .crew-card{transform:translateY(calc(-10px*var(--ui)))}
.ledger .crew-id .avatar{width:calc(44px*var(--ui));height:calc(44px*var(--ui));font-size:var(--fs-4)}
.ledger .crew-name{font-size:var(--fs-6)}
.ledger .stat{padding:var(--s1) 0}
.ledger .stat-v{font-size:var(--fs-5)}
.ledger .seal{min-height:calc(38px*var(--ui));min-width:calc(160px*var(--ui));font-size:var(--fs-5)}
.ledger .btn{min-height:calc(30px*var(--ui))}
.ledger .worth-n{font-size:calc(36px*var(--ui))}
.ledger .gallery{gap:var(--s4)}
.ledger .crew-credited .crew-state .mark{width:calc(50px*var(--ui));height:calc(50px*var(--ui));font-size:calc(6.5px*var(--ui))}
.ledger .crew-mine{border-color:var(--gold);
  box-shadow:0 0 90px -16px rgba(216,174,74,.5),0 24px 60px -30px rgba(216,174,74,.35),inset 0 1px 0 rgba(255,228,158,.3)}
.ledger .crew-mine .avatar{border-color:var(--gold);box-shadow:0 0 24px -4px var(--glow-live)}
.ledger .crew-mine .crew-name{color:var(--gold-warm);text-shadow:0 0 18px rgba(216,174,74,.35)}
.ledger .crew-pts{margin-left:auto}
.ledger .crew-acts{flex-direction:row;flex-wrap:wrap;justify-content:center}
.ledger .a-team.plate-lit{border-top-color:var(--gold)}
.ledger{--panel-4:rgba(var(--hue-74,12,22,17),.74);
  --catch:inset 0 1px 0 rgba(255,228,158,.12);--catch-lit:inset 0 1px 0 rgba(255,228,158,.3);
  --cast:0 18px 32px -18px rgba(0,0,0,.92);--cast-sm:0 6px 12px -6px rgba(0,0,0,.85);
  --glow:0 0 80px -16px rgba(216,174,74,.5);--glow-sm:0 0 24px -6px rgba(216,174,74,.5);
  --sink:inset 0 1px 6px rgba(0,0,0,.45),inset 0 -1px 0 rgba(255,228,158,.05);
  --recess-bg:linear-gradient(180deg,rgba(0,0,0,.22),rgba(7,16,13,.3) 40%,rgba(0,0,0,.26));
  --recess-line:rgba(var(--hue-1,178,201,177),.07);--raise-line:rgba(var(--hue-1,178,201,177),.2);--edge-line:rgba(var(--hue-1,178,201,177),.06)}
.ledger .aar{grid-template-areas:"head head" "team team" "cmp evidence" "notes notes"}
.ledger .a-cmp{display:flex}
.ledger .a-filing,.ledger .a-auspex,.ledger .a-record{display:none}
.ledger .a-cmp,.ledger .a-evidence{align-self:stretch}
.ledger .gallery{grid-template-columns:repeat(2,minmax(0,1fr))}
.ledger :is(.a-head,.r-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.s-head) .plate-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex-direction:row;align-items:center;justify-content:flex-start;text-align:left;padding:var(--s6) var(--s8);gap:var(--s7);flex-wrap:wrap}
.ledger :is(.a-head,.r-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.s-head) .add-man:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:auto}
.ledger :is(.a-head,.r-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.s-head) .plate-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{display:none}
.ledger :is(.a-head,.r-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.s-head) .heading:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-block;font-size:var(--fs-8);letter-spacing:var(--ls-2);line-height:1}
.ledger .crew-card{border-color:var(--raise-line);background:linear-gradient(180deg,rgba(226,192,121,.08),rgba(11,18,16,.9) 30%,rgba(7,16,13,.98));box-shadow:var(--catch),var(--cast)}
.ledger .crew-mine{border-color:var(--gold);box-shadow:var(--catch-lit),var(--cast),var(--glow)}
.ledger .avatar{border-color:var(--raise-line);box-shadow:var(--catch),var(--cast-sm)}
.ledger .crew-mine .avatar{border-color:var(--gold);box-shadow:var(--catch),var(--cast-sm),var(--glow-sm)}
.ledger .shot{border-color:var(--raise-line);border-radius:var(--r2);box-shadow:var(--catch),var(--cast)}
.ledger .shot:hover{border-color:var(--gold);box-shadow:var(--catch-lit),var(--cast),var(--glow-sm)}
.ledger .btn{border-color:var(--raise-line);background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(0,0,0,.28));box-shadow:var(--catch),0 2px 0 rgba(0,0,0,.55),var(--cast-sm)}
.ledger .btn:hover{border-color:var(--gold-line)}
.ledger .btn:active{transform:translateY(2px);box-shadow:var(--sink)}
.ledger .btn-quiet{border-color:transparent;background:transparent;box-shadow:none}
.ledger .seal{border-color:#6D5220;color:var(--ink-on-gold);background:linear-gradient(180deg,var(--gold-warm),var(--gold-lit) 45%,#A9832A);
  box-shadow:inset 0 1px 0 rgba(255,236,180,.6),0 4px 0 #5A4118,var(--cast),var(--glow-sm)}
.ledger .seal:hover{background:linear-gradient(180deg,#F0D48E,var(--gold-lit) 45%,#A9832A);color:var(--ink-on-gold)}
.ledger .seal:active{transform:translateY(4px);box-shadow:inset 0 1px 0 rgba(255,236,180,.3),inset 0 2px 6px rgba(0,0,0,.35),0 0 0 #5A4118,var(--cast-sm),var(--glow-sm)}
.ledger .cmp,.ledger .roll,.ledger .a-notes .plate-body,.ledger .crew-stats{background:var(--recess-bg);border:1px solid var(--recess-line);border-radius:var(--r2);box-shadow:var(--sink)}
.ledger .a-notes .plate-body{padding:0;gap:0}
.ledger .crew-stats{padding:var(--s2) var(--s4)}
.ledger .stat{border-bottom-color:var(--edge-line)}
.ledger .field{background:rgba(4,10,8,.7);border:1px solid var(--recess-line);box-shadow:var(--sink)}
.ledger .field:focus{border-color:var(--gold)}
.ledger .cmp-row{grid-template-columns:calc(92px*var(--ui)) minmax(0,1fr) minmax(0,1fr) calc(100px*var(--ui))}
.ledger :is(.cmp,.roll) .rail:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-left:var(--s7);padding-right:var(--s7);border-bottom-color:var(--edge-line);box-shadow:none}
.ledger :is(.cmp-hdr,.roll-hdr):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(0,0,0,.14);border-bottom-color:var(--rule-head)}
.ledger .cmp .cmp-diff{background:linear-gradient(90deg,rgba(181,112,90,.07),transparent 55%);box-shadow:inset 2px 0 0 rgba(181,112,90,.7)}
.ledger .cmp-foot{padding:var(--s7);border-top:1px solid var(--edge-line)}
.ledger .a-notes .empty{border:0;border-bottom:1px solid var(--edge-line);border-radius:0;padding:var(--s8) var(--s7);background:transparent;box-shadow:none}
.ledger .a-notes .row{padding:var(--s5) var(--s7)}
.ledger .discrep{background:transparent;border:0;border-left:2px solid var(--warn);border-radius:0;box-shadow:none}
.ledger .worth-n{text-shadow:0 0 22px rgba(216,180,90,.3),0 1px 0 rgba(0,0,0,.7)}
.ledger .empty{border-color:var(--edge-line)}
.ledger .roster{max-width:var(--page-w);margin:0 auto;gap:calc(22px*var(--ui)) calc(26px*var(--ui));padding-top:var(--s8)}
.ledger .standing{padding-top:calc(34px*var(--ui))}
.ledger .standing .crew-first{transform:translateY(calc(-10px*var(--ui)))}
.ledger .standing .crew-state .worth-n{font-size:calc(40px*var(--ui))}
.ledger .rail-you{background:linear-gradient(90deg,rgba(216,174,74,.10),transparent 60%);box-shadow:inset 3px 0 0 var(--gold),0 0 40px -10px var(--glow-live)}
.ledger .rail-you .avatar{border-color:var(--gold);box-shadow:var(--catch),var(--cast-sm),var(--glow-sm)}
.ledger .rail-detail{background:rgba(0,0,0,.14);border-bottom-color:var(--edge-line)}
.ledger .rail-detail .crew-stats{background:transparent;border:0;box-shadow:none;padding:0}
.ledger .avatar-officer{border-color:var(--gold)}
.ledger .avatar-sgt{border-color:var(--gold-dim)}
.ledger .company .plate-head{padding-top:calc(14px*var(--ui))}
.ledger .company .plate-body{padding-top:var(--s4)}
.ledger .company .roll{margin:0 var(--s6) var(--s6)}
.ledger .company .empty{margin:0 var(--s6) var(--s6)}
.ledger .screen{max-width:var(--page-w);margin:0 auto;gap:calc(22px*var(--ui)) calc(26px*var(--ui));padding-top:var(--s8)}
.ledger .codex .plate-head .heading{font-size:var(--fs-9)}
.ledger .codex .s-head .heading{font-size:var(--fs-8)}
.ledger :is(.honour,.post:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.thumb):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--raise-line);box-shadow:var(--catch),var(--cast-sm)}
.ledger :is(.honour,.post):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:linear-gradient(180deg,rgba(226,192,121,.06),rgba(11,18,16,.9) 30%,rgba(7,16,13,.98))}
.ledger :is(.post-desk,.honour-held):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold-line);box-shadow:var(--catch-lit),var(--cast-sm),var(--glow-sm)}
.ledger .drop{background:var(--recess-bg);box-shadow:var(--sink)}
.ledger .pick{background:transparent;border-color:var(--recess-line)}
.ledger .pick-on{border-color:var(--gold);background:var(--lift)}
.ledger .cap > *{border-bottom-color:var(--edge-line);border-right-color:var(--edge-line)}
.ledger .cap > .cap-h{background:rgba(0,0,0,.14);border-bottom-color:var(--rule-head)}
.ledger .law{background:rgba(0,0,0,.16);border-color:rgba(181,112,90,.35)}
.ledger .signin .wordmark{font-size:var(--fs-8);letter-spacing:var(--ls-3)}
.ledger .gate .seal{min-width:calc(240px*var(--ui))}
.ledger .auspex-total .worth-n{font-size:calc(40px*var(--ui))}
.ledger .field-n{background:rgba(4,10,8,.7)}
/* FILING STAYS NARROW ON PURPOSE. It is a form answered one question at a
   time, and a form stretched across a monitor is a form whose label and
   field are a foot apart. */
.ledger.filing .screen{max-width:calc(1180px * var(--ui))}
.ledger .filing-bar{justify-content:flex-start;padding-top:var(--s5);padding-bottom:var(--s5)}
.ledger .filing-bar .add-man{margin-left:auto}
.ledger .filing-foot .seal{margin-left:0}
.ledger .filing-bar .heading{font-size:var(--fs-6)}
.ledger.filing .plate-head .label{margin-bottom:var(--s1)}

/* 10 · MOTION --------------------------------------------------------------- */
@keyframes flicker{0%{transform:scaleY(1) scaleX(1);opacity:.95}50%{transform:scaleY(1.12) scaleX(.9) translateX(.5px);opacity:1}100%{transform:scaleY(.94) scaleX(1.05);opacity:.9}}
@keyframes shimmer{from{background-position:200% 0}to{background-position:-200% 0}}
@keyframes enter{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  .candle:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{animation:none}
  .shot:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transform:none}
  .rail:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.mark:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.btn:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.seal:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.shot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) img{transition:none}
}

/* PHONE — one container query on .ledger; the same rules fire on a real phone and in a 390px frame */
@container ledger (max-width:720px){
  .ledger .topbar{flex-wrap:wrap;gap:var(--s4) var(--s6);padding:var(--s5) var(--s6);justify-content:flex-start}
  .ledger .nav{order:3;width:100%;overflow-x:auto;gap:var(--s6);padding-bottom:var(--s2);scrollbar-width:none}
  .ledger .topbar-end{margin-left:auto}
  .ledger .topbar-end .btn{display:none}
  .ledger .aar{grid-template-columns:minmax(0,1fr);grid-template-areas:"head" "team" "cmp" "filing" "auspex" "evidence" "record" "notes";
    gap:var(--s6);padding:var(--s6) var(--s5) calc(150px*var(--ui));max-width:none}
  .ledger .plate-head{padding:var(--s6) var(--s6) var(--s4);gap:var(--s2) var(--s4)}
  .ledger .plate-head-sub,.ledger .plate-head-meta{width:100%;margin-left:0}
  .ledger .plate-body{padding:var(--s6) var(--s6) var(--s7);gap:var(--s5)}
  .ledger .rail,.ledger .objs{padding-left:var(--s6);padding-right:var(--s6)}
  .ledger .heading{font-size:var(--fs-5)}
  .ledger .a-head .heading{font-size:var(--fs-6)}
  .ledger .tiles{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ledger .tile{padding:var(--s5) var(--s6);border-bottom:1px solid var(--line)}
  .ledger .tile:nth-child(2n){border-right:0}
  .ledger .tile-n{font-size:var(--fs-8)}
  .ledger .head-acts{padding:var(--s5) var(--s6)}
  .ledger .head-acts .spacer{display:none}
  .ledger .team-body{display:flex;flex-direction:column;align-items:stretch;padding-top:0;padding-left:var(--s6);padding-right:var(--s6);padding-bottom:var(--s7);gap:var(--s5)}
  .ledger .chain-body{grid-template-columns:minmax(0,1fr)}
  .ledger .promo{justify-self:stretch;max-width:none;padding:0 var(--s6) var(--s6)}
  .ledger .crew-group{display:flex}
  .ledger .crew-list,.ledger .crew-list-other{display:flex;flex-direction:column;gap:var(--s5);width:100%}
  .ledger .crew-card,.ledger .crew-list-mine .crew-card{grid-template-columns:minmax(0,1fr);grid-template-areas:"id" "state" "stats" "ask" "acts" "note";
    padding:var(--s6);gap:var(--s5);transform:none;justify-items:stretch;text-align:left;border-radius:var(--r2)}
  .ledger .crew-card::before{display:none}
  .ledger .crew-id{flex-direction:row;text-align:left}
  .ledger .crew-id .col{align-items:flex-start}
  .ledger .crew-id .row{justify-content:flex-start}
  .ledger .crew-state{align-items:flex-start;text-align:left}
  .ledger .crew-state .stamp{max-width:none}
  .ledger .crew-stats,.ledger .crew-list-mine .crew-stats{grid-template-columns:repeat(3,minmax(0,1fr))}
  .ledger .stat{align-items:flex-start}
  .ledger .crew-acts{display:grid;grid-template-columns:1fr 1fr;width:100%}
  .ledger .crew-locked .crew-acts,.ledger .crew-locked .crew-note,.ledger .crew-credited .crew-acts,.ledger .crew-escalated .crew-acts{display:none}
  .ledger .seal{grid-column:1/-1;min-height:calc(48px*var(--ui));min-width:0}
  .ledger .crew-pts{grid-column:1/-1;margin-left:0;justify-content:space-between;width:100%}
  .ledger .kv,.ledger .kv-4{grid-template-columns:calc(88px*var(--ui)) minmax(0,1fr);row-gap:var(--s2)}
  .ledger .kv .mark,.ledger .kv .btn{grid-column:2;justify-self:start}
  .ledger .kv .on-hover{opacity:1}
  .ledger .kv-v{font-size:var(--fs-4)}
  .ledger .cmp-row{grid-template-columns:calc(64px*var(--ui)) minmax(0,1fr) minmax(0,1fr) calc(22px*var(--ui));column-gap:var(--s3)}
  .ledger .cmp-row .label{letter-spacing:var(--ls-1);font-size:calc(8.5px*var(--ui));overflow-wrap:anywhere}
  .ledger .cmp-row .kv-v{overflow-wrap:normal;font-size:var(--fs-3)}
  .ledger .cmp .on-hover{display:none}
  .ledger .cmp-foot{padding-left:var(--s6);padding-right:var(--s6)}
  .ledger .gallery{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s4)}
  .ledger .worth-n{font-size:calc(40px*var(--ui))}
  .ledger .discrep{flex-direction:column;gap:var(--s3)}
  .ledger .console{width:calc(140px*var(--ui));right:calc(28px*var(--ui))}
  .ledger .candles{right:var(--s4)}
  .ledger .perch-head{right:var(--s6)}
  .ledger .perch-team{left:auto;right:var(--s6)}
  /* the roster */
  .ledger .roster{grid-template-columns:minmax(0,1fr);grid-template-areas:"head" "standing" "roll";gap:var(--s6);padding:var(--s6) var(--s5) calc(150px*var(--ui));max-width:none}
  .ledger .standing{display:flex;flex-direction:column;align-items:stretch;gap:var(--s5);padding:var(--s5) 0 0}
  .ledger .standing .crew-card{transform:none}
  .ledger .standing .crew-card:nth-child(1){order:2}.ledger .standing .crew-card:nth-child(2){order:1}.ledger .standing .crew-card:nth-child(3){order:3}
  .ledger .standing .crew-state{flex-direction:row;align-items:baseline;gap:var(--s3)}
  .ledger .roll-row{grid-template-columns:calc(28px*var(--ui)) minmax(0,1fr) auto;column-gap:var(--s4)}
  /* A PHONE KEEPS THE NAME AND THE ONE FACT THAT CHANGES HOURLY. Company,
     last online and last post are all in his card, one tap away. */
  .ledger .roll-co,.ledger .roll-seen,.ledger .roll-post{display:none}
  .ledger .only-phone{display:inline}
  .ledger .rail-detail{padding-left:var(--s6)}
  .ledger .stats-6{grid-template-columns:repeat(3,minmax(0,1fr))}
  .ledger .companies{grid-template-columns:minmax(0,1fr)}
  .ledger .views{order:5}
  .ledger .field-find{width:100%;order:6}
  .ledger .you-stand{margin-left:auto}
  /* the other screens */
  .ledger .screen{grid-template-columns:minmax(0,1fr);gap:var(--s6);padding:var(--s6) var(--s5) calc(150px*var(--ui));max-width:none}
  .ledger .screen > *{grid-column:auto}
  .ledger .tiles-5{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ledger .tiles-5 .tile:last-child{grid-column:1/-1}
  .ledger .field-date{width:calc(50% - var(--s2))}
  .ledger .rep-row{grid-template-columns:minmax(0,1fr) auto auto;row-gap:var(--s2)}
  .ledger .rep-state{grid-column:1/-1}
  .ledger .rep-id,.ledger .rep-diff,.ledger .rep-men,.ledger .rep-by{display:none}
  .ledger .steps{width:100%;order:9}
  .ledger .thumbs{grid-template-columns:repeat(3,minmax(0,1fr))}
  .ledger .picks{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ledger .tree-l{margin-left:var(--s4);padding-left:var(--s5)}
  .ledger .post{flex-wrap:wrap}
  .ledger .rail.wrap > .grow{flex:1 1 100%}
  .ledger .rail.wrap > .avatar + .grow{flex:1 1 calc(100% - 36px*var(--ui))}
  .ledger .desk-row{grid-template-columns:minmax(0,1fr) auto;row-gap:var(--s2)}
  .ledger .desk-row > :nth-child(2){grid-column:1/-1;order:3}
  .ledger .honours{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ledger .stat-row{grid-template-columns:minmax(0,1fr) calc(56px*var(--ui)) calc(72px*var(--ui))}
  /* The PvE board keeps its honours on a phone too: brother, honours, kills, best. */
  .ledger .stat-row.stat-pve{grid-template-columns:minmax(0,1fr) calc(56px*var(--ui)) calc(56px*var(--ui)) calc(72px*var(--ui))}
  .ledger .st-more{display:none}
  .ledger .scope-row{grid-template-columns:minmax(0,1fr) auto;row-gap:var(--s2)}
  .ledger .scope-row > :nth-child(3){grid-column:1/-1}
  .ledger .named-row{grid-template-columns:minmax(0,1fr) auto auto;row-gap:var(--s2)}
  .ledger .named-row > :nth-child(2),.ledger .named-row > :nth-child(4){grid-column:1/-1}
  .ledger .log-row{grid-template-columns:calc(60px*var(--ui)) minmax(0,1fr);row-gap:var(--s2)}
  .ledger .log-act,.ledger .log-target,.ledger .log-why{grid-column:2}
  .ledger .signin{min-height:calc(640px*var(--ui));padding-top:var(--s8)}
  .ledger .filing-bar{flex-wrap:nowrap}
  .ledger .filing-bar .meta{display:none}
  .ledger .filing-foot{padding:var(--s5) var(--s5)}
  .ledger .filing-foot .meta{display:none}
}

/* ============================================================================
   3. ledger-carry.css -- the old screens carried into the redesign
   ========================================================================== */
/* ==========================================================================
   CALIBAN LEDGER — ledger-carry.css — WHAT THE NEW SHEET DOES NOT SAY.

   Load order is ledger.css, then this. ledger.css is the redesign; this file
   is the part of the old inline <style> in index.html that the redesign has
   no answer for, lifted out so that block can be deleted a screen at a time
   instead of all at once.

   HOW THE LIST WAS MADE, BECAUSE IT IS NOT A LIST ANYBODY WROTE DOWN.
   Three sets, and a subtraction:
     A  class names with a rule in index.html's inline <style>        570
     C  class names with a DECLARING rule in ledger.css               304
     A − C  names the new sheet has no answer for                     455
            of those, named nowhere in index.html or the ui/app scripts       80
     CARRIED                                                         375

   TWO THINGS THAT LOOK LIKE COVERAGE AND ARE NOT, both found by counting
   rather than by reading the sheet:

   AN EMPTY RULE IS NOT A RULE. ledger.css line 157 is
   `.crew-place:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){}` — a declared name with nothing
   in it. Counting selectors would have called .crew-place covered and thrown
   away the mono, the tracking and the gold of a squad mark. C above counts
   only rules that declare something, which is why it is 304 and not 305.

   THE MARKUP IS NOT THE ONLY PLACE A CLASS IS WRITTEN. index.html's own
   <script> blocks build class lists the templates then interpolate —
   `tickCls: on ? "pick-box pick-on" : "pick-box"`,
   `faceCls(base, has) => base + " lazy-avatar"` — so a survey that reads
   class= attributes alone misses .pick-box, .lazy-avatar, .avatar.avatar-face
   and 70-odd more, and reports 292 where the true figure is 375. Set B here
   is every word in index.html AND every word in the js modules, which errs
   deliberately toward carrying: a carried rule for a name nobody uses costs
   bytes, and a missing one costs a screen.

   WHAT THAT CAME TO, counted by parsing this file in the same engine that
   renders it: 580 style rules over 610 selector parts and 44 @media blocks —
   517 scoped component rules (§ 3), 59 servo-skull rules (§ 1), 29 @keyframes
   (§ 4), and four small blocks written here rather than lifted (§ 1's :root
   tokens and its box-sizing, § 2's fourteen tokens, § 4's reduced-motion
   guard). 262 selector parts were left behind: each names only classes
   ledger.css already declares, or names no class at all (`*`, `html`,
   `body`, `a`, `input`, the scrollbar pseudo-elements), which is the new
   sheet's base layer and not ours to re-assert.

   THE COMMENTS COME ACROSS WITH THE RULES. Where the author wrote down why a
   declaration is what it is — an owner's words, a measurement, a mistake made
   twice — that comment is carried with its rule. A rule whose reason is left
   behind is a rule the next person deletes.

   The scoping is tools/scope-ledger.mjs's, run over this file's component
   half: `:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))` on each selector's leftmost compound,
   which adds no specificity and counts the scope root as inside itself. Of
   the 610 selector parts here, the only ones that can match outside .ledger
   are § 1's — the skull's 63, his box-sizing and the :root he reads from.
   § 2, § 3 and § 4 escape nothing.
   ========================================================================== */

/* ==========================================================================
   § 1 · THE SERVO-SKULL — UNSCOPED, AND IT HAS TO BE.

   Everything else in this file is confined to .ledger. These are not, because
   the skull is not in the app's tree. skull.js appends .skull-host,
   .skull-glass and .skull-toggle to <body> (skull.js builds the host at
   :1168 and the glass at :1378; the toggle is the only way back and is
   present even when he is not loaded at all), and the rest — the cone, the
   field, the pool, the fire, the puffs — hang off that host. The scope root
   is a div inside body, not body itself: commit 637c36a measured it as
   `<div class="ledger page filing">`. So `:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))` on
   .skull-host matches nothing, and a scoped skull is not a mis-placed skull,
   it is an absent one.

   THE SAME ARGUMENT APPLIES TO HIS TOKENS, and this is the half that is easy
   to miss. Every one of these rules reads var(--ink), var(--serif),
   var(--ui), var(--rule-head) and the like. Today those come from the inline
   sheet's `:root`. ledger.css declares its tokens on `:where(.ledger)`, so
   the moment the inline block goes, a body-level element inherits nothing and
   the bubble loses its type, its colour and its scale in one step. The block
   below re-declares, on :root, exactly the 25 tokens the unscoped sheets read
   — the skull's 18, plus the seven more that skull-chat.css wants, since it
   is linked separately and lives at body level for the same reason. They are
   the old sheet's values, unchanged. They cannot fight ledger.css: .ledger
   re-declares all 25 for its own subtree, and a declaration on the element
   beats one inherited from the document.
   ========================================================================== */

:root{
  --bad-lit:#C4685F;
  --fs-1:calc(9px * var(--ui));
  --fs-2:calc(10px * var(--ui));
  --fs-4:calc(12px * var(--ui));
  --fs-5:calc(13px * var(--ui));
  --fs-7:calc(16px * var(--ui));
  --gold:#C9A45C;
  --gold-line:rgba(201,164,92,.35);
  --gold-warm:#E2C079;
  --good:rgb(var(--hue-2,127,180,137));
  --good-2:rgb(var(--hue-15,110,158,122));
  --gut:clamp(12px,2.1vw,34px);
  --ink:rgb(var(--hue-67,228,235,226));
  --ink-2:rgb(var(--hue-21,169,188,175));
  --ink-3:rgb(var(--hue-19,142,158,147));
  --ink-4:rgb(var(--hue-14,109,125,112));
  --ink-5:rgb(var(--hue-12,95,112,102));
  --line-2:rgb(var(--hue-11,43,58,38));
  --ls-2:.14em;
  --mono:'JetBrains Mono',monospace;
  --r2:3px;
  --rule:rgba(var(--hue-1,178,201,177),.10);
  --rule-head:rgba(201,164,92,.22);
  --serif:'Cormorant Garamond',serif;
  --ui:1;
}

/* AND HE LOSES border-box WITH THE REST OF THE BASE LAYER. The inline sheet
   opens `*{box-sizing:border-box}`; ledger.css's equivalent is written
   `:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))`, which again does not reach body level. It is
   not cosmetic here: .skull-say sets max-width AND padding AND a border, so
   under content-box the reading bubble grows 25px wider than the 310.5px it
   was measured at — 23px of padding and 2px of border, outside the max-width
   instead of inside it. Asserted on his own subtree only — the host page's box
   model is not this file's to set. */
[class^="skull-"],[class*=" skull-"],
[class^="skull-"] *,[class*=" skull-"] *{box-sizing:border-box}

/*
 * WHAT HE IS READING, SAID OUT LOUD. Owner: "lets have that little guy when he
 * typing too put in a small chat bubble the name of the brothers, the operation
 * or mission and outcome."
 *
 * Positioned entirely from skull.js -- it rides his own coordinates -- so
 * everything here is paint. Fixed, because he is; pointer-events none, because
 * he is an ornament and a sergeant must never have to click past him.
 */
.skull-say{position:fixed;left:0;top:0;z-index:21;display:none;
  pointer-events:none;max-width:310.5px;
  padding:calc(8.05px * var(--ui)) calc(11.5px * var(--ui));
  border:1px solid var(--rule-head);border-radius:var(--r2);
  background:linear-gradient(180deg,rgba(var(--hue-57,19,34,27),.96),rgba(var(--hue-77,11,20,16),.96));
  box-shadow:0 10px 26px -12px rgba(0,0,0,.9),
             inset 0 1px 0 rgba(var(--hue-23,228,240,226),.06);
  /* The tail. Drawn on the RIGHT because the bubble sits to his left. */
  --tail:10px}

.skull-say::after{content:"";position:absolute;right:-6px;bottom:12px;
  width:10px;height:10px;transform:rotate(45deg);
  background:rgba(var(--hue-78,13,23,18),.96);
  border-right:1px solid var(--rule-head);border-top:1px solid var(--rule-head)}

.skull-say[data-side="right"]::after{left:-6px;right:auto;border-right:0;border-top:0;
  border-left:1px solid var(--rule-head);border-bottom:1px solid var(--rule-head)}

/* --ink-1 WAS NEVER DECLARED. The ladder is --ink then --ink-2..--ink-6, so
   this resolved to nothing and the title fell back to whatever it inherited --
   it has never once been the colour written here. --ink is the top of that
   ladder and what a title beside an --ink-4 line wants. */
.skull-say-k{font-family:var(--serif);font-size:calc(var(--fs-5) * 1.15);color:var(--ink);
  line-height:1.25;letter-spacing:.01em}

.skull-say-v{font-family:var(--mono);font-size:calc(var(--fs-2) * 1.15);color:var(--ink-4);
  line-height:1.5;letter-spacing:.02em;margin-top:2px}

/* The verdict of the PREVIOUS report, set quieter and ruled off, so it cannot
   be misread as a judgement on the one he is working now. */
/* --rule-2 WAS NEVER DECLARED EITHER, and an undefined colour in a border
   shorthand does not vanish -- it falls back to currentColor. So the rule that
   exists to set the previous verdict QUIETER apart has been drawing itself in
   the text's own colour, the loudest line in the bubble. --rule is the quiet
   one this asked for. */
.skull-say-v:last-child:not(:nth-child(2)){color:var(--ink-5);margin-top:5px;
  padding-top:5px;border-top:1px solid var(--rule)}

/* HIS OWN LINES ARE NOT A DATA READING. The reading below him is dense --
   a title and up to four fields -- and 13px is what fits it. A quip is one
   short sentence with a temper, the owner asked for it bigger ("the quotes are
   a little too small"), and one sentence can afford the width and the size that
   four fields cannot. */
.skull-say-q{font-family:var(--serif);font-size:calc(var(--fs-7) * 1.15);color:var(--ink);
  line-height:1.3;letter-spacing:.01em}

.skull-say-big{max-width:391px}

@media (max-width: 999px){
.skull-say{display:none!important}
}

@media (prefers-reduced-motion: reduce){
.skull-say{transition:none}
}

/*
 * THE OPEN REPORT'S HEADER, ON A PHONE.
 *
 * Owner: "we see awaiting judgment back and worth its too tall and takes up too
 * much screen." It is sticky, so whatever it costs it costs on every scroll --
 * and it was costing three wrapped rows before a single screenshot.
 *
 * The state word drops to the size of a heading rather than a title, the
 * sentence under it goes on the same line, and the padding halves. Nothing is
 * removed: the word, the reason, the worth and the buttons are all still there
 * -- they stop being a title page.
 */
/* THE RULES THAT ANSWER THIS ARE WITH THE REST OF THE JUDGING PAGE'S, far
 * below, and they have to be: a media query adds no specificity, so a phone
 * override for .aar-head written HERE would be silently overruled by
 * .aar-head's own base rule declared later in the sheet. The two selectors
 * that used to sit here named a `.serif` and a `.row.g6` that this header has
 * not held since it was rebuilt -- both matched nothing at any width.
 */
/*
 * ══ THE SERVO-SKULL'S CANVAS ══════════════════════════════════════════════
 *
 * It sits UNDER the phosphor and the bezel (60 and 61), so the screen's own
 * grain falls over it and it reads as being inside the machine rather than
 * pasted on top of it. It takes no pointer events at all -- the rule the whole
 * of this file keeps is that ornament never costs a reader a click.
 */
.skull-host{position:fixed;inset:0;z-index:55;pointer-events:none}

.skull-host canvas{position:absolute;inset:0;width:100%;height:100%}
.skull-dodge{position:absolute;width:160px;height:160px;max-width:none;
  transform:translate(-50%,-50%);pointer-events:none}
.skull-dodge[hidden]{display:none}

/*
 * THE AUSPEX BEAM: A FIELD, AND A CONE THAT THROWS IT.
 *
 * Owner, 2026-09-02: "the beem needs to be wide then scan across something like
 * reading a barcode from top to bottom."
 *
 * So it is not a pointer. The CONE fans from the lens down onto the thing, and
 * the FIELD lies over the thing's own bounds with a bright bar travelling down
 * it -- a document being read, not a laser being aimed. Neither can be baked
 * into the model: both are sized and placed from the target's rectangle, which
 * nothing knows until the moment it is chosen.
 */
.skull-reading,.skull-reading *{
  text-shadow:0 0 2px rgba(var(--hue-20,158,255,205),.8),0 0 6px rgba(var(--hue-17,112,232,168),.5),
    0 0 12px rgba(var(--hue-13,100,220,158),.15)}

.skull-field{position:absolute;left:0;top:0;pointer-events:none;opacity:0;
  transition:opacity .18s ease;overflow:visible}

/* NO BACKGROUND HERE AT ALL. It used to carry the area glow, but this element
   is overflow:hidden and exactly the size of the text -- so a gradient reaching
   260% of its height was cut off flat at the box, which is the square the owner
   saw. All it does now is CLIP the travelling bar. The glow belongs on .skull-
   pool below, which has no clip and room to fade out properly. */
/*
 * IT LIGHTS THE WORDS, NOT A BOX.
 *
 * Owner: "his scanner lights up a square can it light up JUST the text and not
 * the square?" So the field itself is now invisible -- no border, no tint. All
 * it does is CLIP the travelling bar to the glyph bounds.
 *
 * The bar carries a backdrop-filter, which brightens whatever is BEHIND it
 * rather than painting over it. On this page the ground is nearly black and the
 * words are pale, so raising the brightness of everything under the bar shows up
 * almost entirely on the letters -- the text lights as the bar passes and the
 * background barely moves. mix-blend-mode cannot do this: the canvas is a fixed,
 * z-indexed layer and so an isolated stacking context, and a blend would only
 * ever reach the other ornaments inside it.
 */
.skull-field::after{content:"";position:absolute;left:0;right:0;height:13px;
  top:var(--scan,0%);margin-top:-6px;
  /*
   * SOFT ON ALL FOUR SIDES, AND IT HAS TO BE THE MASK THAT DOES IT.
   *
   * Owner: "but it looks like a square." It did, and the background gradient
   * was never going to fix it: BACKDROP-FILTER BRIGHTENS THE WHOLE OF AN
   * ELEMENT'S BOX, evenly, and a background only tints on top of that. So the
   * colour faded at the ends while the BRIGHTENING stopped dead -- a hard
   * rectangle of lit page with a soft-coloured bar sitting inside it.
   *
   * A mask does modulate the backdrop-filter, so the fade has to live there.
   * Two of them, intersected: one across the bar and one along it, which takes
   * the brightening itself to nothing on every edge.
   */
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 38%,#000 62%,transparent),
                     linear-gradient(90deg,transparent,#000 18%,#000 82%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 38%,#000 62%,transparent),
             linear-gradient(90deg,transparent,#000 18%,#000 82%,transparent);
  -webkit-mask-composite:source-in;
  mask-composite:intersect;
  backdrop-filter:brightness(2.9) saturate(1.5) hue-rotate(-18deg);
  -webkit-backdrop-filter:brightness(2.9) saturate(1.5) hue-rotate(-18deg);
  background:linear-gradient(90deg,
    transparent, rgba(var(--hue-7,170,255,205),.22) 14%, rgba(var(--hue-7,170,255,205),.22) 86%, transparent);
  box-shadow:0 0 16px rgba(var(--hue-42,130,255,190),.45)}

/* ACROSS, NOT DOWN. A row of a table is read top to bottom; a line of text is
   read left to right, so the bar turns ninety degrees and travels the other
   way. Same field, same cone, different grain. */
/*
 * BURNING IT IN, rather than reading it.
 *
 * Owner: "i would LOVE for the part of the quote that gets written following
 * the scanner it glows a tiny bit like it was burned into the background maybe
 * even with a tiny bit of smoke TINY bit. but like he burns it in leaving the
 * text?"
 *
 * So the two halves of a quote change no longer look alike. ERASING keeps the
 * cold green auspex light. WRITING runs the same bar hot -- the backdrop-filter
 * pushes brightness far harder and swings the hue toward ember, so the letters
 * flare orange-white exactly as they appear.
 *
 * ::before is the COOLING TRAIL: everything already written, tinted warm and
 * falling off with distance from the edge, so what he laid down a moment ago is
 * still glowing faintly and what he wrote first has gone cold.
 */
/*
 * THE AUSPEX PANEL IS WRITTEN, NOT FOUND.
 *
 * Its body is clipped away from the moment a report opens and drawn back in
 * left to right under the beam, so the finding appears as the skull reads it.
 * The class is only ever added by skull.js, and only when he is actually coming
 * -- so with no skull loaded (a phone, reduced motion, no WebGL, or simply no
 * ?skull=1) this rule never applies and the panel is just there, as it always
 * was. An ornament may not be the reason a reader cannot see a finding.
 */
/* IT WRITES IN THE FINDINGS, NOT THE RECORD. This used to name `> .col`, which
 * was the auspex panel's only body. THE RECORD carries three bodies now -- the
 * table, the findings and the override footer -- and `> .col` would clip all
 * three, so the six fields a sergeant opened the report to read would be blank
 * for the length of a flight. The findings are the part the skull actually
 * produces, so the findings are the part it draws in. .auspex-say is a direct
 * child of the plate: the sc-if around it compiles to a Fragment and emits no
 * box, so `>` passes straight through it. */
.aar-auspex.skull-writing > .auspex-say{clip-path:inset(0 var(--aw,0%) 0 0)}

/*
 * THE SWITCH, AND WHAT HAPPENS WHEN YOU THROW IT.
 *
 * A small brass plate above the bottom-right footer with a lamp in it: lit while he is
 * about, dark when he is not. It is always present even when he is not loaded
 * at all, because it is the only way back.
 */
.skull-toggle{position:fixed;right:14px;z-index:62;
  bottom:calc(72px + env(safe-area-inset-bottom,0px));
  display:flex;align-items:center;gap:7px;cursor:pointer;flex:none;white-space:nowrap;
  padding:5px 9px 5px 7px;border:1px solid var(--gold-line);
  background:linear-gradient(180deg,rgba(var(--hue-79,28,36,31),.94),rgba(14,20,17,.94));
  clip-path:polygon(7px 0,100% 0,100% 100%,0 100%,0 7px);
  font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-2);
  text-transform:uppercase;color:var(--ink-4);
  box-shadow:inset 0 1px 0 rgba(214,196,150,.14), 0 2px 10px rgba(0,0,0,.5);
  transition:color .18s ease, border-color .18s ease, box-shadow .18s ease}

.skull-toggle:hover{color:var(--gold-warm);border-color:var(--gold);
  box-shadow:inset 0 1px 0 rgba(214,196,150,.2), 0 0 14px rgba(180,143,50,.28)}

/* The lamp. Lit is a live thing; dark is a glow that has gone out. */
.skull-toggle i{width:8px;height:8px;border-radius:50%;flex:none;
  background:rgb(var(--hue-82,42,52,46));box-shadow:inset 0 0 3px rgba(0,0,0,.7);
  transition:background .25s ease, box-shadow .25s ease}

.skull-toggle.on i{background:rgb(var(--hue-3,123,232,168));
  box-shadow:0 0 7px rgba(var(--hue-3,123,232,168),.9), 0 0 16px rgba(var(--hue-3,123,232,168),.45)}

.skull-toggle.on{color:var(--gold-warm)}

@media (max-width: 999px){
.skull-toggle{display:none}
}

/*
 * HITTING THE GLASS. A ring thrown out from the point of impact and a brief
 * bloom over the whole page -- the only moment anything on this site flashes,
 * which is what makes it land. Both are gone inside half a second.
 */
/* ==========================================================================
   THE TEAR -- where he comes through the gallery.

   Owner: "when looking at a screenshot and the screenshot gallery is on top of
   the plane where he is can we have him rip through the plane on one of the
   edges so h[e] doesnt hit a screenshot make a tear in the gallery that there
   until we close the gallery then have him continue his scans on that plane?"

   The lightbox is z-index 70 and he lives at 55, so a screenshot genuinely does
   bury him. Rather than raise him quietly through it -- which would look like a
   z-index change, because it is one -- he goes to an EDGE, opens the overlay
   like a sheet, and climbs out onto the front of it. The rip stays open behind
   him the whole time the gallery does, so there is always a reason he is up
   there.

   It is a real gap and not a drawn one: the fill is darker than the overlay
   above it and carries the page's own ground, so what shows through the slit
   reads as the room behind the sheet. The jagged edges are generated per tear
   in skull.js, so no two rips are the same shape.
   ========================================================================== */
/* ==========================================================================
   THE HOLE HE CAME THROUGH.

   Owner: "make it look like glass instead of a tear w[i]th a silouette of him
   left behind and some cracks eminating from the silouette."

   Two attempts at a TEAR failed the same way, and the second time I wrote down
   why without acting on it: a hole in a dark sheet over a dark room has nothing
   to show. The gallery backdrop is rgba(4,8,7,.94) and the page beneath it is
   nearly black, so there is no contrast to be had from a gap however it is
   shaded.

   Glass has no such problem. Cracks are LINES, and a line is visible against
   anything -- so the break carries its own legibility instead of borrowing it
   from whatever is behind. The silhouette is the shape he left in it, and the
   fractures run out from that shape rather than from a point, which is what
   says something went THROUGH rather than hit it.
   ========================================================================== */
.skull-glass{position:fixed;z-index:71;pointer-events:none;
  opacity:0;transition:opacity .3s ease;
  filter:drop-shadow(0 0 6px rgba(200,240,255,.55))
         drop-shadow(0 0 20px rgba(var(--hue-41,120,220,180),.3))}

.skull-glass.open{opacity:1;transition:none}

@media (prefers-reduced-motion:reduce){
.skull-glass{transition:none}
}

.skull-crack{position:fixed;z-index:59;pointer-events:none;opacity:0;
  border:2px solid rgba(var(--hue-48,214,236,224),.85);border-radius:50%;
  box-shadow:0 0 22px rgba(var(--hue-22,190,255,220),.5), inset 0 0 18px rgba(var(--hue-22,190,255,220),.3)}

.skull-crack.hit{animation:crackout .5s cubic-bezier(.15,.7,.3,1) forwards}

.skull-bloom{position:fixed;inset:0;z-index:58;pointer-events:none;opacity:0;
  background:radial-gradient(circle at var(--hx,50%) var(--hy,50%),
    rgba(var(--hue-50,226,255,238),.30) 0%, rgba(var(--hue-44,150,230,190),.10) 26%, transparent 58%)}

.skull-bloom.hit{animation:bloomout .42s ease-out forwards}

@media (prefers-reduced-motion: reduce){
.skull-crack,.skull-bloom{display:none}
}

/*
 * SPARKS. Struck off him when the cursor knocks into him -- bright, small, and
 * gone in under a second. Thrown from the render loop rather than by a keyframe,
 * because they have to leave from wherever he was hit.
 */
.skull-spark{position:absolute;left:0;top:0;width:3px;height:3px;pointer-events:none;
  opacity:0;border-radius:50%;
  background:radial-gradient(circle, #FFF6DC 0%, #FFC96A 45%, rgba(255,140,40,.5) 75%, transparent 100%);
  box-shadow:0 0 6px rgba(255,190,90,.9)}

/* A SHARD, NOT AN EMBER. The ember above is right for a knock and wrong for a
   window: glass carries no heat, so it is pale rather than amber, and it is a
   splinter rather than a dot -- longer than it is wide, lit hard along its
   length, and turning as it flies (skull.js gives each one its own spin). */
.skull-spark.glassy{width:9px;height:2.5px;border-radius:1px;
  background:linear-gradient(90deg,
    rgba(var(--hue-58,214,255,248),0) 0%, #FFFFFF 32%, rgb(var(--hue-83,230,255,250)) 62%, rgba(150,235,255,0) 100%);
  box-shadow:0 0 9px rgba(214,250,255,.95), 0 0 20px rgba(140,230,255,.5)}

/* AN ARC OFF A CUT CABLE. Owner: "the sparks are weak i wanna see big blue...
   then see the explosion".

   The ember above is a hot fleck struck off metal -- amber, 3px, right for a
   knock and wrong for this. Mains going to earth is not hot, it is BRIGHT: a
   white core that reads as overexposed, a hard cyan corona around it, and a
   blue glow thrown far enough to light the casing it is coming from. Four
   times the ember's size and three shadows deep, because the whole point is
   that you see it from behind a machine that is hiding the thing making it. */
.skull-spark.arc{width:12px;height:12px;
  background:radial-gradient(circle, #FFFFFF 0%, #DFF6FF 22%, #6FD4FF 46%,
    rgba(60,150,255,.55) 72%, transparent 100%);
  box-shadow:0 0 14px rgba(150,225,255,1), 0 0 34px rgba(80,180,255,.8),
    0 0 68px rgba(40,120,255,.45)}

@media (prefers-reduced-motion: reduce){
.skull-spark{display:none}
}

/*
 * THE CRACK. Real radiating fractures, drawn fresh each time so no two impacts
 * look alike, held for a moment and then fading out over a few seconds -- glass
 * that stayed cracked would become part of the furniture, and this is meant to
 * be something that just happened.
 */
.skull-shatter{position:fixed;z-index:59;pointer-events:none;
  opacity:0;filter:drop-shadow(0 0 4px rgba(200,240,255,.5))}

.skull-shatter.hit{animation:shatterfade 4.6s ease-out forwards}

@media (prefers-reduced-motion: reduce){
.skull-shatter{display:none}
}

/*
 * THE DAY HE FLIES TOO NEAR THE CANDLES.
 *
 * Owner: "you think randomly he can get too clos to the cangles and one of his
 * feet could catch fire then have him rush off the page with a smoke trail
 * behind him?"
 *
 * Rarely -- about one errand in fourteen -- he goes down to look at the candles
 * burning along the bottom of the page, gets too close, and one of them takes.
 * Then he bolts for the nearest edge trailing smoke, and comes back a few
 * seconds later from somewhere else, having learnt nothing.
 *
 * It is the only thing on this page that happens TO him rather than being
 * something he does, which is why it is worth having: a thing that can be
 * caught out is a thing with a life of its own.
 */
/* The screen anchor has no animation: the fire field is painted behind the
   skull by its render loop, and cannot replace the transform that follows him. */
.skull-fire{position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;
  opacity:0;visibility:hidden}
.skull-fire.lit{opacity:1;visibility:visible}
.skull-host .skull-fire > canvas{position:absolute;inset:auto;display:block;
  max-width:none;pointer-events:none}

/* The trail. Each puff is placed and faded from the render loop rather than by
   a keyframe, because they have to be left BEHIND him along a path nobody knows
   in advance. */
.skull-puff{position:absolute;left:0;top:0;width:15px;height:15px;pointer-events:none;
  opacity:0;border-radius:50%;filter:blur(4px);
  /* Owner: "leave brighter white smoke behind him and double the amount". Near
     white rather than the grey it was, because smoke off a burning thing on a
     near-black page has to be pale to read at all. */
  background:radial-gradient(circle, rgba(246,246,242,.85) 0%, rgba(226,224,216,.35) 45%, transparent 72%)}

@media (prefers-reduced-motion: reduce){
.skull-fire,.skull-puff{display:none}
}

/* THE SMOKE, and it has to stay tiny -- the owner said so twice. Three puffs on
   deliberately unrelated timings, drifting up off the burn line and gone in
   under two seconds. Any more and it stops being a wisp and starts being a
   fire. */
.skull-smoke{position:absolute;width:9px;height:9px;pointer-events:none;
  opacity:0;border-radius:50%;filter:blur(3px);
  background:radial-gradient(circle, rgba(214,210,200,.34) 0%, transparent 72%)}

.skull-smoke.on{opacity:1}

.skull-smoke::before,.skull-smoke::after{content:"";position:absolute;inset:0;
  border-radius:50%;background:inherit;
  animation:smokeup 1.7s ease-out infinite}

.skull-smoke::after{animation-duration:2.3s;animation-delay:-.9s}

@media (prefers-reduced-motion: reduce){
.skull-smoke{display:none}
}

/*
 * THE BEAM, REBUILT AS LIGHT RATHER THAN AS A SHAPE.
 *
 * Owner: "the 'scanning area' with the lines is not great can you rethink that
 * and make it look more realistic??"
 *
 * The old one was a filled trapezoid with two drawn lines down its sides, and
 * that is the problem in one sentence: hard geometric edges read as vector art,
 * because nothing in the physical world has them. Light from a lens is brightest
 * at the lens, spreads at an angle, softens at its edges where the aperture
 * blurs it, and dims with distance.
 *
 * So it is a CONIC GRADIENT struck from the lens itself -- a true angular wedge,
 * with the two edges falling off over several degrees instead of stopping dead.
 * A radial mask over the top takes the brightness down with distance travelled.
 * No clip-path, no drawn lines, nothing with a border.
 *
 * The angles are computed per frame from the lens to the first and last letters,
 * so the wedge still lands exactly on the words -- it just no longer announces
 * where it stops.
 */
.skull-cone{position:absolute;left:0;top:0;pointer-events:none;opacity:0;
  transition:opacity .2s ease;
  background:conic-gradient(from var(--from,170deg) at var(--ax,50%) 0%,
    transparent 0deg,
    rgba(var(--hue-6,150,255,196),.05) var(--soft,4deg),
    rgba(var(--hue-6,150,255,196),.20) calc(var(--soft,4deg) * 2),
    rgba(var(--hue-6,150,255,196),.20) calc(var(--span,20deg) - var(--soft,4deg) * 2),
    rgba(var(--hue-6,150,255,196),.05) calc(var(--span,20deg) - var(--soft,4deg)),
    transparent var(--span,20deg));
  /* IT FADES OUT BEFORE IT LANDS, rather than stopping on a line.
  
     Owner: "theres this sharp line at the top of the scan too where his beam
     ends on the verticlt bar if that could just be a fade that would be
     perfect."
     
     The element is exactly as tall as the gap between the lens and the words,
     so the gradient simply ran out at the bottom edge and left a hard
     horizontal cut across the beam. The radial mask above was supposed to take
     care of distance, but its circle is sized to the FARTHEST CORNER of the
     box -- about 400px on a wide, short wedge -- so at the bottom edge, 130px
     down, it is still near full strength.
     
     A second mask runs along the beam instead of out from its apex, and takes
     it to nothing over the last fifth. The light thinning as it arrives is also
     what a beam does; the pool underneath is what actually lights the words. */
  -webkit-mask-image:radial-gradient(circle at var(--ax,50%) 0%,
    rgba(0,0,0,.95) 0%, rgba(0,0,0,.55) 55%, rgba(0,0,0,0) 100%),
    linear-gradient(180deg,#000 0,#000 52%,rgba(0,0,0,.55) 80%,rgba(0,0,0,0) 100%);
  mask-image:radial-gradient(circle at var(--ax,50%) 0%,
    rgba(0,0,0,.95) 0%, rgba(0,0,0,.55) 55%, rgba(0,0,0,0) 100%),
    linear-gradient(180deg,#000 0,#000 52%,rgba(0,0,0,.55) 80%,rgba(0,0,0,0) 100%);
  -webkit-mask-composite:source-in;
  mask-composite:intersect}

/* Quote light reaches the lettering before fading. Combining both distance
   masks erased the end of this beam above the dark footer. */
.skull-field.across ~ .skull-cone{
  -webkit-mask-image:linear-gradient(180deg,rgba(0,0,0,.95) 0%,rgba(0,0,0,.7) 82%,transparent 100%);
  mask-image:linear-gradient(180deg,rgba(0,0,0,.95) 0%,rgba(0,0,0,.7) 82%,transparent 100%)}

.skull-cone.burn{
  background:conic-gradient(from var(--from,170deg) at var(--ax,50%) 0%,
    transparent 0deg,
    rgba(255,190,120,.06) var(--soft,4deg),
    rgba(255,190,120,.24) calc(var(--soft,4deg) * 2),
    rgba(255,190,120,.24) calc(var(--span,20deg) - var(--soft,4deg) * 2),
    rgba(255,190,120,.06) calc(var(--span,20deg) - var(--soft,4deg)),
    transparent var(--span,20deg))}

/*
 * WHERE IT LANDS: A POOL, AND A ROUND ONE.
 *
 * Owner: "make the square in the background on the slement a little more round
 * and gradient though."
 *
 * The box is now much larger than the words it sits under, and the gradient is
 * finished well inside it -- transparent by 74% of a 42% radius -- so the light
 * has faded to nothing long before it reaches any edge. That is the whole trick
 * to a soft glow: it is not the gradient that makes a square, it is the gradient
 * running out of room. Four stops rather than two, so the falloff is a curve
 * instead of a ramp.
 */
.skull-pool{position:absolute;left:0;top:0;pointer-events:none;opacity:0;
  transition:opacity .2s ease;
  background:radial-gradient(ellipse 42% 44% at 50% 50%,
    rgba(var(--hue-20,158,255,205),.26) 0%,
    rgba(var(--hue-43,136,246,190),.15) 30%,
    rgba(var(--hue-17,112,232,168),.06) 52%,
    rgba(var(--hue-13,100,220,158),.02) 66%,
    transparent 74%)}

.skull-pool.burn{background:radial-gradient(ellipse 42% 44% at 50% 50%,
    rgba(255,206,150,.30) 0%,
    rgba(255,182,112,.17) 30%,
    rgba(255,158,74,.07) 52%,
    rgba(240,140,60,.02) 66%,
    transparent 74%)}

/* ACROSS, NOT DOWN. A row of a table is read top to bottom; a line of text is
   read left to right, so the bar turns ninety degrees and travels the other
   way. Same field, same cone, different grain. */
/* THE BAR TURNED ON ITS SIDE NEEDS ITS MASK TURNED WITH IT.
   
   .skull-field::after above is a HORIZONTAL bar, and its mask fades the ends
   with a 90deg gradient and the long edges with a 180deg one. Standing that bar
   up for a quote without redefining the mask leaves a vertical 5px sliver
   wearing a horizontal bar's fade: the 90deg pass takes it to nothing at 18%
   and 82% of FIVE PIXELS, and the 180deg pass -- meant to soften the long edges
   -- instead cuts the travelling line off flat at top and bottom. The bar is
   almost erased across its width and hard-edged along its length, which is
   backwards on both axes.
   
   And the mask modulates the backdrop-filter, so this is not a tint going
   wrong: it is the BRIGHTENING that stops dead, which is what shows up as an
   artifact against the words rather than as a softer bar.
   
   One gradient, running the length of the bar, so its two ends fade out. The
   width is 5px and needs no fade across it. */
.skull-field.across::after{left:var(--scan,0%);top:0;bottom:0;right:auto;
  width:5px;height:auto;margin-top:0;margin-left:-2px;
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 12%,#000 88%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 12%,#000 88%,transparent);
  background:linear-gradient(180deg,
    transparent, rgba(var(--hue-7,170,255,205),.22) 14%, rgba(var(--hue-7,170,255,205),.22) 86%, transparent)}

/*
 * BURNING IT IN, rather than reading it.
 *
 * Owner: "i would LOVE for the part of the quote that gets written following
 * the scanner it glows a tiny bit like it was burned into the background maybe
 * even with a tiny bit of smoke TINY bit. but like he burns it in leaving the
 * text?"
 *
 * So the two halves of a quote change no longer look alike. ERASING keeps the
 * cold green auspex light. WRITING runs the same bar hot -- the backdrop-filter
 * pushes brightness far harder and swings the hue toward ember, so the letters
 * flare orange-white exactly as they appear.
 *
 * ::before is the COOLING TRAIL: everything already written, tinted warm and
 * falling off with distance from the edge, so what he laid down a moment ago is
 * still glowing faintly and what he wrote first has gone cold.
 */
.skull-field.burn::after{
  backdrop-filter:brightness(3.6) saturate(2.4) sepia(.5) hue-rotate(-30deg);
  -webkit-backdrop-filter:brightness(3.6) saturate(2.4) sepia(.5) hue-rotate(-30deg);
  background:linear-gradient(180deg,
    transparent, rgba(255,200,130,.34) 14%, rgba(255,200,130,.34) 86%, transparent);
  box-shadow:0 0 18px rgba(255,170,80,.55), 0 0 42px rgba(255,120,40,.22)}

.skull-field.burn::before{content:"";position:absolute;top:0;bottom:0;left:0;
  width:var(--scan,0%);pointer-events:none;
  background:linear-gradient(90deg,
    rgba(255,150,60,0) 0%, rgba(255,150,60,.05) 70%, rgba(255,186,110,.16) 100%)}

/*
 * THE SERVO-SKULL, and what it is allowed to say.
 *
 * Owner: "how fucking awesome would it be to have a servo skull floating
 * around the page? small but visible. when you load up a report the servo
 * skull gives it a quick scan and says auspex clean or whatever the 40k lingo
 * would be?"
 *
 * A servo-skull is a dead servant's skull on anti-grav impellers, running one
 * task without judgement -- scouting ahead and reporting what it sees. That is
 * exactly what the reader does, so the skull is the reader's face and the
 * auspex is what it hands you.
 *
 * IT REPORTS, IT DOES NOT DECIDE. The sweep is theatre over an answer that was
 * worked out on a GPU at the owner's house hours earlier; nothing about what
 * it says is invented while it flies. If the ledger holds no reading it says
 * so rather than playing an animation and landing on "clear".
 *
 * AND IT HOLDS STILL FOR ANYONE WHO ASKS. A thing that bobs forever in the
 * corner of a screen somebody is trying to read is a cost, not a flourish --
 * particularly for the readers this site is actually for. prefers-reduced-
 * motion stops every part of it and leaves the verdict, which is the half that
 * carries the meaning.
 */
/* The drawn skull's own styles went with the drawing. */
/* The beam it drops while it reads, across the panel it is reading. */
.skull-beam{position:absolute;left:0;right:0;top:0;height:2px;
  background:linear-gradient(90deg,transparent,var(--good),transparent);
  box-shadow:0 0 12px var(--good);
  animation:skullbeam 1.15s ease-in-out;pointer-events:none}

@media (prefers-reduced-motion: reduce){
.skull-beam{ display: none }
}

/* ==========================================================================
   § 2 · THE FOURTEEN TOKENS ledger.css NEVER DECLARED.

   The old sheet's :root carries 103 custom properties and ledger.css's own
   103 cover all but these. They are read by rules in § 3 and by nothing in
   ledger.css, so they come across at the old sheet's values. Anything the two
   sheets BOTH declare is deliberately absent from this block: where the
   redesign has an opinion about --gold or --panel, the redesign wins, which
   is the whole point of cutting over.
   ========================================================================== */

:where(.ledger){
  --cast-1:0 10px 22px -12px rgba(0,0,0,.85),0 2px 5px -2px rgba(0,0,0,.5);
  --cast-2:0 16px 34px -16px rgba(0,0,0,.9),0 3px 8px -3px rgba(0,0,0,.55);
  --con-b:0px;
  --con-r:calc(var(--s9) * 6);
  --con-w:131px;
  --face-1:linear-gradient(180deg,rgba(var(--hue-32,60,92,72),.16),rgba(0,0,0,.20));
  --hair-3:rgba(var(--hue-40,120,170,140),.1);
  --ink-cool:rgb(var(--hue-45,196,208,196));
  --lift-hard:rgba(92,66,26,.44);
  --lip:inset 0 1px 0 rgba(var(--hue-23,228,240,226),.07);
  --sink-1:inset 0 1px 3px rgba(0,0,0,.5);
  --sink-2:inset 0 2px 8px rgba(0,0,0,.6);
  --track:rgba(var(--hue-26,24,44,34),.5);
  --ui-page:1;
}

/* ==========================================================================
   § 3 · THE ORPHANS — 375 class names, 517 rules, in the old sheet's order.

   Source order is preserved exactly, because a media query adds no
   specificity: a phone override written before the base rule it overrides is
   silently lost, and the old sheet interleaves the two. Re-sorting this
   section by component would break screens at 500px and nowhere else.

   Being loaded after ledger.css, a rule here wins a tie against it. That is
   intended and it is narrow: a carried selector always names at least one
   class ledger.css has never heard of, so the only ties it can win are inside
   an orphan component — `.trace .btn` tuning a button that only the trace
   sheet contains.
   ========================================================================== */

/* ==========================================================================
COMPONENTS — declared BEFORE the utilities so a utility can adjust one.
========================================================================== */
.panel:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--panel)}

/* THE SAME MATERIAL AS A PLATE. A card and a banner were iron with a gold
   edge and no bevel, sitting beside plates that had one -- so two boxes a few
   pixels apart caught the light differently for no reason anybody chose. They
   take the plate's lit top lip and dark bottom now. Their gold border stays:
   a card and a banner CARRY something (a notice, a rule you can edit), and the
   plate's quieter --line-3 edge is for a panel that merely holds a section. */
.card:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border:1px solid var(--gold-line);background:var(--panel-2);
padding:var(--s6) var(--s7);display:flex;flex-direction:column;gap:var(--s5);
box-shadow:var(--bevel)}

/* ==========================================================================
   FORGE -- the one inline edit.

   Three of the banners on this site were the same thing spelled three times:
   `col g4 banner r1 bd-gold danim` with a 300ms entrance, opened under a
   control to edit the thing above it. A sergeant met that shape three times
   and had to learn it three times.

   ORNAMENT AUDIT
     left rail + faint ember  STATE  this, and only this, is being edited.
                              EDITORS_SHUT already guarantees that at the
                              level of state; this is the same sentence said
                              where the reader can see it.
     the 220ms entrance       STATE  it appeared just now, under your hand.
                              It is the only motion in the shared layer, it
                              plays once, and it is never on something being
                              read -- only on the thing you just opened.
   It reuses the sheet's existing `enter` keyframe rather than adding one.
   ========================================================================== */
.forge:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:flex;flex-direction:column;gap:var(--s4);
padding:var(--s5) var(--s6);
border:1px solid var(--gold-line);border-left:2px solid var(--gold);
border-radius:var(--r1);
background:var(--panel-3);
box-shadow:var(--bevel),0 0 24px -14px var(--glow-live);
animation:enter var(--dur-in) var(--ease-settle)}

/* A menu that floats over the page: the roster's rank and operative pickers,
and anything else that has to sit above a row without being part of it. */
.pop:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;z-index:30;top:100%;left:0;max-height:260px;overflow:auto;
border:1px solid var(--gold);border-radius:var(--r1);background:var(--pop);
box-shadow:0 14px 40px -10px rgba(0,0,0,.9);padding:var(--s3);
display:flex;flex-direction:column;gap:var(--r2)}

/* A ROW OF A MENU. Laid out rather than run as text because half of them
   carry something on the right -- a count, a worth -- and the mark that says
   which one is set needs a column of its own so the words below it line up
   with the words above. */
.pop-item:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s3);
cursor:pointer;padding:5px var(--s4);font-size:var(--fs-5);color:var(--ink-cool)}

.pop-item:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(var(--hue-28,27,48,36),.8);color:var(--ink)}

.pop-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold)}

.pop-mark:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none;width:9px;font-size:var(--fs-2);color:var(--gold)}

.account-avatar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;overflow:hidden}

.account-avatar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;background:var(--bg)}

/* THE PLACES STAND APART. Owner, on the strategium's company beside the
   judging page: "give it some nice seperation and such to look like this."
   What that page does is not more ink -- it is air. A fireteam's groups carry
   a margin above them and its men sit in cards with real gaps, so YOURS TO
   JUDGE and OTHER SQUADS read as two things rather than as one list with a
   line in it. This aside had ten pixels above a place and two between its men,
   which is a single dense block however many rules run through it.
   The rule stays and the space around it grows. */
.cmd-tier:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:baseline;gap:var(--s4);
  padding:var(--s5) 0 var(--s2);margin-top:var(--s6);
  border-bottom:1px solid var(--hair)}

.cmd-tier:first-child:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-top:0}

/* NO RULE UNDER A MAN HERE, AND THIS IS THE THIRD TIME THAT ANSWER WAS WRONG.
   A hairline was put under each name to "get some seperation here like in the
   reports", and the result was lined paper: a place head already carries a
   rule, so a company of one-man places became heading-rule, name-rule,
   heading-rule, name-rule down the whole column. Owner: "please fix this!!"

   THE PATTERN, WRITTEN DOWN SO IT STOPS REPEATING. cbeea05 ruled the muster's
   rows and made a spreadsheet. This ruled a company's men and made a ledger.
   Both times the request was for SEPARATION and both times ink was the wrong
   currency for it. What the judging page does -- the page the owner keeps
   pointing at -- is air: a margin above each group and a real gap between
   cards, and its only hairline is the one beside a GROUP heading.

   So separation here is the gap above a place and the gap between its men, and
   the single rule under the place head. Nothing else. */
/* AND THE RHYTHM IS THE RECORD'S. Owner, pointing at the judging page: "needs
   the right spacing." What that panel actually does is give a row ROOM --
   .record-row pays --s5 top and bottom and .record-grid holds a 34px floor --
   and its rule is rgba(var(--hue-1,178,201,177),.05), half the strength of --hair, which is
   why you have to look for it rather than at it.

   A man here was 4px of padding in a 24px line, which is why nothing but ink
   ever looked like separation. He gets the record's floor instead. No rule:
   with a ruled place head above him, even a .05 hairline reads as lined paper
   at this column width. */
.cmd-aside:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .rail-sm{padding:var(--s4) var(--s3);min-height:calc(34px * var(--ui))}

.cmd-tier-name:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-3);
  letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--gold-dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.hair-b:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-bottom:1px solid var(--hair)}

.hair-t:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-top:1px solid var(--hair)}

.hair-r:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-right:1px solid var(--line)}

.rule-b:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-bottom:1px solid var(--rule)}

.notch:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){clip-path:polygon(13px 0,100% 0,100% 100%,0 100%,0 13px)}

.notch-sm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){clip-path:polygon(7px 0,100% 0,100% 100%,0 100%,0 7px)}

.topbar-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s6);padding-top:var(--s6);padding-bottom:var(--s6);row-gap:0}

@media (min-width:1000px){
.topbar-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:30px}
}

/* The band of big numbers at the top of a tab. Owner liked it on the roster
and asked for it on reports and statistics, so it is one shape in one place
rather than three that drift. */
.totals:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-wrap:wrap;border-bottom:1px solid var(--line)}

.total:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1;min-width:130px;padding:var(--s6) var(--s7);display:flex;
flex-direction:column;gap:5px;border-right:1px solid var(--line)}

.total:last-child:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-right:none}

.total-n:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-9);font-weight:500;
font-variant-numeric:tabular-nums}

/* ---- controls ---- */
.act:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:pointer;transition:background var(--t),box-shadow var(--t),
color var(--t),border-color var(--t)}

/* AND THE 860px QUERY THAT WAS HERE COULD NEVER FIRE. This tab is in
   DESKTOP_ONLY and the strip is gated on `wide = s.w >= 1000`, so the
   narrowest width at which the screen exists AT ALL is 1000. Everything below
   it was dead CSS pretending to be responsive. */
/* FOUR BROTHERS SIDE BY SIDE. Owner, 2026-09-12: "infact be able to open up 4
   people at once and see EVERYTHING they did and everything dont to them and
   by who."

   AS MANY COLUMNS AS THERE ARE MEN, supplied by the markup as --dn, the way
   .grid-hdr takes --dcols. Not auto-fit: two men asked for must be two wide
   squares and not two narrow ones beside two empty tracks.

   AND NOT ONE MEDIA QUERY, which is a correction. The tab is in DESKTOP_ONLY
   and its strip is gated on `wide = s.w >= 1000`, so the 860px rule directly
   above this can never fire on this screen -- it was written for a width that
   cannot reach the tab. The real narrow case IS 1000px, where four panes are
   about 230px each (about 340px at 1440), and that is why a pane holds a rail
   and a count of rungs rather than a ladder: nothing here needs a breakpoint
   because the narrowest live case is the case it is drawn for. */
.panes:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(var(--dn),minmax(0,1fr));
gap:var(--s5);align-items:start}

/* ======================================================== TRACE A BROTHER --
   Owner, 2026-09-12, on the audit log exactly as it shipped: "yeah as i thought
   nothing but text shit everywhere i have no idea what the fyuck im looking
   at." Then what he actually wanted, in his own words: "this needs to be member
   related not fucking role or action related", "I need to know what heppened to
   THIS GUY!! who dhd THIUS GUY effect?", "and when i see what what done to him
   i want to see by who?!? i need ot be able to trace through events."

   TWO COLUMNS, AND THE LEFT ONE IS PEOPLE. 268px is the narrowest column that
   holds a face, a name of the length this guild really uses -- "Grand Master
   Chaplain Sapphon | Legion" is 38 characters -- and a count; the right column
   takes the rest and its two panels split that between them.

   AND NOT ONE MEDIA QUERY BELOW 1000px. The tab is in DESKTOP_ONLY and the
   strip is gated on `wide = s.w >= 1000`, so the narrowest window that can
   reach this screen at all is 1000 -- I shipped an 860px breakpoint for this
   very tab once that could never fire. At 1000 the two panels are about 330px
   each, which is the width the rows below are drawn for. */
.trace:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:268px minmax(0,1fr);
gap:var(--s6);align-items:start}

/* THE ROLL STAYS WHERE IT IS AND THE LIST INSIDE IT SCROLLS. Sticking the
   whole plate would take the search box and the four orders out of reach the
   moment a reader scrolled 572 men, and they are the only way back. */
.trace-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:sticky;top:var(--s5)}

.trace-list:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){overflow-y:auto;max-height:64vh;min-height:0;
border-top:1px solid var(--hair)}

/* THE BOX SITS INSIDE THE PLATE'S RIVETS AND NOT ON THEM. .plate has no
   padding of its own and draws four rivets on its ::before at inset 5px, so a
   full-width control flush to its edge is drawn over them. The rows below have
   their own padding and clear it already. */
.trace-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .search{margin:var(--s4) var(--s5)}

/* ONE MAN ON THE ROLL. A grid and not a flex row, for the reason .audit-row
   states: a list whose left edge moves per row cannot be scanned. The count
   column is `auto` and sized to its own content -- a fixed column here is how
   "CHANNEL PERMISSIONS CHANGED" came to be painted over a brother's name at
   1440px on the feed. */
.trace-man:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:calc(22px * var(--ui)) minmax(0,1fr) auto;
align-items:center;gap:var(--s4);padding:var(--s3) var(--s5);
border-bottom:1px solid var(--rule);border-left:2px solid transparent}

.trace-man:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--row-alt)}

.trace-man-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--panel-on);border-left-color:var(--gold)}

.trace-nm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-6);line-height:1.15}

.trace-co:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-meta);
text-transform:uppercase;color:var(--ink-4)}

/* A MAN WHO HAS GONE AND A BOT ARE MARKED, NEVER DROPPED. House rule: nothing
   is hidden because a man has left -- his file is often the one somebody
   wants. 15 of the 567 have left and 11 are bots (measured against the live
   ledger 2026-09-12 21:15Z; the mock-up he approved was built on an earlier
   sweep that read 572/20/11, which is why the two disagree). */
.trace-gone:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .trace-nm,.trace-bot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .trace-nm{color:var(--ink-3)}

.trace-gone:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .trace-co{color:var(--bad-lit)}

.trace-ct:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);color:var(--ink-4);
text-align:right;white-space:nowrap}

/* 476 OF 572 HAVE NOTHING ON THEM (measured 2026-09-12), so his row is quiet
   rather than a pair of noisy zeroes down two thirds of the list. */
.trace-ct-quiet:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-6)}

/* THE MAN, AND THEN THE MEN HIS LOG NAMES. Owner, typing his own name and
   getting thirteen rows: "how come when i trype revelation i get all this
   shit?" The thirteen are a real answer to a different question, so they are
   banded rather than dropped. It spans all three columns of the row it is
   carried by -- see `band` in traceRoll for why it is not a row of its own --
   and the rule above it is the seam. */
.trace-band:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-column:1/-1;font-family:var(--mono);font-size:var(--fs-1);
letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--ink-5);
padding-bottom:var(--s3);margin-bottom:var(--s3);
border-bottom:1px solid var(--hair)}

/* WHY HE MATCHED, WHEN IT WAS NOT HIS NAME. A man found by a role nobody can
   see on his row reads as a wrong answer. */
.trace-lit:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(196,138,58,.28);color:var(--ink);border-radius:2px;padding:0 1px}

.trace-hit:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);color:var(--gold);
letter-spacing:var(--ls-1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* HOW THE ROLL IS ORDERED. Busiest answers "who is being worked on", latest
   answers "what just happened", a-z finds a man already in mind, and roster is
   the order the chapter itself musters in. Four questions, one control. */
.trace-sorts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;border-bottom:1px solid var(--hair)}

.trace-sort:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1;text-align:center;cursor:pointer;
font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-2);
text-transform:uppercase;color:var(--ink-5);padding:var(--s3) var(--s2);
border-right:1px solid var(--rule);background:none}

.trace-sort:last-child:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-right:none}

.trace-sort:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-2)}

.trace-sort-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold);box-shadow:inset 0 -2px 0 var(--gold)}

/* FOURTEEN DROPDOWNS, OVER BOTH PANELS AND INSIDE NEITHER. Owner: "filters
   would be rolls server actions etc etc all the filter dropdown you can think
   of", and then, on the two panels: "actually who he evffected and and dont to
   him is perfect." A filter that only applied to half the screen would be a
   filter nobody could trust, so they sit above both. auto-fit rather than a
   fixed count: fourteen controls at 146px wrap to four rows at 1000px and
   three at 1440 without a breakpoint for either. */
.trace-drops:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(auto-fit,minmax(146px,1fr));
gap:1px;background:var(--hair);border:1px solid var(--line)}

.trace-drop:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--bg);border:none;color:var(--ink-3);
font-family:var(--mono);font-size:var(--fs-2);letter-spacing:var(--ls-1);
padding:var(--s3) var(--s4);cursor:pointer;min-width:0;text-overflow:ellipsis}

.trace-drop:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink)}

.trace-drop:focus:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){outline:1px solid var(--gold);outline-offset:-1px}

/* A SET FILTER SAYS SO. Fourteen controls is exactly the number at which one
   left on by accident becomes "the screen is broken". */
.trace-drop-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold);background:var(--panel-on)}

/* TWO PANELS, SIDE BY SIDE AND EQUAL. Owner: "the right side needs to be the
   action", then "actually who he evffected and and dont to him is perfect."
   Neither half is the more important, so neither gets more room. */
.trace-sheet:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
gap:var(--s5);align-items:start}

.trace-side:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:0}

.trace-rail:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){overflow-y:auto;max-height:64vh;min-height:0}

/* THE DAY, ONCE, OVER THE ROWS THAT HAPPENED IN IT. Owner: "need to be able to
   sort by date as well" -- and before that, the fault it fixes: the feed
   printed "09-12" against two hundred consecutive rows, which is the same fact
   two hundred times and no help at all. The rows carry the time only. */
.trace-day:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-2);
text-transform:uppercase;color:var(--gold-ash);
padding:var(--s5) var(--s6) var(--s2);border-bottom:1px solid var(--rule)}

.trace-ev:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:calc(38px * var(--ui)) minmax(0,1fr);
gap:var(--s4);align-items:start;padding:var(--s4) var(--s6);
border-bottom:1px solid var(--rule)}

.trace-ev:last-child:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-bottom:none}

/* A KICK, A BAN AND A TIMEOUT GET THE EDGE. Those three are what a commander
   is scanning a wall of nicknames for; a rename is not one of them and stays
   quiet, which is the whole point of giving only three of them a colour. */
.trace-ev-heavy:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(155,44,36,.10);box-shadow:inset 2px 0 0 var(--bad)}

.trace-t:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);color:var(--ink-5);
font-variant-numeric:tabular-nums;padding-top:2px}

.trace-said:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-5);line-height:1.5;min-width:0}

/* THE VERB LEADS AND THE OTHER MAN IS THE LAST WORD ON THE LINE, because he is
   the next step of the trace. Owner, on the first cut: "need to be more clear
   what happened" -- the rows read "- Battle Brother by Caine", which is the
   payload said aloud rather than an account of an event. */
.trace-lead:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-weight:600;color:var(--ink)}

.trace-verb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-3)}

.trace-mono:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-4)}

/* HOW MANY DAYS TO SET A MAN ASIDE FOR. Owner: "remind me in X days let me
   specify that number" -- so it is a box he types in, not a menu of guesses.
   Sized to three digits because the server refuses anything over a year, and
   tabular figures so 7 and 30 do not jump the button beside them about. */
.days-box:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(46px * var(--ui));text-align:center;
font-family:var(--mono);font-size:var(--fs-2);font-variant-numeric:tabular-nums;
color:var(--ink-2);background:var(--panel);border:1px solid var(--rule);
border-radius:2px;padding:var(--s2) var(--s3)}

.days-box:focus:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){outline:none;border-color:var(--gold);color:var(--ink)}

/* THE ONE THING THIS SCREEN CANNOT ANSWER, SAID RATHER THAN LEFT BLANK.
   Discord records who DID a voice drag, the room and a count, and never the
   brother. Set quieter than the sentence it ends and allowed to wrap, because
   it is a caveat and not the event -- but it is on the line, not in a tooltip:
   the owner went looking for a move he had made and found nothing, which is
   exactly what an unexplained gap costs. */
.trace-unsaid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-5);font-size:var(--fs-2);font-style:italic}

/* A ROLE THAT MOVED, AND WHICH WAY IT WENT. Green is given, red is taken --
   the two colours the rest of this site already uses for those two meanings.
   Mono, because a role name is a token and not prose. */
.trace-pill:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-block;font-family:var(--mono);font-size:var(--fs-3);
padding:0 var(--s3);margin:1px 2px 1px 0;border:1px solid;white-space:nowrap}

.trace-add:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--good);background:rgba(var(--hue-2,127,180,137),.12);
border-color:rgba(var(--hue-2,127,180,137),.34)}

.trace-rem:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--bad-lit);background:rgba(196,104,95,.11);
border-color:rgba(196,104,95,.32)}

/* EVERY NAME IS A FACE AND EVERY FACE IS THE NEXT STEP. Owner: "and when i see
   what what done to him i want to see by who?!? i need ot be able to trace
   through events." So the other man in the act is drawn as himself -- his own
   Discord picture where he has one, 111 of 567 do (measured 2026-09-12
   21:15Z: `authors.avatar_path` joined to guild_members) -- and
   clicking him opens his file. That IS the feature. */
.trace-peer:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;gap:var(--s3);
vertical-align:-4px;font-family:var(--serif);font-size:var(--fs-6);
font-weight:600;color:var(--ink);padding:1px var(--s3) 1px 2px;border-radius:999px}

.trace-peer:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--panel-on);color:var(--gold-warm)}

/* A MAN WHO IS ON NO ROLL -- Discord's own acts, and the handful of ids this
   guild has never seen -- is still named and still drawn, and is not a door,
   because there is no file behind him to open. */
.trace-peer-gone:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-3);cursor:default}

.trace-reason:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:block;font-family:var(--mono);font-size:var(--fs-3);
color:var(--ink-4);margin-top:2px}

/* AN EMPTY PANEL IS THE COMMON CASE -- 294 of 567 men have nothing on them at
   all, measured 2026-09-12 -- so it says so in a sentence, with the floor
   named. A blank box reads as a broken screen. */
.trace-none:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s7) var(--s6);color:var(--ink-3);font-size:var(--fs-5);
margin:0}

/* THE WALK, ACROSS THE TOP. Every step back is one click, and the trail is the
   only record of where a reader has been -- the panels hold one man. */
.trace-trail:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-5);
color:var(--ink-2);background:none;border:1px solid var(--line);
padding:1px var(--s4);cursor:pointer}

.trace-trail:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-warm);border-color:var(--gold-line)}

.trace-trail-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink);border-color:var(--gold-line)}

.trace-who:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;flex-wrap:wrap;gap:var(--s5);
padding:var(--s6) var(--s7)}

.trace-who-name:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-8);font-weight:600;
line-height:1.05}

.trace-tally:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:auto;display:flex;gap:var(--s8)}

.trace-tally-n:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-8);font-weight:500;
display:block;text-align:right}

.trace-tally-k:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);
letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--ink-4);
display:block;text-align:right}

/* EVERY LANE LEAVES A LIGHT. Owner: "a flashing blue symbol on them".
   The fleet reads six reports at once; its single current id cannot name all
   six. Claimed rows pulse blue, queued rows wait dimly, and the glyph makes
   the work visible beside the words without taking away the row's tint. */
.scanning:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;background:rgba(122,146,196,.07);
box-shadow:inset 2px 0 0 var(--esc);animation:scanrow 1.4s ease-in-out infinite}

.scan-queued:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;background:rgba(122,146,196,.03);
box-shadow:inset 2px 0 0 rgba(122,146,196,.4)}

.scan-glyph:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-block;color:var(--esc-lit)}

.scan-glyph-reading:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:scanmark 1.4s ease-in-out infinite}

.scan-glyph-queued:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--esc);opacity:.5}

/* The reader's own backlog pulses, because it drains by itself and the only
   question about it is whether it is moving. */
.tally-scan:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgb(var(--hue-4,59,200,178));border-color:rgb(var(--hue-4,59,200,178));
animation:led 2.2s ease-in-out infinite}

/* ---- A FILTER SAYS ITS QUESTION AND ITS ANSWER ----
   Owner: "all the damn filters are crazy... lets combine them into their
   groups and make them dropdowns so its not so cluttered." Seventeen chips in
   four unlabelled groups are four controls, each the width of what it says.
   Built on .decide-v's shape -- the same gold-lined box with the same caret --
   because opening a list of words and picking one is the same gesture the
   override on a report already is, on the same screen. */
.filt:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:flex;align-items:center;gap:var(--s3);
flex:none;cursor:pointer;border:1px solid var(--gold-line);
background:var(--panel-3);padding:var(--s3) var(--s5);
transition:border-color var(--t)}

.filt:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold)}

.filt:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"▾";font-size:var(--fs-3);color:var(--gold-dim)}

.filt-k:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
letter-spacing:var(--ls-key);text-transform:uppercase;color:var(--ink-6)}

.filt-v:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-5);color:var(--gold-warm);white-space:nowrap}

/* Lit once it is narrowing the list, so a bar with something set on it reads
   as set at a glance and nobody has to open four menus to find out why the
   list is short. */
.filt-set:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold)}

.filt-set:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .filt-v{color:var(--gold)}

/* .chip-group is gone with the last row of filter chips. .chip itself stays:
   the rank scope on the permissions screen is one, and it is a control that
   says a value rather than a filter that says a choice. */
.search:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:7px;border:1px solid var(--hair-2);
background:var(--panel-3);padding:5px 9px;min-width:190px}

.search:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) input{background:transparent;border:none;outline:none;padding:0;
font-size:var(--fs-5);color:var(--ink);width:100%}

.field-num:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(60px * var(--ui));text-align:right}

.field-min:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(48px * var(--ui));text-align:right;border-color:var(--hair-2);
background:var(--panel-3);padding:3px 7px;font-size:var(--fs-5)}

.tick:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:11px;height:11px;flex:none;border:1px solid var(--line-3);background:transparent}

/* THE WORD IN AN EMPTY TILE. One type for both lines -- the slot it wants and
   the reason the picture is not here -- so the second reads as the quieter
   half of one sentence rather than as a different kind of text. */
.shot-slot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:calc(9.5px * var(--ui));line-height:1.6;
letter-spacing:var(--ls-ctl);color:rgb(var(--hue-38,85,100,87))}

/* A CSS BACKGROUND, NOT AN <img>, and that is not a style preference.
The template is served as ordinary HTML and the browser parses it before the
runtime ever sees it, so an img whose src is a binding gets fetched verbatim
by the preload scanner -- two 404s for the unresolved placeholder text on
every single page load. An unresolved binding inside url() is invalid CSS
instead, so the declaration is dropped and nothing is requested. */
.shot-img:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:100%;height:100%;background-position:center;
background-repeat:no-repeat;background-size:cover}

/* ==========================================================================
   MARK -- the one state pill, in the four lamps and nothing else.

   .shot-mark and .shot-mark-on are second names on the base and on the green
   one: skull.js points at `.aar-auspex .shot-mark` to place its verdict, so
   the class has to stay in the DOM. Whether a stats card came off a picture is
   one use of this pill, not a component of its own.

   ORNAMENT AUDIT: every one of these SHOWS A STATE, which is the whole
   component. The default is the COLD one -- a mark with no job given to it
   gives off no light, because --glow-spent is transparent. Four colours, four
   meanings, and the word inside says what the colour says, so nothing here is
   carried by hue alone.
   ========================================================================== */
.shot-mark:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;gap:var(--s4);flex:none;
padding:var(--s3) var(--s5);font-family:var(--mono);font-size:var(--fs-1);
font-weight:500;letter-spacing:var(--ls-2);text-transform:uppercase;white-space:nowrap;
color:var(--ink-5);border:1px solid var(--line-2);border-radius:var(--r1);
background:var(--panel-2);box-shadow:0 0 14px -6px var(--glow-spent)}

.shot-mark-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--good);border-color:var(--good-2);
box-shadow:0 0 14px -6px var(--glow-read)}

/* THE LAMP, opt-in. A dot in currentColor with its own halo, for the marks
   reporting a MACHINE's state -- the auspex verdict, a live sweep. Never on an
   ordinary label: a lamp on every pill is a string of lights, which is the
   noise the rule exists to stop. */
.shot-mark[data-verdict]:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{content:"";flex:none;
width:6px;height:6px;border-radius:50%;
background:currentColor;box-shadow:0 0 7px currentColor}

/* THE AUSPEX VERDICT TAKES ITS OWN HUE, EDGE AND ALL. The pill used to be a
   neutral green box with a coloured word in it, so the one component on the
   screen whose whole job is a verdict said it in text alone. data-verdict is
   the fact the page already states for skull.js -- "reading" / "bad" / "good"
   -- so the border and the wash read the same fact rather than a second copy
   of it, and no script has to hand out a class for it. */
.shot-mark[data-verdict="bad"]:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--warn-2);
border-color:rgba(181,112,90,.35);background:rgba(181,112,90,.08)}

.shot-mark[data-verdict="good"]:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:rgba(var(--hue-2,127,180,137),.3);
background:rgba(var(--hue-2,127,180,137),.07)}

/* HOW FAR THE JUDGEMENT HAS GOT. A report is settled a man at a time, and this
   is read at a glance a hundred times an evening, so it is a line rather than
   a sentence. */
/* 3px on a rounded ash track, which is the mockup's own bar. It draws ONE
   fill rather than the mockup's one pip per brother: a pip row needs an sc-for
   over the men, and {{ dSettledPct }} -- the fraction the sentence beside it
   quotes -- would then have nothing reading it. A dropped binding costs more
   than three gaps in a 3px line. */
.meter:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;height:3px;border-radius:var(--r1);
background:rgba(var(--hue-1,178,201,177),.14);overflow:hidden}

.meter-fill:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:0;top:0;bottom:0;border-radius:var(--r1);
background:linear-gradient(90deg,rgb(var(--hue-33,63,122,92)),rgb(var(--hue-5,121,196,141)));transition:width var(--t)}

/* THE TWO COLUMNS OF AN OPEN REPORT: what was filed on the left, what is being
   decided on the right. They were three columns in one wrapping row -- team,
   filing, evidence -- which put the buttons above the pictures they are about
   and left the sergeant scrolling between them. Both fall to one column on a
   phone, where the order is evidence first and the decision under it. */
/* THE SPACE BETWEEN THE COLUMNS IS THE CONTAINER'S, and it was seven panels'
   margins instead. Every panel carried `mt7 mx7`, so the gap between the two
   columns was two 16px margins meeting in the middle -- 32px that no single
   edit could retune -- and each panel sat --gut PLUS 16px in from the glass
   while the gutter's whole reason for existing is to be the one inset. Gaps
   here, no margins there: the columns are 24px apart, the panels in a column
   22px, and all three numbers are stated once. */
/* ONE TRACK GROWS AND IT IS THE FILING. The mockup's page is a grid --
   `minmax(620px,1fr) 560px` -- so the deciding column is 560px at every width
   and the filing column absorbs all the slack. Both tracks were flex:1 1 with
   grow enabled, which made the side column a function of the viewport: 565px
   at 1600 and 491px at 1280, against a drawn 560 at both. flex:0 1 lets it
   shrink below 560 when there is genuinely no room and never above it.
   The page's own top and bottom room is here too: 22px under the title block's
   rule and the mockup's deep 72px foot, which the panels used to answer for
   with margins and then stopped answering for at all. */
/* NOWRAP ABOVE THE BREAKPOINT, and that is a condition of the fixed track. A
   wrapping row breaks its line on the items' BASIS, not on their shrunk size,
   so the moment the deciding column stopped being a percentage and became a
   fixed 560px the two columns would have STACKED anywhere under about 1390px
   -- in source order, with no phone reordering, which puts the men and the
   buttons below everything a sergeant has already read. Both children carry
   min-width:0, so nowrap shrinks them instead and nothing overflows. Under
   1000px the media query below turns this into a single column properly. */
/*
 * THE MOCKUP IS A GRID, AND APPROXIMATING IT IN FLEX WAS WRONG.
 *
 * Its line 68, verbatim: display:grid; grid-template-columns:minmax(620px,1fr)
 * 560px; gap:24px; align-items:start. This was built as flex:1 1 620px against
 * flex:0 1 560px, which lands within a few pixels of the grid at 1280px and at
 * 1600px -- the two widths the mockup is drawn at, and the two I measured. It
 * does NOT land anywhere else: flex-basis is a starting point and the free
 * space is then shared, so on a 2526px monitor the filing column took ~1900px
 * and dragged every panel in it wide. The owner saw that before I did.
 *
 * AND IT IS NOT CAPPED. It was, briefly, at the mockup's own 1636px and
 * centred -- on the reasoning that "pixel for pixel" past 1600px can only mean
 * the page stops growing at the width it was drawn for. The owner looked at it
 * full width and settled it: "make them full width but respect the page
 * padding."
 *
 * Which is right, and the grid is why. Under the old flexbox BOTH columns grew
 * and the deciding column came out at 616px against a drawn 560; under
 * minmax(620px,1fr) 560px only the filing column takes the slack, so the
 * fireteam holds its drawn width at any monitor and the extra room goes to the
 * evidence and the record, which are the things that want it. The sides come
 * from .gutter on the scrollport, so the page's own padding is already
 * respected and nothing here needs to restate it.
 */
.aar-cols:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;
grid-template-columns:minmax(calc(620px * var(--ui)),1fr) calc(560px * var(--ui));
align-items:start;
gap:calc(22px * var(--ui)) calc(24px * var(--ui));
padding-top:calc(22px * var(--ui));padding-bottom:calc(72px * var(--ui))}

.aar-main:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:0;gap:calc(22px * var(--ui))}

/* IT STICKS, and the header above it does not -- the mockup pins the deciding
   column at top:20px and lets the filing scroll under it, which is the whole
   reason its header is a flat block rather than a bar. .aar-cols already sets
   align-items:flex-start, which is the precondition: a stretched item is full
   height and sticky never engages. On a phone .aar-side is display:contents
   and position is inert, so there is no narrow-width case to answer for. */
.aar-side:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:0;gap:calc(20px * var(--ui));
position:sticky;top:calc(20px * var(--ui));
/* CAPPED, OR THE PINNING HIDES THE BUTTONS IT EXISTS TO KEEP ON SCREEN.
   The deciding column holds the fireteam head, a ~140px card per brother
   with its own accept/refuse row, THE UNBOUND and the notes well: with
   three brothers it is taller than the scrollport at the mockup's own
   1600x1000. Sticky without a height cap pins the TOP of that column, so
   the later men's buttons sit below the fold for the whole scroll -- the
   exact inverse of the reason the column was made sticky. The cap makes
   the column scroll inside itself instead. No phone guard needed:
   .aar-cols > .aar-side is display:contents under 1000px, where position
   is inert. */
max-height:calc(100dvh - calc(40px * var(--ui)));overflow:auto}

/*
 * THE ORDER A PHONE READS AN AAR IN, which is not the order a desk reads it in.
 *
 * Owner, 2026-09-01: "fornthe phone view lets have the fire team at the top
 * then the evidence then the as written then the call then the notes." On a
 * desk the two columns stand side by side and there is no order to argue
 * about; folded into one, the columns simply concatenate -- evidence, filing,
 * notes, then the fireteam and the call underneath -- so the men he is
 * deciding about ended up below everything he had already read.
 *
 * AS WRITTEN IS GONE FROM THAT SEQUENCE and its order:5 with it. Owner,
 * 2026-09-04: "the as written hiddin shit is not needed!! at the bottom." THE
 * RECORD's first column carries what the brother filed, field by field, so the
 * panel was repeating what was already above it behind a click. The gap in the
 * order values is deliberate and costs nothing -- `order` sorts, it does not
 * count -- and leaving it means the panels that remain keep the numbers this
 * comment has always given them.
 *
 * `display: contents` is what makes this possible without a second copy of the
 * markup: the two column wrappers stop generating boxes, their panels become
 * direct children of .aar-cols, and one `order` on each puts them in his
 * sequence. One set of panels, one set of bindings, two readings.
 *
 * The breakpoint is 1000px because that is where `wide` flips in renderVals,
 * and a layout that disagreed with the script about which shape it is in would
 * be worse than either shape.
 */
@media (max-width: 999px){
/* CHILD SELECTORS, because both wrappers also carry `.col`, which sets
     display:flex at the same specificity and wins on source order -- measured
     in the browser: a wrapper went to contents and these two stayed flex, so
     nothing reordered and the side column kept a fixed width wider than the
     phone and clipped its own text. */
.aar-cols:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .aar-main,.aar-cols:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .aar-side{ display: contents; }
/* STRETCH AND DO NOT WRAP. .aar-cols is a wrapping row with
     align-items:flex-start, which is right for two columns side by side and
     wrong for one column of panels: measured, the panels came out 433, 433,
     433, 311 and 205 wide inside a 366 phone -- each sized to its own content,
     some overflowing and some stranded. In one column every panel is the width
     of the screen. */
/* One column. Was flex-direction, which a grid does not read -- with
     .aar-main and .aar-side both display:contents their panels are the
     grid's own children, so the track count is what stacks them. */
.aar-cols:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){ grid-template-columns: 1fr; align-items: stretch; }
/* THE AUSPEX WAS LEFT OUT, so on a phone it kept source order and led the
     report -- the machine's reading arriving before the men it is about. It
     belongs directly under the pictures it read. Anything unordered defaults to
     0 and jumps to the front, which is the bug, so every panel is named. */
.aar-team:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){ order: 1 }
/* Directly under the men, because that is what it is about: somebody who
     played and whom the ledger cannot name. Above the pictures, because the
     answer is a person on this report and not a detail of the evidence. */
.aar-unbound:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){ order: 2 }
.aar-evidence:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){ order: 3 }
/* THE STRIP UNDER THE PICTURES WAS THE ONE PANEL NOBODY NAMED, and the law
     three lines up is exactly what it broke: unordered, it defaulted to 0 and
     led the phone -- "nobody has read these screenshots yet" arriving above THE
     FIRETEAM, before the screenshots it is about. It draws only when
     dUnchecked is true, which is why it was never seen. Same order as the
     evidence, so source order keeps it directly underneath. */
.aar-unread:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){ order: 3 }
/* THE RECORD IS THE CALL, so it keeps the call's place in the owner's phone
     sequence -- "fornthe phone view lets have the fire team at the top then the
     evidence then the as written then the call then the notes". The auspex's
     order:4 went with the panel that carried it: its findings are inside THE
     RECORD now, and a rule naming .aar-auspex would ALSO match this panel (it
     still carries the class for skull.js) and fight this one on source order. */
.aar-record:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){ order: 6 }
.aar-notes:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){ order: 7 }
}

/* A menu hung off a control near the right edge opens inward. */
.pop-right:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){left:auto;right:0}

.lightbox:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:fixed;inset:0;z-index:70;display:flex;align-items:center;
justify-content:center;padding:var(--s7);
background:rgba(4,8,7,.94);backdrop-filter:blur(6px);
animation:fadein .18s ease}

.lightbox-panel:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s5);
max-width:min(96vw,1500px);max-height:94vh}

/* contain, because this is where the picture is actually read: a tall phone
grab and a wide monitor capture are both shown whole rather than cropped to
a box that suits neither. */
.lightbox-img:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:min(96vw,1500px);height:78vh;background-position:center;
background-repeat:no-repeat;background-size:contain;
border:1px solid var(--gold-line);background-color:var(--bg-deep)}

/* THE BROTHER'S CARD, which is a lightbox holding a panel rather than a
picture. It borrows the overlay and the click-away-to-close, and parts company
after that: a picture wants the whole viewport and a card wants to be a card,
so this one is narrow, has the panel's own ground under it, and scrolls
INSIDE itself rather than growing past the screen. */
/* A BROTHER ON AN AAR, and how far his approval has got. Three of these sit
where the filer's name used to, because an aar is settled by each man on it
being answered for separately -- so the row has to show all three and which of
them are done. Bordered rather than filled: three solid green blocks in a
sixty-row table is a wall, and the point is to be scannable. */
/* THE ONE CONTROL ON A FIRETEAM ROW. Owner, 2026-08-31: "simple checkbox next
to their name that only the sgt or above in THEIR company can click." Ticking
it credits him at what the aar is worth; the cross beside it refuses him and
asks why. It is drawn, not an <input>, for the same reason the WHO MAY ACT grid
is: a native checkbox cannot be given this palette on every browser. */
.tickbox:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;justify-content:center;
width:22px;height:22px;border:1px solid;border-radius:2px;
font-size:calc(13px * var(--ui));line-height:1}

/* .crew-chip is gone. It was the rounded pill a brother wore on a report, and
   it is the thing the owner pointed at when he asked for the roster to look
   like the reports section -- so it became .ident, the plate, and both of its
   users took the plate instead. */
.battle-link:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:inherit;text-decoration:underline;text-decoration-style:dotted;
text-underline-offset:.25em;cursor:pointer}

.battle-link:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.battle-link:focus-visible:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink);text-decoration-style:solid}

.card-panel:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:min(96vw,620px);gap:0;
background:var(--bg);border:1px solid var(--gold-line);
box-shadow:0 18px 60px rgba(0,0,0,.6)}

/* auto-fit rather than a fixed count: the same grid carries six facts on a
desktop and two across on a phone with nothing to switch on. */
.grid-facts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
gap:1px;background:var(--line);border-top:1px solid var(--line);
border-bottom:1px solid var(--line)}

@media (prefers-reduced-motion:reduce){
.lightbox:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
/* The forge's entrance is the only animation the shared layer adds, and it
   plays once. It still goes: a reader who asked for no motion asked for none. */
.forge:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
}

/* ---- lists: every table on the site is these two rules plus a template ---- */
.grid-hdr:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;align-items:center;gap:9px;padding:var(--s4) var(--s8);
border-bottom:1px solid var(--hair);background:rgba(var(--hue-72,9,18,14),.66);
color:var(--ink-4)}

/* The colour is INHERITED from .grid-hdr above rather than set here, so a
.c-gold on one header cell still wins. Setting it on this selector made it
(0,1,1) and it silently beat every colour utility -- which is how the points
column came out grey. */
.grid-hdr:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>div{font-size:var(--fs-2);font-weight:600;letter-spacing:var(--ls-2);
text-transform:uppercase;white-space:nowrap}

/* A shape, a line, and what to do next. The mark's job is BOUNDARY: something
   belongs in this space and is not here. It is UNLIT on purpose -- an empty
   state must never be the brightest thing on a screen, or absence reads as an
   event. The only light in the block is on the button, if the reader may act
   at all. (No wash behind the glyph: the border already bounds it and the
   glyph already says "missing", so a gradient there would mark nothing.) */
.empty-mark:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;justify-content:center;flex:none;
width:calc(44px * var(--ui));height:calc(44px * var(--ui));
border:1px solid var(--line-2);
font-family:var(--serif);font-size:var(--fs-8);line-height:1;color:var(--ink-6)}

.empty-title:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-7);
letter-spacing:var(--ls-2);color:var(--ink-3)}

.empty-line:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){max-width:46ch;font-size:var(--fs-6);line-height:1.45;color:var(--ink-4)}

.navtab-sm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-3);font-weight:600;letter-spacing:var(--ls-1);
text-transform:uppercase}

/* ==========================================================================
   ROUND THREE — new components, not conversions.

   Meant to be appended into ledger.css's COMPONENTS region (before the
   UTILITIES block), the same place .card/.pop/.totals/.bar live, so that a
   utility class placed alongside one of these still wins. Nothing in this
   file writes a raw hex or rgba() colour and nothing sizes text off a raw
   px value -- every colour and font size below is one of the tokens already
   declared in :root. No @keyframes name here collides with an existing one
   (led, shimmer, scanv, rise, glow, fadein, spin, enter, bootmark, bootline,
   track, wipe, tickin, glowtext, hairline, reticle, emberrise, emberglow,
   flame, halo, breathe, phosphor) -- this file's two are quotein/quoteout.
   ========================================================================== */
/* ==========================================================================
   THE QUOTE FOOTER

   Owner: "a fade in then fade and slide out making room for another to fade
   in", with "a blur and acceleration effect". One line rotates every 60s;
   the strip is atmosphere, so it gets a fixed height (no page jump when a
   longer quote replaces a shorter one) and stays out of the interface's way.

   .quote-in and .quote-out are the two overlapping instances themselves --
   each is an inset:0 layer inside .quote, which is why .quote only needs
   position + a real height and neither instance needs its own class for
   that. The entrance reuses the sheet's own decelerating settle curve
   (cubic-bezier(.2,.8,.2,1), the same one .anim-enter already uses); the
   exit reuses the sheet's own accelerating "pulled away" curve
   (cubic-bezier(.5,0,.75,0), the same one every .boot-leaving reversal
   already uses) rather than inventing a third feel for the same motion.
   ========================================================================== */
/*
 * THE SHELL IS AS TALL AS THE VISIBLE WINDOW, NOT AS TALL AS 100vh.
 *
 * Owner, 2026-09-01: "the buttons at the bottom dont exist just the underline
 * are they under the cool quote bar?"
 *
 * They were under the URL BAR. On a phone `100vh` is the viewport with the
 * browser's chrome HIDDEN -- the biggest it ever gets -- so while the address
 * bar is showing, the bottom of a 100vh column sits below the glass. The nav
 * strip is the last child, so it went first, and its top border was the last
 * thing still on screen: exactly "just the underline".
 *
 * `100dvh` is the viewport as it is RIGHT NOW and resizes as the chrome slides
 * away. The 100vh line stays above it as the fallback for a browser that does
 * not know dvh, which gets today's behaviour rather than a collapsed page.
 *
 * And the strip clears the home indicator. A row of buttons a thumb cannot
 * reach without hitting the system gesture area is a row of buttons that
 * misfires, so the inset is added to its padding rather than assumed to be
 * zero.
 */
.shell:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;overflow:hidden;background:#0B1210;
  height:100vh;height:100dvh}

.navbar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-bottom:env(safe-area-inset-bottom,0px)}

.quotebar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;flex:none;width:100%;overflow:hidden;
  /* TALL ENOUGH FOR THE TWO LINES IT ALLOWS. The cross-fade needs both quotes
     absolutely positioned over one another, so the bar cannot grow to its
     content and the height has to be stated. 56px fits one line and the
     attribution; a phone wraps far more often than a desk does, so it gets the
     second line's worth. Measured at 390x664: two lines at this size plus the
     attribution need 78. */
  height:calc(56px * var(--ui));
  padding:var(--s2) var(--s9);
  border-top:1px solid var(--hair-3);
  background:var(--bg-deep)}

/* GROUPED, NOT RANKED. Owner: "too many of them theres shold be smaller fewer
   clusters of them", then "3-5 of different sizes in 2 clusters on the left and
   right side." Twenty-two evenly spaced candles read as a fence. Two groups
   flanking the quote read as candles someone lit, and they frame the line
   instead of running under it. Four on the left, five on the right -- matched
   pairs look placed, uneven ones look lit. */
.cluster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:flex-end;gap:4px}

/* The wick's flame. Not a circle -- a teardrop, wide at the base and drawn to a
   point, which is the shape the eye actually reads as fire. */
.flame:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:50%;bottom:100%;width:4px;height:7.5px;
  margin-left:-2px;border-radius:50% 50% 45% 45% / 62% 62% 38% 38%;
  background:radial-gradient(ellipse at 50% 72%,
    #FFF2CE 0%, #F6D98A 26%, #D8A63C 55%, rgba(190,120,30,.55) 78%, transparent 100%);
  transform-origin:50% 100%;
  animation:flicker 2.3s ease-in-out infinite}

/* The light it throws. Kept wide and very faint: this is what makes the bar
   feel lit from below instead of having stickers on it. */
.flame:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";position:absolute;left:50%;top:50%;width:30px;height:26px;
  margin:-13px 0 0 -15px;border-radius:50%;
  background:radial-gradient(circle, rgba(232,176,74,.20) 0%, rgba(216,150,50,.07) 45%, transparent 72%)}

/* Burnt down unevenly, and the two clusters share no silhouette. */
.cluster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(1){height:15px}

.cluster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(2){height:8px}

.cluster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(3){height:19px}

.cluster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(4){height:11px}

.cluster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(5){height:22px}

.cluster:nth-child(2):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(1){height:10px}

.cluster:nth-child(2):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(2){height:17px}

.cluster:nth-child(2):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(3){height:9px}

.cluster:nth-child(2):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(4){height:21px}

.cluster:nth-child(2):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .candle:nth-child(5){height:13px}

/* OUT OF STEP, TWELVE WAYS. Duration comes from the cluster and delay from the
   candle's place in it, so every flame gets its own pair without twelve rules.
   The numbers share no factors -- a rank that flickers together reads instantly
   as one animation applied N times, which is what makes this look cheap. */
.cluster:nth-child(1):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .flame{animation-duration:2.9s}

.cluster:nth-child(2):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .flame{animation-duration:2.3s}

.candle:nth-child(1):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .flame{animation-delay:-.3s}

.candle:nth-child(2):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .flame{animation-delay:-1.7s}

.candle:nth-child(3):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .flame{animation-delay:-1.1s}

.candle:nth-child(4):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .flame{animation-delay:-2.3s}

.candle:nth-child(5):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .flame{animation-delay:-.9s}

/* A guttering candle is still a lit candle. Reduced motion stops the movement
   and keeps the light. */
@media (prefers-reduced-motion: reduce){
.flame:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
}

/*
 * CROPPED BY THE BOX, NOT BY A CLIP PATH. background-size:100% auto with the
 * position at 0 0 draws the picture --con-w SQUARE from the top-left corner,
 * and the box is only 600/768 of that tall -- so image rows 600 to 768, the
 * cabinet plinth there is no room for, fall outside the padding box and are
 * never painted at all. The mask takes the last thirty rows down to nothing so
 * the machine sinks into the page instead of ending on a cut line.
 */
/* ABOVE THE QUOTE BAR, BELOW EVERYTHING THAT MATTERS. 22 clears .quote (1) and
   the bar itself so the plinth draws over them, and stays under .pop (30), the
   toast (40), the skull (55) and the phosphor/bezel (60/61) -- a popup, a
   message and the servitor himself all still come out in front of it. */
.aar-console:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:fixed;z-index:22;pointer-events:none;isolation:isolate;
  --lit:0;--pop:0;
  right:var(--con-r);bottom:var(--con-b);
  /*
   * THE WHOLE PICTURE, BASE AND ALL. This was `* 600 / 768` -- the top 600 rows
   * of a 768x768 asset -- so that the plinth ran off the bottom and the machine
   * appeared to sink into the page. That was right while it sat behind the
   * quote bar. It now draws OVER the bar, and the owner saw the consequence at
   * once: "the console is missing its base." The crop was cutting rows 600 to
   * 767, which is 22% of the machine and every part of it that meets the floor.
   *
   * Square, because the asset is. Every percentage below moved with it -- they
   * were fractions of the 600 crop and are fractions of 768 now.
   */
  width:var(--con-w);height:var(--con-w);
  background:var(--console-art, none) 0 0/100% auto no-repeat;
  /* A breath of a fade at the very foot so it meets the bar rather than ending
     on a line. 2%, not the 5% that was hiding the plinth. */
  -webkit-mask-image:linear-gradient(180deg,#000 0 98%,transparent 100%);
          mask-image:linear-gradient(180deg,#000 0 98%,transparent 100%)}

/*
 * z-index 20 puts the machine above the embers, the candles and the quote, and
 * BELOW .pop (30) so a dropdown opening into the corner is never crossed and
 * below the toast (40) so a message to a reader always wins. He himself flies
 * at 55, so he is drawn in front of it -- which is the whole tableau: we watch
 * him work, over his shoulder.
 */
/*
 * THE GLASS, MEASURED OFF THE FILE RATHER THAN GUESSED. The tube's inner edge
 * traced at pixel resolution sits at x[176,596], y[152,391] of the 768px
 * picture. Against the element that is left 176/768, width 420/768, and -- the
 * box being cropped at row 600 -- top 152/600 and height 239/600.
 */
.aar-crt:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;overflow:hidden;
  /* The glass, traced at x[176,596] y[152,391] of the 768px asset. Vertical
     figures are of 768 now that the whole picture shows; the horizontal pair
     never changed, because the width never was cropped. */
  left:22.917%;top:19.792%;width:54.688%;height:31.120%;
  border-radius:4.8% / 8.4%;
  /* The phosphor wash behind the glyphs, and the spill onto the bezel. rgb(var(--hue-3,123,232,168))
     is the green the skull's own fill light already throws at this page. */
  background:radial-gradient(118% 130% at 50% 42%,
    rgba(var(--hue-3,123,232,168),calc(.11 * var(--pop,0))) 0%,
    rgba(var(--hue-3,123,232,168),calc(.04 * var(--pop,0))) 58%, transparent 100%);
  box-shadow:0 0 calc(20px * var(--pop,0)) rgba(var(--hue-3,123,232,168),calc(.20 * var(--pop,0)))}

/*
 * THE LIGHT DIES UNDER THE FRAME. This is the piece that actually sells "inside
 * the tube": the bezel's own shadow continued inward, so the rain falls off
 * beneath the metal instead of stopping flat at a rectangle. It has to be the
 * pseudo-element and not an inset shadow on .aar-crt itself, because an inset
 * shadow paints under the element's children and the canvas is a child.
 *
 * There are deliberately NO scanlines here. .phosphor already lays them over
 * the whole viewport at 3px, above both the console and the skull, so a second
 * set at a second pitch would only beat against the first.
 */
.aar-crt:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;
  box-shadow:inset 0 0 13px 4px rgba(0,0,0,.82), inset 0 2px 6px rgba(0,0,0,.9)}

/*
 * mix-blend-mode:screen is what makes the rain live IN the glass: the tube's
 * baked specular sweep and its vignette survive underneath it, and a black
 * pixel is the identity under screen, so the trails simply stop contributing as
 * they decay. The blend is confined to the machine because .aar-console is
 * position:fixed with a z-index and therefore already an isolated stacking
 * context -- isolation:isolate up there says so out loud rather than leaving it
 * to be inferred. The radial mask kills the rain before it reaches the corners,
 * which both matches the tube's own curvature and hides the pixel of
 * registration error that would otherwise show as a green line on the iron.
 */
.aar-rain:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;inset:0;width:100%;height:100%;
  opacity:var(--lit,0);mix-blend-mode:screen;
  -webkit-mask-image:radial-gradient(118% 128% at 50% 45%,
    #000 58%,rgba(0,0,0,.55) 84%,transparent 100%);
          mask-image:radial-gradient(118% 128% at 50% 45%,
    #000 58%,rgba(0,0,0,.55) 84%,transparent 100%)}

/*
 * THE WORKING SURFACE, PUBLISHED AS AN ELEMENT so that skull.js never has to
 * hold a number about this machine. It measures this box and puts his ARM TIPS
 * at its centre; everything else about where he hovers follows from the model.
 * Image x[144,624], y[462,558] -- the key deck, centred under the tube.
 */
/* The keys he stands at. Vertical figures rebased on 768 with the crop gone --
   77% and 16% of the old 600 are 60.156% and 12.5% of the whole. */
.aar-console-deck:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;left:18.75%;top:60.156%;width:62.5%;height:12.5%}

/* THE SAME LINE THE SKULL KEEPS, in both the places he keeps it: skullWanted()
   refuses anything under 1000px and .skull-toggle disappears there. A console
   exists FOR him to work at, and below this there is nobody to work it -- an
   AAR arriving still announces itself in the reports list. The height floor is
   the other half of the same judgement: on a short window the machine would
   swallow the page rather than stand in the corner of it. */
/* THE COMPANY GOES LAST ON A PHONE. Owner: "in the stratigum id like to put
   the company below everything else. Just want to see what I need to do."

   Side by side it is a column beside the work and reads as context, which is
   why it was put first -- but a phone stacks this row, and stacked it becomes
   a hundred-odd brothers standing between the reader and ASKED OF YOU. His
   own work is what the screen is for; the roll of the company is what he
   scrolls to when he wants it.

   ORDER, NOT A SECOND COPY OF THE MARKUP. .row-stretch is a flex row, so one
   declaration moves the whole panel and the desktop layout -- where the
   company still leads -- is untouched. */
/* A SUMMARY IS ONE LINE ON A PHONE. Owner: "I like the summaries but their way
   too big."

   Each tile is a three-line stack -- a label, a 28px figure, and a note under
   it -- which is right beside four of its kind on a wide screen and is most of
   the first viewport on a narrow one. Laid on its side the same tile is a
   label and a number on one line, and the four of them take two rows instead
   of eight.

   THE NOTE IS WHAT GOES, and nothing else. It is the line that explains the
   figure -- "Matches the filters below", "1 active vox channel" -- which is
   worth its room beside a column of numbers and is not worth a phone's screen
   under one. The FIGURES all stay, which is what was asked for.

   This rule once had to spare a hand as well: `gone quiet` was a tile carrying
   a control, drawn with the same .t4 the notes use. That tile is gone, so no
   tile holds anything but text and the exception went with it. If one ever
   does again, mark the control and spare it by name -- do not widen this to
   the class, or a hand will disappear on a phone and nothing will say so. */
/* The number drops from --fs-9 to --fs-6 because it no longer has a line to
   itself and must sit on the baseline of the words beside it. */
@media (max-width: 999px){
.total:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1 1 auto;min-width:0;flex-direction:row;flex-wrap:nowrap;
    align-items:baseline;gap:var(--s4);
    padding:calc(7px * var(--ui)) var(--s6)}
.total:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .t4{display:none}
.total:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .total-n{font-size:var(--fs-6)}
/* THE LABEL GIVES, THE FIGURE NEVER DOES. Owner: "the summary for mobile on
     roster needs to be styled like reports" -- and the two were already the
     same rule. What differed was the words: the reports bar's longest label is
     `awaiting auspex`, the roll's is `brothers on roster`, and a nowrap label
     that long pushed its own figure onto a second line. One tile on the roster
     was two lines high while every tile on reports was one, which is the whole
     of what he was looking at.
     So the row does not wrap at all now and the LABEL is what runs out of
     room, ellipsised, because a summary whose number has been pushed out of
     sight is not a summary. Reports is untouched by this: nothing there is
     long enough to reach the ellipsis. */
.total:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
    min-width:0;flex:0 1 auto}
.total:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .total-n{flex:none}
}

@media (max-width: 999px){
.cmd-aside:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){order:9}
}

@media (max-width: 999px){
.aar-console:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:none}
}

@media (max-height: 699px){
.aar-console:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:none}
}

/* A dead screen that lights is still a screen that lights. Reduced motion takes
   the rain's movement away -- console.js never starts a frame loop at all and
   paints one still screenful instead -- and keeps the light, which is the
   candles' own rule directly above. The cross-fade is what is left of it. */
@media (prefers-reduced-motion: reduce){
.aar-rain:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transition:opacity .24s ease}
.aar-crt:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transition:box-shadow .24s ease}
}

/* THE QUOTE RIDES ABOVE THE RANK, and carries its own darkness with it.
   Measured at 1440: a flame landed squarely on the "40K" of the attribution.
   Ornament never gets to cost a word, so the quote lays down a soft pool of the
   deep background under itself -- the candles still burn through at the edges,
   the letters stay crisp, and nothing had to be moved or removed. */
.quotebar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .quote{position:relative;z-index:1;
  background:radial-gradient(60% 130% at 50% 55%,
    rgba(11,18,16,.94) 0%, rgba(11,18,16,.82) 45%, rgba(11,18,16,0) 100%)}

/*
 * HE SHOOK THE MONITOR, AND LEFT A CRACK IN IT.
 *
 * Owner: "can we add a little shake to the whole page and a little crack in the
 * glass too? like he shook the monitor crask goes away after a few seconds
 * fades away?"
 *
 * THE SHAKE IS ON THE SHELL, which carries the bezel and the phosphor with it --
 * so what moves is the SCREEN, while he stays exactly where he struck it. That
 * is the right way round: he is the one doing the hitting. It is also why he is
 * not inside the shell; his canvas hangs off the body, untouched by this.
 *
 * Small and brief. A page that lurches is a page nobody can read, and the whole
 * effect is over in a third of a second.
 */
.shell.rattled:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:rattle .34s cubic-bezier(.36,.07,.19,.97) both}

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

/* THE PHOSPHOR WASH. The whole app is one screen in one machine, and a screen
   has scanlines and falls off at its edges. Kept far below the threshold where
   it costs anybody a word: 3% on the lines, and a vignette that only reaches
   the corners. It sits above everything and takes no clicks. */
.phosphor:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:fixed;inset:0;z-index:60;pointer-events:none;
  background:
    repeating-linear-gradient(180deg, rgba(0,0,0,.055) 0 1px, transparent 1px 3px),
    radial-gradient(120% 100% at 50% 50%, transparent 55%, rgba(0,0,0,.38) 100%)}

/* THE BEZEL. Where the screen stops and the machine begins: a lit inner lip on
   the top and left, shadow on the bottom and right, as though the glass is set
   into a frame lit from the same corner as everything else. */
.bezel:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:fixed;inset:0;z-index:61;pointer-events:none;
  /* ONE LIT EDGE. The left lip said the light came from up and to the left,
     which no other surface in the sheet agrees with -- every plate, card and
     banner lights its top only. The sides take the shadow instead. */
  box-shadow:
    inset 0 1px 0 rgba(255,228,158,.18),
    inset 1px 0 0 rgba(0,0,0,.4),
    inset -1px 0 0 rgba(0,0,0,.55),
    inset 0 -1px 0 rgba(0,0,0,.6),
    inset 0 0 40px rgba(0,0,0,.28)}

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

/* AFTER .quotebar, not before it: same specificity, so source order decides,
   and a media query above the rule it overrides does nothing at all. */
@media (max-width: 999px){
.quotebar:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){height:calc(78px * var(--ui))}
}

/*
 * A QUOTE THAT DOES NOT FIT WRAPS RATHER THAN STOPPING MID-WORD.
 *
 * Owner: "some ofnthe quotes are too big and dont fit how do we solve that?"
 * It was one line, nowrap, with an ellipsis -- so on a phone the longer ones
 * ended in "…" and the line nobody could read was the punchline. They are
 * quotes; the last few words are the point of them.
 *
 * Two lines, clamped. Two is the number because the bar is a fixed height --
 * the cross-fade needs both quotes absolutely positioned over each other, so
 * the bar cannot grow to fit its content -- and two lines of this size fit
 * inside it with the attribution underneath. A quote too long for two lines
 * still ends in an ellipsis, which is the honest floor rather than a promise.
 */
/* NO ITALIC. Cinzel is loaded as wght@400;600;700 -- there is no italic axis,
   so the browser was shearing the roman by hand. The same lines already render
   upright on the Settings tab, so this was inconsistent as well as synthetic. */
.quote-text:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--serif);font-size:var(--fs-5);
  letter-spacing:var(--ls-1);color:var(--ink-3);line-height:1.35;
  max-width:100%;overflow:hidden;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;
  text-overflow:ellipsis}

.quote-who:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);font-weight:600;
  letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--gold-dim);
  max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.quote-in:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.quote-out:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;inset:0;
  /* Both sweeps clip at the scanner, in the quote container's coordinates. */
  clip-path:inset(0 var(--wipe-r,0px) 0 var(--wipe-l,0px));
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--s2);text-align:center}

.quote-in:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:quotein .9s cubic-bezier(.2,.8,.2,1) forwards}

.quote-out:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:quoteout .7s cubic-bezier(.5,0,.75,0) forwards}

/* The servo-skull suppresses this on the layer itself when IT turns the strip
   over -- see skull.js. Toggling a rule from here restarted the animation the
   moment the toggle came off, which is worse than leaving it alone. */
/* The same hundred lines, read all at once on the Settings screen instead of
   one a minute along the bottom. It is a LISTING and not the strip: nothing
   here is a fixed height and nothing is clipped to one line, because the point
   of the screen is to read the whole of each one. The columns reflow off the
   same 280px measure the company grid uses, so a quote is never a single word
   wide on a large display or two words wide on a small one. */
/* ==========================================================================
   THE SUB-TAB BAR

   Sits under the main nav (Settings' permissions/admins/overrides/quotes,
   the Roster's brothers/companies), so resting state stays dim and small --
   only the active tab is allowed to touch gold, via its own class rather
   than a colour the row hands out for free.
   ========================================================================== */
.subtabs:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s2);width:100%;
  border-bottom:1px solid var(--hair-3)}

.subtab:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:pointer;flex:none;
  padding:var(--s4) var(--s6);
  font-size:var(--fs-3);font-weight:600;letter-spacing:var(--ls-2);
  text-transform:uppercase;color:var(--ink-4);
  border-bottom:2px solid transparent;
  transition:color var(--t),border-color var(--t)}

.subtab:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-2)}

.subtab-on:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink);border-bottom-color:var(--gold)}

.decide:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:flex;flex-direction:column;
  gap:var(--s3);min-width:calc(150px * var(--ui));flex:1 1 auto}

/* THE FIELD'S NAME, SET LIKE A COLUMN HEAD AND NOT LIKE PROSE. It was the
   body face in sentence case at the size of the value beside it, so on a row
   whose whole shape is "name, then what it says" the two ran together. */
.decide-k:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:var(--ls-key);text-transform:uppercase;color:var(--ink-6)}

.decide-v:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;display:flex;align-items:center;gap:var(--s3);
  cursor:pointer;border:1px solid var(--gold-line);background:var(--panel-3);
  padding:var(--s3) var(--s5);
  font-size:var(--fs-5);color:var(--gold-warm);
  transition:border-color var(--t)}

.decide-v:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold)}

.decide-v:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"▾";margin-left:auto;
  font-size:var(--fs-3);color:var(--gold-dim)}

.decide-set:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--gold);color:var(--gold)}

/* THE AUSPEX RETURN. A sensor names what comes back: a lock is the whole
   contact, a ghost is a reading that may not be there at all. Coloured on the
   same three-step ramp the rest of the panel uses -- brass for good, ash for
   partial, warm for a return worth doubting -- rather than a fourth palette. */
.lock:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:var(--ls-2);text-transform:uppercase;white-space:nowrap;
  padding:2px 7px;border-radius:2px;border:1px solid currentColor}

.lock-full:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--good)}

.lock-strong:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold)}

.lock-part:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-ash)}

.lock-ghost:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--warn-2)}

.decide-src:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);
  letter-spacing:var(--ls-1);text-transform:uppercase;color:var(--ink-5)}

/* ONE ROW OF THE RECORD. Its own padding rather than .py5, because the row needs
   side padding for the hover ground and the ellipsis to have air, and its own
   divider rather than .rule-b, because a rule BETWEEN ROWS is half the weight
   of the rule that ends a block -- .05 against .10. The hover is cool and very
   faint on purpose: --lift is the warm "you are here" lamp for a control, and
   a row of a record is not a control. */
.record-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s5) var(--s3);
  border-bottom:1px solid rgba(var(--hue-1,178,201,177),.05);
  transition:background var(--t)}

.record-row:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(var(--hue-1,178,201,177),.035)}

/*
 * THE RECORD DRAWS AT 70%. Owner, 2026-09-04: "lets also make the record a
 * little smaller 30% smaller."
 *
 * IT IS ONE SCALE, NOT TWENTY EDITS. Every measure in this panel is already
 * written as `calc(Npx * var(--ui))` or as a token off the same ladder, so the
 * whole thing shrinks by moving --ui underneath it -- and it keeps shrinking
 * correctly the day somebody changes a track width or a row height, which
 * hand-multiplying twenty numbers would not.
 *
 * THE LADDER HAS TO BE DECLARED AGAIN HERE, and this is the part that is easy
 * to get wrong: a custom property is substituted where it is DECLARED, not
 * where it is used. --s5 and --fs-4 are declared on :root, so they resolved
 * against the PAGE's --ui before this panel ever inherited them; redefining
 * --ui alone would shrink the four bare calc()s in the rules below and leave
 * every padding and every type size exactly as they were. Re-declaring the
 * ladder against the local --ui is what makes the panel scale as one thing.
 *
 * Only the rungs the record actually draws with are listed. A rung it does not
 * use would be dead weight, and a rung it starts using later will inherit the
 * page's value and stand out immediately -- which is the failure being visible
 * rather than silent.
 */
.aar-record:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){
  /* AND BACK UP A LITTLE. Owner, 2026-09-05: "the ENTIRE record needs to be a
     little bigger." .7 was 30% off a full-size table and it read as a table
     seen from further away; .8 keeps the record smaller than everything around
     it, which is what the original ask was for, without making a sergeant lean
     in. The whole panel still moves as ONE number -- that is the point of the
     paragraph above -- so this line is the entire change to its box. */
  --ui:calc(var(--ui-page) * .8);
  /*
   * THE BOX SHRINKS BY 30%. THE TYPE DOES NOT, and running them together was
   * the mistake. Owner: "the text is WAY too small in the record now."
   *
   * He asked for the RECORD to be smaller, and what makes a table smaller is
   * its rows and its gutters -- the padding, the row height, the track widths,
   * all of which ride --ui above. Type is not a dimension of the box: --fs-1 is
   * already 9px, and 30% off it is 6.3px, which is not small type, it is
   * unreadable type. The ladder is declared against the PAGE's scale with a
   * gentle trim instead, so the panel reads as tighter rather than as further
   * away.
   */
  /* THE TYPE COMES BACK TO FULL SIZE with it. The .92 trim existed to make a
     30%-smaller box read as tighter rather than as further away; at .8 the box
     no longer needs the words apologising for it, and "the ENTIRE record"
     means the words as well as the table. */
  --fs-1:calc(9px * var(--ui-page));  --fs-2:calc(10px * var(--ui-page));
  --fs-3:calc(11px * var(--ui-page)); --fs-4:calc(12px * var(--ui-page));
  --fs-5:calc(13px * var(--ui-page)); --fs-6:calc(14px * var(--ui-page));
  --s1:calc(2px * var(--ui));  --s2:calc(4px * var(--ui));
  --s3:calc(6px * var(--ui));  --s4:calc(8px * var(--ui));
  --s5:calc(10px * var(--ui)); --s6:calc(12px * var(--ui));
  --s7:calc(16px * var(--ui)); --s8:calc(20px * var(--ui))}

/* ---- THE RECORD'S THREE COLUMNS -----------------------------------------
   ONE TRACK LIST, TWO USERS. The column strip and every row's inner grid read
   the same declaration, so a column that widens widens in both and the heads
   stay over the values they name. They are the mockup's own tracks, its own
   12px gutter and its own 34px row. */
.record-hdr:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.record-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;
  grid-template-columns:minmax(calc(165px * var(--ui)),1fr)
                        minmax(calc(165px * var(--ui)),1fr)
                        minmax(calc(150px * var(--ui)),max-content);
  gap:0 var(--s6)}

/* 9px down to the rule, which is .plate-head's own step and not on the --s
   ladder. Sides are --s3, so the strip starts on the same 26px as the rows
   under it -- the panel's 20 plus the row's 6, which is the mockup's 20+6. */
.record-hdr:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-items:center;padding:0 var(--s3) calc(9px * var(--ui));
  border-bottom:1px solid var(--line-3)}

/* The middle head takes the brass ash the mockup gives it. --gold-ash IS its
   #8c7a52; nothing new is minted for it. */
.record-hdr-mid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-ash)}

/* HIS OWN COMPANY, SET APART FROM THE REST OF THE STRIKE TEAM. Owner,
   2026-09-15: "when you're approving aars as the sgt I want to see your
   company seperated a bit from the rest of the brothers in the strike team."

   The heading was already drawn and carried FOUR PIXELS above it -- less than
   the gap between two brothers of the same squad -- so the first man he cannot
   answer for sat level with the last man he can, and the division he is meant
   to read was the only thing on the card that did not look like one.

   THE GAP IS THE DIVISION, not another rule: the group heading already draws a
   hairline of its own, and two lines four pixels apart is a box, not a break.
   The cards below it are stood down rather than hidden -- he still has to see
   who else was on the run, and the men he is answering for are the ones the
   eye should land on. Hover brings a stood-down card back to full, because
   reading a name is not the same as being asked for a word about it. */
.crew-group-other:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-top:calc(22px * var(--ui))}

.crew-card-other:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){opacity:.66}

.crew-card-other:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.crew-card-other:focus-within:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){opacity:1}

@media (prefers-reduced-motion:no-preference){
.crew-card-other:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transition:opacity .14s ease}
}

/* WHICH ONE IS YOU. Set in the gold the page uses for the reader's own name
   everywhere else, so it is recognised before it is read. */
/* ON LEAVE. Owner: "he just gets a label on his name in the roster 'on leave'."
   Quiet on purpose -- it is a fact about a man, not a warning about him, so it
   reads like the rank beside it rather than the ON VOX badge, which is a thing
   happening right now. */
/* THE COMPANY AS ITS POST READS IT. A position is a heading in the chapter's
   own words with its men under it, indented by what it answers to -- the same
   shape a man sees in #legionnaires-rank-and-file, which is the point. */
.org-post-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;gap:var(--s5);align-items:baseline;
  padding:var(--s5) var(--s6) 2px var(--pad);margin-top:var(--s4);cursor:grab;border-radius:3px}

.org-post-row:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(230,223,204,.05)}

.org-man-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;padding:2px var(--s6) 2px var(--pad);border-radius:3px}

.org-man-row:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(230,223,204,.03)}

.org-held:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){opacity:.4;cursor:grabbing;background:rgba(180,143,50,.12)}

.leave-badge:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:.08em;text-transform:uppercase;white-space:nowrap;
  padding:1px 5px;border:1px solid #4A5A6A;border-radius:3px;color:#8FA3B8}

/* THE GUILD KNOWS HIM. A green disc and a mono word beside the YOU badge, the
   same measure as it so a man who is both does not get two different chips.
   It is drawn only when the last sweep listed him -- see teamOf(). */
.crew-dc:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;gap:calc(5px * var(--ui));
  font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--good);
  margin-left:calc(6px * var(--ui));padding:1px 5px;
  border:1px solid rgba(var(--hue-5,121,196,141),.35);border-radius:2px;vertical-align:2px}

.crew-dc-dot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:4px;height:4px;border-radius:50%;background:var(--good);flex:none}

/* HIS THREAD, indented under his row so it reads as belonging to him rather
   than to the card's edge, and separated by a rule the way the log upstairs is. */
.crew-thread:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-top:calc(8px * var(--ui));margin-top:calc(2px * var(--ui));
  border-top:1px solid var(--rule)}

.crew-say:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-top:calc(6px * var(--ui))}

.h-esc-line:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:rgba(122,146,196,.55);color:var(--esc-lit)}

.pill-variance:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--bad-lit);
  padding:2px 7px;border:1px solid rgba(192,90,60,.34);border-radius:2px;
  background:rgba(192,90,60,.10);white-space:nowrap}

.record-hdr-end:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){text-align:right}

.record-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-items:center;min-height:calc(34px * var(--ui))}

/* STRETCHED, AND RULED ON ITS LEADING EDGE. The divider belongs to the COLUMN,
   not to the line of type, so it runs the full height of the row -- which is
   what makes six rows read as a table rather than as six stacked pairs. */
.record-mid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;align-self:stretch;min-width:0;
  padding-left:var(--s6);border-left:1px solid var(--hair)}

/* NO overflow:hidden, and the mockup's own rule would have been a bug here:
   the override menu is an absolutely-positioned .pop hanging off this cell, and
   a clipped cell would cut it off at the row's edge. min-width:0 is what the
   ellipsis beside it actually needs. */
.record-end:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;justify-content:flex-end;align-items:center;
  gap:var(--s4);min-width:0}

/* THE TABLE FOLDS, because the mockup never had to. It is drawn at a hard
   min-width:1280px, where three tracks with a 480px floor always fit; this app
   runs down to a phone, where 480px of tracks inside a 340px panel would push
   the row's own text off the side of the screen. One column, no rule, heads
   dropped -- every row already carries its field name in .decide-k.
   These sit HERE and not in the block at the top of the sheet: a media query
   adds no specificity, so written up there they would lose to the base rules
   above on source order. */
@media (max-width:999px){
.record-hdr:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:none}
.record-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:1fr;gap:var(--s3);align-items:flex-start}
.record-mid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-self:auto;padding-left:0;border-left:0}
.record-end:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){justify-content:flex-start}
}

/* Sized to its own column rather than to the panel, so a long explanation
   makes its field taller instead of stretching the whole row. */
.decide-help:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--fs-3);color:var(--ink-4);line-height:1.4;
  max-width:calc(260px * var(--ui))}

.decide-worth:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:auto;font-family:var(--mono);font-size:var(--fs-3);
  color:var(--gold-dim);text-align:right}

/* ==========================================================================
   THE MUSTER -- the companies, and the one standing open beside them.

   Owner, 2026-09-01: "lets workflow the whole companies tab in the roster i
   want that to look and feel fucking awesome", then "i want it to look and
   feel good NOT POPULATE IT", then "yeah look feel functionality framework for
   when the bot is done... it looks like shit right now... and is clunky to
   use."

   WHAT WAS WRONG WAS NEVER THE CONTENT. Seven near-identical cards filled the
   top third of a 1600px screen and abandoned the rest; the grid resolved to
   four columns so the seventh row was three cells short; the widest run on a
   card about a company was its sergeant's name; and every place inside an
   opened company got a full-width iron plate whether it held six men or one.
   Nothing below adds a fact to the screen. One count is repaired and one
   sentence -- "no discord role", printed fourteen times above the men on the
   Legion -- is deleted rather than restyled.

   .co-slot is DELETED, not kept out of caution: declared, used nowhere, and
   named in .rail's own doctrine as the inferior version of itself -- "and
   :focus-within so a keyboard can reach it, which .co-slot's version could
   not". .co-group is deleted too. It is a rank-group divider and it was used,
   once, to wrap the LAST line of a company card, where its :first-child escape
   hatch can never fire -- so every card carried an accidental 13px indent and
   an accidental --hair-3 rule, the outermost division in this sheet, drawn
   inside a 300px card. Nobody chose that. .co-grid goes with the grid;
   .quote-grid keeps the rule it was sharing.

   ORNAMENT AUDIT
     the plate's rivets     BOUNDARY  the tab is one panel, said once
     the roll row's rule    BOUNDARY  where one company ends
     .co-lead               BOUNDARY  where a place's name ends and the figure
                                      that answers it begins. A leader: the
                                      oldest device in typesetting, and the
                                      only thing on this screen that is not a
                                      letter.
     .rail-act hover/press  STATE     you are here, and you are pressing
     .rail-on               STATE     this company is the one standing open
     .rail-you              WHOSE     yours -- a shape, not a second gold
     .mark-read .mark-lamp  STATE     a machine fills this and is watching it
   THREE OF THE FOUR LAMPS ARE UNSPENT HERE. --glow-owed and --glow-spent are
   left alone on purpose: when the bot starts governing a place there will be a
   real machine state to light, and a screen that has already used its lights
   to say "this is a company" will have nothing left to say it with.
   ========================================================================== */
/* Two names for one grid now: the quotes, and any plate laid beside another.
   The companies left it -- seven cards in an auto-fill grid resolve to four
   columns at 1440px, and seven is 4+3 with a hole. */
.quote-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;gap:var(--s5);
  grid-template-columns:repeat(auto-fill,minmax(calc(280px * var(--ui)),1fr))}

/* THE WHOLE TAB IS ONE PLATE. A grid of cards de-rivets because twelve riveted
   rectangles say "this is a panel" twelve times; a single panel rivets, which
   is the same rule read the other way.

   overflow is the INITIAL value restated, and it is a guard rather than a
   layout: .plate does not clip today, and this is (0,1,0) declared after it,
   so if a later hand gives .plate an overflow the place menus still escape
   their column. Said plainly because a rule that only restates a default
   reads as load-bearing to the next person, and this one is only a latch. */
.muster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){overflow:visible}

/* THE SPLIT. 1000px is the threshold the interface already calls `wide` and the
   one --ui steps at, and renderVals decides which panes exist, so the layout
   and the script cannot disagree about which shape they are in.
   min-height so a four-brother company is FRAMED rather than a stub under a
   full roll -- the plate reaches, and the room shows below it, which is what
   the fixed arch behind this screen has always been for. */
.muster-split:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:stretch;min-width:0;gap:var(--s6);padding:var(--s6)}

@media (min-width:1000px){
.muster-split:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(480px * var(--ui))}
}

@media (max-width:999px){
.muster-split:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex-direction:column;padding:var(--s5);gap:var(--s5)}
}

/* A PANEL EACH, WITH THE SAME DEPTH EVERY OTHER BLOCK ON THE SITE HAS. Owner,
   2026-09-11: "it needs sepertion the middle and the left both need tiehr own
   depth panels." One hairline between two panes made them one pane with a rule
   down it; these are two objects now -- lit top edge, cast shadow, the plate's
   own face -- sitting in the plate that holds them both. */
.muster-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.muster-one:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;border-radius:var(--r2);
  background:var(--panel-3);background-image:var(--face-1);
  border:1px solid var(--line-3);border-top-color:rgba(var(--hue-8,210,225,210),.14);
  box-shadow:var(--bevel),var(--cast-2)}

/* THE WIDTH OF THIS PANE IS NOT SET HERE. ledger-extra.css loads after this
   sheet and redeclares `.muster-roll`'s flex at the same specificity, so it
   wins on order and this basis is never the one in force. Two rounds of
   widening were shipped into this line and changed nothing anybody could see.
   The basis below is the floor the other sheet also uses, so reading either
   one alone gives the same answer; the SHARE that makes it grow is over
   there, under "THE COMPANY SHEET SHARES ITS WIDTH TOO". */
.muster-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){
  flex:0 1 calc(420px * var(--ui));min-width:0;overflow:hidden}

.muster-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .rail:last-child{border-bottom:none}

/* A LITTLE AIR BETWEEN THE COMPANIES. Owner: "the companines tab on the left
   needs a little seperation". Seven companies stacked on one rule each read as
   a single ruled block; a company is a thing you pick, not a line you scan.
   The rules stay -- this roll is a list of choices and they mark where one
   ends -- and the room around them grows, which is the answer that has worked
   every other time on this site. */
.muster-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .rail{padding-top:var(--s5);padding-bottom:var(--s5)}

/* AND AIR AT THE SIDES, WHICH THEY HAD NONE OF. Owner, of this pane and the
 * chart beside it: "the company to the left serioussllyyy please fix that".
 *
 * `.ledger .rail` ZEROES the horizontal padding every rail carries by default
 * (ledger.css:598) -- the sheet hands a rail its gutters from the recess it
 * sits in, and `.ledger :is(.cmp,.roll) .rail` is the only rule that puts them
 * back. These rows are in neither, so a company's face, its name and its
 * tally all sat hard against the pane's own border, and the pane clips
 * (`overflow:hidden`), so anything that did not fit was cut rather than
 * spilled. --s7 is the number that recess uses, so the roll now reads with
 * the same gutter as every other list on the site.
 *
 * The chart beside it padded #command-board here, and went with
 * command-tree.js on 2026-09-24. */
.muster-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .rail{
  padding-left:var(--s7);padding-right:var(--s7)}

.muster-one:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1 1 auto;min-width:0}

/* A CHAPTER WITH NO COMPANIES IN IT. shownCo is null, so atCoOne is false and
   the roll is the only pane -- and an sc-if compiles to a React.Fragment with
   no wrapper element (support.js:645), so the roll genuinely IS the only child
   here. Without this, the first screen a new chapter ever sees is NO COMPANIES
   YET centred in a 363px gutter with a 528px vertical rule beside it and a
   thousand pixels of empty plate to its right: the abandoned screen this whole
   rebuild exists to kill, in the one state it cannot be argued with. */
.muster-roll:only-child:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1 1 auto}

@media (max-width:999px){
.muster-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:1 1 auto}
}

/* A COMPANY IS A ROW IN A LIST, and everything else about it is the rail's --
   the brass rule under it, the warm hover, the 3px state edge, the
   :focus-within that reveals its own controls. Only the axis changes: two
   lines rather than one, and --s5 top and bottom against .rail's --s4, because
   seven of these are the whole left-hand side of the screen and are read at
   arm's length rather than scanned at a hundred an evening. */
.co-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s5) var(--s7);gap:var(--s5)}

/* THE FIGURE, IN ITS OWN COLUMN, so the seven form a column down the roll
   instead of floating to wherever each name ran out. text-align:right and
   .num's tabular figures, because a column that twitches is not a column. */
.co-tally:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none;display:flex;flex-direction:column;align-items:flex-end;
gap:var(--s1);text-align:right}

/* A PRESS, which this sheet has never expressed anywhere, and --lift-hard has
   sat in the tokens with no user since it was named. One step of the same warm
   lamp. On a phone it is the only answer a thumb ever gets. */
.muster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .rail-act:active{background:var(--lift-hard)}

/* A KEYBOARD CAN REACH EVERY CONTROL ON THIS TAB NOW, so it has to be able to
   see where it is: the sheet's only focus style was input:focus, and the three
   text boxes were the only focusable things on 8467 lines. Inset by a pixel so
   a focused row does not grow. */
.muster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) :focus-visible{outline:1px solid var(--gold);outline-offset:-1px}

/* A THUMB HAS NO HOVER. .on-hover is opacity-only so a screen reader keeps it
   and :focus-within so a keyboard reaches it -- and a touch device, which has
   neither, lost it completely. Scoped to this tab so no other screen changes. */
@media (hover:none){
.muster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .rail .on-hover,.muster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .co-place-head .on-hand{opacity:1}
}

.co-place:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:0;padding-bottom:var(--s6)}

/* A BIG PLACE FLOWS INTO COLUMNS RATHER THAN RUNNING OFF THE SCREEN. Owner:
   "this just has so much ampty space and scrolls down".

   The split above says plainly why it cannot fix this by itself: "the order of
   places IS the chain of command, and re-ordering to square the columns would
   throw away the one fact the order carries... the room under RESERVES is the
   shape of the 10th, not a fault." That is true of the PLACES. It is not true
   of the MEN inside one: seventy-three reserves have no order worth keeping
   beyond their run headings, and a tower of them is what makes the 10th scroll
   while a third of the window sits empty.

   So the chain of command keeps its order and a crowd stops pretending to be a
   queue. Only a place big enough to be a wall gets this -- below that a second
   column would put two names beside each other and call it a layout. */
.co-men-split:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){column-width:calc(230px * var(--ui));column-gap:var(--s8)}

/* A man, and the run heading above him, must not be torn across the break. */
.co-men-split:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > *{break-inside:avoid}

.co-men-split:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .rail-group{break-after:avoid}

/* THE ONE GLYPH A PLACE REVEALS. Deliberately NOT named .on-hover: a place
   contains rails, `.rail .on-hover` is already scoped to the rail, and reusing
   the name here would light every man's x the moment a hand entered the place
   -- which is the ninety-glyph screen this replaces, arrived at from the other
   direction. Two reveals at two scales need two names. */
.co-place-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .on-hand{opacity:0;transition:opacity var(--t)}

.co-place-head:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .on-hand,.co-place-head:focus-within:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .on-hand{opacity:1}

/* The confirmation is the menu's own box with the question in it: same
   component, no new one, and nothing floats over the page that the reader did
   not open.

   .pop.co-ask, not .co-ask, and the doubled class is load-bearing: the sheet
   sizes the floating menu a second time in the UTILITIES region at :2162, and
   the utilities land after the components. At (0,1,0) this rule would set a
   measure that a later rule of equal weight silently took back -- the exact
   class of defect the ordering comment on this block exists to prevent. A
   sentence needs more measure than a list of four words does. */
.pop.co-ask:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s4);padding:var(--s5);min-width:calc(268px * var(--ui))}

.co-empty:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .empty-mark{width:calc(30px * var(--ui));height:calc(30px * var(--ui));
font-size:var(--fs-6)}

.co-empty:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .empty-line{font-size:var(--fs-5)}

/* THE BAR WRAPS ON A PHONE, HERE, because this one carries a decision and not
   only a filter. Below 1000px the shared bar goes flex-wrap:nowrap with
   overflow-x:auto and no scroll affordance of any kind: measured at 390px its
   content was 480px in a 332px box, so a button could sit with its centre off
   the screen and be reached only by guessing that a 27px strip scrolls
   sideways. A control you cannot see is not a control. THE SECOND BUTTON HAS
   SINCE GONE -- `build from discord` was the one measured off the edge, and
   the seed it triggered runs on its own after every worker push -- but the
   rule stays: the search placeholder is the longest on the site at 164px and
   wrapping is still the only way it fits the box it is in. Scoped to .muster
   so no other list screen changes. */
@media (max-width:999px){
.muster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>.bar{flex-wrap:wrap;overflow-x:visible}
.muster:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>.bar>.search{flex:1 1 100%;min-width:0}
}

@media (prefers-reduced-motion:reduce){
/* A guttering candle is still a lit candle: the movement goes, the light
     stays. .anim-enter, .danim and .forge are already cut by the sheet's own
     three hooks; these two are the transitions it does not reach. */
.co-place-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .on-hand{transition:none}
}

/* ==========================================================================
   REDUCED MOTION -- one consolidated block, the same pattern the sheet
   already uses for .act/.btn/.chip and for .boot-leaving. The quote
   crossfade is disabled outright rather than slowed: the outgoing instance
   is pinned to opacity:0 and the incoming to opaque and sharp, so the swap
   still happens, it just happens as a cut instead of a slide-and-blur.
   ========================================================================== */
@media (prefers-reduced-motion:reduce){
.quote-in:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.quote-out:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
.quote-in:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){opacity:1;filter:none;transform:none}
.quote-out:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){opacity:0;filter:none;transform:none}
.subtab:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.decide-v:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transition:none}
}

/* A brother's own Discord picture, filling the same circle the initials use.
   The URL lives in data-lazy-bg until IntersectionObserver sees the circle. A
   CSS background in the rendered row is fetched even when that row is hundreds
   of places below the viewport; the roster used to download every full-sized
   Discord avatar on first open. Initials remain visible until the image loads.

   THE SELECTOR IS DOUBLED and that is load-bearing. `color:transparent` is
   what hides the initials once the picture is behind them, and at (0,1,0) it
   was silently taken back by .c-2 -- also (0,1,0), declared 96 lines later in
   the utilities -- on every face whose class string carries a colour. Measured
   on the companies tab: "avatar avatar-sm c-2 lazy-avatar avatar-face" resolved
   to rgb(var(--hue-21,169,188,175)), so two-letter initials were drawn ON TOP of fifty-three
   brothers' photographs. .avatar.avatar-face is (0,2,0) and cannot lose to a
   utility whatever order the sheet is read in. */
.avatar.avatar-face:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:transparent;background-size:cover;background-position:center;
background-repeat:no-repeat;text-shadow:none}

/* Owner: "put notes in its own section in a bigger text box under overrides",
   and this was 132px tall with 96px of padding under the caret to answer it.
   THE ELEMENT IS AN <input>, not a textarea -- it has always been one line of
   text -- so the height bought no room to write in, only 96px of dead box
   below a caret pinned to the top of it. The mockup draws a single-line
   composer, which is what the control actually is. If he wants the room back
   it is a textarea, not a min-height. */
.notes-box:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:100%;line-height:1.5;
  padding:var(--s5) var(--s6);
  font-family:var(--sans);font-size:var(--fs-5)}

.row-stretch:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:stretch}

.fn:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none}

.mh0:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:0}

.h100:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){height:100%}

.ac:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-items:center}

.at:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-items:flex-start}

.as:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-self:flex-start}

.jc:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){justify-content:center}

.jb:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){justify-content:space-between}

.fill:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute;inset:0}

.abs:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:absolute}

.fixed-fill:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:fixed;inset:0}

.rel:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative}

.clip:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){overflow:hidden}

.scroll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){overflow:auto}

.scroll-x:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){overflow-x:auto}

.none:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){pointer-events:none}

.p0:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:0}

.p4:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s4)}

.p5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s5)}

.p6:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s6)}

.p7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s7)}

.p8:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s8)}

.px5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s3) var(--s5)}

.px6:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s4) var(--s6)}

.px7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s5) var(--s7)}

.pad-card:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s6) var(--s8)}

.pad-panel:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s7) var(--s8)}

/* A section heading sits closer to what it introduces than to what is above
it, which is why the three-value paddings existed in eleven places. */
.pad-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s7) var(--s8) var(--s3)}

.pt3:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-top:var(--s3)}

.pt5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-top:var(--s5)}

.pb7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-bottom:var(--s7)}

.py5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--s5) 0}

.m0:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin:0}

.mt1:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-top:var(--s1)}

.mt7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-top:var(--s7)}

.mx7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin-left:var(--s7);margin-right:var(--s7)}

/* THE GUTTER, and it is one number so nothing can drift out of line with
   anything else. Every screen used to run to the glass: the roster's first
   column, the reports table and the report's own sticky header all began at
   pixel zero, which on a wide monitor reads as an unfinished window rather
   than a full one. The nav bar above takes the same value, so the sigil, the
   tabs and the content below them share one left edge.

   It scales with the window rather than sitting at a fixed width -- a laptop
   cannot spare 40px and a 32-inch monitor wants more than 12 -- and it is
   applied to the SCROLL CONTAINER, so a sticky header inside a screen is
   inset with everything else instead of sliding out from under it. */
.gutter:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-left:var(--gut);padding-right:var(--gut)}

.num:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-variant-numeric:tabular-nums}

.w5:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-weight:500}

.w6:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-weight:600}

.w7:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-weight:700}

.ls1:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){letter-spacing:var(--ls-1)}

.ls2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){letter-spacing:var(--ls-2)}

.ls3:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){letter-spacing:var(--ls-3)}

.ls-key:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){letter-spacing:var(--ls-key)}

.ls-ctl:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){letter-spacing:var(--ls-ctl)}

.up:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){text-transform:uppercase}

.tight:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){line-height:1.1}

.snug:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){line-height:1.3}

.c-cool:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-cool)}

.c-6:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-6)}

.c-gold-warm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-warm)}

.c-gold-dim:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-dim)}

.c-ash:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-ash)}

.c-bad-lit:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--bad-lit)}

/* ---- fills ---- */
.bg-base:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--bg)}

.bg-deep:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--bg-deep)}

.bg-panel3:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--panel-3)}

.bg-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--row-alt)}

.bg-track:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--track)}

.bg-line:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--line)}

.bg-gold:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--gold)}

.bg-good:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--good)}

.bg-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--bad)}

/* ---- edges ---- */
.bd:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border:1px solid var(--gold-line)}

.bd-hair:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border:1px solid var(--hair-2)}

.bd-line:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border:1px solid var(--line-3)}

.bd-gold:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border:1px solid var(--gold)}

.bd-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border:1px solid var(--bad)}

.r1:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-radius:var(--r1)}

.r2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-radius:var(--r2)}

.rc:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-radius:50%}

.anim-enter:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:enter var(--dur-screen) var(--ease-settle)}

.sticky:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:sticky}

.grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid}

.pre:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){white-space:pre-wrap}

.block:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:block}

.fit:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){object-fit:contain}

.cover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){object-fit:cover}

.pop:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:calc(230px * var(--ui))}

/* A card and a banner already took the plate's lip; they take its cast too, at
   the NESTED step, because they sit inside sections rather than on the page.
   The forge keeps its ember and gains the ground under it. */
.card:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background-image:var(--face-1);box-shadow:var(--bevel),var(--cast-1)}

.forge:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:var(--bevel),var(--cast-1),0 0 24px -14px var(--glow-live)}

/* WHAT YOU TYPE INTO IS CUT INTO THE PANEL, NOT SITTING ON IT. One groove, on
   both text surfaces the app has, so a field and a search box are the same
   depth on every screen. */
.search:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:var(--sink-1)}

/* ---- the four steps as classes, for everything that is not a component ----
   A tile, a bar, a drawer, a reading well. Composable: they set one property
   and expect a border and a ground from the classes already on the element. */
.raise-1:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:var(--bevel),var(--cast-1)}

.sink-2:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:var(--sink-2)}

.face-1:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background-image:var(--face-1)}

/* THE PINNED SURFACE, with a name. The nav, the mobile nav and .bar all solve a
   sticky translucent surface with blur(7px) and .bar's own comment calls that
   "one material"; the AAR header was a fourth pinned surface solving it with a
   hand-written gradient and no blur. background-COLOR rather than the
   shorthand, so a face can be laid over it. */
.glass:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background-color:var(--panel);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px)}

/* A THING THAT RISES UNDER THE HAND — the tile going from resting on the panel
   to held above it. THE FULL TRANSITION LIST IS RESTATED ON PURPOSE: a bare
   `transition:box-shadow` here lands after .shot at equal specificity and would
   REPLACE its list, and the tile would stop lifting at the moment it was given
   a shadow. If .shot's transition ever changes, change this with it. */
.h-raise:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transition:box-shadow var(--t),transform var(--t),border-color var(--t),background var(--t)}

.h-raise:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:var(--bevel),var(--cast-2)}

.pip-sm:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:8px;height:8px;flex:none}

.pip-gold:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--gold-warm);border-color:var(--gold-warm)}

/* A reason that actually cost him the points, against one that only notes something. */
.pip-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--bad);border-color:var(--bad)}

/* A BUTTON THAT IS OFF. It was two colour utilities and an inline cursor, so
   the shared layer had a state it could not say. */
.btn-off:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:not-allowed;color:var(--ink-6);border-color:var(--line-3)}

/* THE DRAWER THAT OPENS UNDER A ROW: an editor cut into the card it belongs to,
   which is why it sinks rather than rises. Its entrance was a CONSTANT passed
   through --danim on three byte-identical elements — the dynamic hook being
   used as a literal. It reuses the sheet's `enter` keyframe, the same one the
   forge opens with. */
.drawer:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:var(--sink-2);animation:enter var(--dur-in) var(--ease-settle)}

.list-cap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){max-height:calc(230px * var(--ui))}

.pop-wide:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:calc(250px * var(--ui));max-height:calc(320px * var(--ui))}

@media (prefers-reduced-motion:reduce){
.h-raise:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){transition:none}
/* The drawer's entrance plays once and is the same one the forge's is. A
   reader who asked for no motion asked for none. */
.drawer:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
}

/* ==========================================================================
DYNAMIC HOOKS — the only thing a style= attribute may carry.
A value the server computes cannot live in a stylesheet, so it arrives as a
custom property and one of these reads it. See DESIGN.md for the form.
========================================================================== */
.dc:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--c)}

.dbg:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--dbg)}

.dbd:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--dbd)}

.dw:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:var(--dw)}

.dh:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){height:var(--dh)}

.dpad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:var(--dpad)}

.dgap:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--dgap)}

.dfs:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-size:var(--dfs)}

.dcols:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-template-columns:var(--dcols)}

.danim:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:var(--danim)}

.dflex:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:var(--dflex)}

.dta:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){text-align:var(--dta)}

.dcur:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:var(--dcur)}

.dshadow:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:var(--dshadow)}

.dstyle:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-style:var(--dstyle)}

/* ==========================================================================
   THE MUSTER — a brother, marked, on every list there is.

   Owner: "id like the users everywhere in the roster to be more awesome
   looking", then "I didnt want the whoele row to be a glass tube just the
   brother...", then "yeah not a fan of the glass bubbles how can we make them
   look badass?" -- and then a picture of the thing he actually wanted, with
   "lets actually make it look more liks this."

   TWO WRONG ANSWERS BEFORE THIS ONE, and they were wrong the same way: both
   put the brother in a CONTAINER -- a glass capsule, then a stamped plate --
   and his mockup has none. A man on a muster roll is not an object in a box.
   He is an index, the numeral of his company, the stamp of his rank and his
   name, laid out on a ruled table, and the only thing drawn around anything is
   the small dark chip his rank sits in.

   WHAT MAKES IT READ is the ruling, not the ornament: full-height hairlines
   between the columns, the brother's own column on a ground a shade lighter
   than the rest, and a legend under the foot of the list. That is a muster
   roll, and it is what the picture is.
   ========================================================================== */
/* THE MARK ITSELF: no ground, no border, no shadow. A row of them IS the list. */
.ident:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;gap:var(--s4);
  min-width:0;max-width:100%}

/* The name is the only part that gives -- and on the two-line form it is the
   COLUMN holding the name that has to. .ellipsis and .col both set a floor of
   their own content, and a flex item's floor is its content, so without this a
   long name pushes the marks off the end instead of truncating. */
.ident:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>.ellipsis,.ident:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>.col{min-width:0}

/* A BROTHER ON A REPORT keeps an edge, because a dozen of them wrap inside one
   cell and have to be told apart -- and the edge is the colour of what his
   sergeant did with him, which is the one piece of state a crew mark carries. */
.ident-tint:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:2px var(--s4) 2px var(--s4);border-radius:var(--r2);
  background:rgba(0,0,0,.22);box-shadow:inset 0 0 0 1px var(--dbd)}

/* THE SAME MARK WITH NOTHING TO SAY. THE RECORD's AS WRITTEN column is the
   brother's own words, so the men he named are drawn without the credit
   colour: a sergeant's decision inside the brother's quoted squad is the
   conflation this record's three columns exist to undo. */
.ident-quiet:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:2px var(--s4);border-radius:var(--r2);
  background:rgba(0,0,0,.22);box-shadow:inset 0 0 0 1px var(--hair)}

/* ---- THE RULED TABLE ----
   .rail-grid centres its cells and spaces them with a gap, which leaves a
   divider nothing to run down: a border on a centred cell is only as tall as
   its own text. So the muster stretches its cells, drops the gap, and pays for
   the spacing with padding -- and the hairline then runs the full height of
   the row, which is what a ruled table is. The header takes the same class, or
   the ruling and the labels part company at the first long name. */
.muster-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){align-items:stretch;gap:0}

/* THE HEADER KEEPS ITS OWN VERTICAL PADDING AND GIVES UP ITS HORIZONTAL. The
   cells below supply that now, and a header that kept both would sit its
   labels 16px to the right of the columns they name. */
.grid-hdr.muster-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-left:0;padding-right:0}

.muster-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>*{display:flex;align-items:center;min-width:0;padding:0 var(--s6)}

.muster-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>*:first-child{padding-left:var(--s7)}

.muster-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>*+*{border-left:1px solid var(--hair)}

.muster-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>*:last-child{justify-content:flex-end;padding-right:var(--s7)}

/* The men are lit a shade above the facts beside them. It is the difference
   between a list of brothers and a spreadsheet about them. */
.muster-grid:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>*:first-child{background:rgba(var(--hue-1,178,201,177),.025)}

/* ON VOX IS THE ONLY THING WORTH A BADGE. The column used to print OFF VOX six
   hundred times, which is six hundred rows saying nothing -- the fact anybody
   opens this screen for is who is in a channel NOW. Off and unknown are blank,
   and the cell's title still says which of the two it is. */
.vox-badge:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:var(--ls-2);white-space:nowrap;
  padding:2px 6px;border-radius:var(--r2);
  color:var(--good);border:1px solid rgba(var(--hue-2,127,180,137),.45);
  background:rgba(var(--hue-2,127,180,137),.08)}

/* THE FOOT OF THE ROLL: what the dashes meant, and how many men were counted. */
/* ROOM FOR A MAN WITH TWO NAMES. Owner, on the roll: "we need some padding
   here." Every other grid on this site puts ONE line in a row, and the 10px of
   --dpad that is right for those wraps a thirty-pixel block here: the muster's
   first cell carries a man's name and his handle stacked, so the roll reads as
   one paragraph of men rather than as a list of them.

   A FLOOR RATHER THAN MORE PADDING, for the reason the record gave when the
   strategium had the same complaint: a row told how short it may be keeps its
   rhythm whether a man has a handle, a vox flag, both or neither. Reports and
   the honours are untouched -- their rows are one line and already sit right. */
/* A BADGE IS THE ROLE'S OWN PICTURE, at the size a name can carry. Discord
   draws these at 32 and up. It went out at 16px, half-lit, on the reasoning
   that a badge must not compete with a man's face -- and the owner read it the
   way it looked: "make the icons a little bigger they are way too small". An
   honour that has to be squinted at is not being shown. 22px is a little over
   a line of type, which is what a thing worn ON a name should be, and the
   dimming is gone: "that is a distinguised icon its important to always show."
   The bytes are asked for at 64 so the picture is still sharp on a retina
   screen at this size. Never allowed to shrink -- a row running out of width must
   take it from the name, which ellipsises, and not from the badge, which would
   simply become a smudge. */
/* AND ROOM AT THE EDGES OF THE ROLL. Owner: "please give the brother column
   some padding now since you removed the old badges." The stencils that used
   to sit in front of a man's face were also, accidentally, his left margin --
   with them gone his portrait starts hard against the plate's own edge, and
   the "brother" heading above him is indented 16px by .muster-grid's first
   cell while he is not.

   PADDING INSIDE THE CELL, never on the row. --dpad is the row's box and
   widening it there would shrink every track by 32px while the header's stay
   full width, walking all six columns to the left of the labels that name
   them. Padding inside the first and last cells moves the ink and leaves the
   tracks exactly where the header put them. The figures at the far end get
   the same treatment for the same reason -- .muster-grid ends on --s7 too, and
   a points column flush to the edge reads as an overflow. */
.muster-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>*:first-child{padding-left:var(--s7)}

.muster-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>*:last-child{padding-right:var(--s7)}

/* THE TICK ON A PICKED REPORT. A box rather than a native checkbox: the rows
   are a grid of hairlines and mono figures, and a platform control would be the
   one thing on the screen drawn by somebody else's designer. Fixed size so a
   ticked row and an unticked one are the same height -- a list that shifts as
   you tick down it is a list you lose your place in. */
.pick-box:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:calc(15px * var(--ui));height:calc(15px * var(--ui));
  border:1px solid var(--line-3);border-radius:var(--r1);
  font-family:var(--mono);font-size:var(--fs-2);line-height:1;color:transparent}

/* HONOURS ARE A DESK VIEW. Owner: "lets remove them from the momile view only
   keep them on the pc view." Uncapped, they are the widest thing on a roster
   row, and a phone row has a name and a handle to fit before it has room for
   ornament. Hidden rather than capped, so the desk view stays complete. */
@media (max-width:999px){
.role-badge:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:none}
}

.role-badge:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none;width:calc(22px * var(--ui));height:calc(22px * var(--ui));
  object-fit:contain;vertical-align:middle}

.role-badge-lg:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:calc(30px * var(--ui));height:calc(30px * var(--ui))}

.muster-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-height:calc(52px * var(--ui))}

.muster-card:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-top:var(--s7);padding-bottom:var(--s7)}

.muster-foot:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s5);
  padding:var(--s5) var(--s7);border-top:1px solid var(--hair)}

/* THE ROW A HANDLE NOBODY HOLDS SITS ON, on the honours. */
.row-orphan:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){box-shadow:inset 2px 0 0 var(--ink-6)}

/* ==========================================================================
   THE AAR JUDGING PAGE — the places the owner's mockup and the shared
   components disagree, and the mockup wins for this page alone.

   EVERY RULE HERE IS SCOPED. .mark is the app's one state pill, .btn-gold its
   one primary action, .field its one form control and .pad-card its one body
   inset — the mockup contradicts all four, but only on this screen, and this
   sheet has been burned before by a page's opinion escaping into a component.
   Scoped here means: it lands on the judging page and nowhere else, without
   touching the component or reordering anything.

   Placed after the d* hooks on purpose. .dbg, .dbd and .dcur are `background`,
   `border-color` and `cursor` at (0,1,0) declared last, so a component's own
   declaration always loses to them; a rule that has to survive one of those
   has to sit here AND carry two classes.
   ========================================================================== */
/* ---- the page title block ------------------------------------------------
   Flat on the page ground: the mockup gives it padding and a 1px bottom rule
   and nothing else — no ground, no blur, no shadow and no position. It is the
   DECIDING column that is pinned now (see .aar-side), which is what lets the
   filing scroll under a fixed fireteam rather than under a bar repeating a
   word the fireteam already says. Sides come from .gutter, so the block starts
   on the same left edge as the panels beneath it. */
/* THE DECIDING COLUMN IS A TRACK, NOT A SHARE, and the track is declared on
   .aar-cols now rather than here. This used to be flex:0 1 560px, overriding
   the `dflex` binding (--dflex:1 1 {{ dSideW }}, flex-GROW enabled, which made
   the column a function of the viewport: measured 565px at 1600 and 491px at
   1280 against a drawn 560 at both). Both are inert under a grid, which is
   what the mockup actually declares, so the width is the 560px track and the
   binding stays in the markup untouched and unused. It is left there because
   removing it is a markup change for no gain, and the script still sets it. */
/* IT RUNS THE FULL WIDTH BECAUSE THE COLUMNS DO, and that is the whole rule:
   the way back has to sit on THE RECORD's left edge and WORTH on the
   fireteam's right, so the header can only ever be as wide as the page under
   it. Both were capped at 1636px and centred for an hour; the owner asked for
   full width, .aar-cols lost its cap and THIS ONE WAS LEFT BEHIND -- which is
   how the header came to sit centred and narrow over columns running edge to
   edge. Same two declarations on both, or neither. */
.aar-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:calc(22px * var(--ui)) 0 calc(18px * var(--ui))}

.aar-head-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s9)}

.aar-ref:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border:0;background:none;padding:0;text-align:left}

.aar-ref:focus-visible:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){outline:1px solid var(--gold);outline-offset:3px}

/* Owner, on the header this block replaces: "we see awaiting judgment back and
   worth its too tall and takes up too much screen." The WORTH figure is the
   tall thing, so it is what comes down -- 26px against the desktop's 40 keeps
   the whole block to one row's height on a phone without losing the figure. */
@media (max-width:999px){
.aar-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-top:var(--s7);padding-bottom:var(--s6)}
.aar-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .worth-n{font-size:var(--fs-9)}
}

/* BACK IS A BUTTON. Owner: "make it an actuall button and give it a little
   space". .btn is already the mockup's declaration character for character —
   mono 500, --fs-2 (its 10px), --ls-ctl (its .12em), uppercase, --r1, 9px
   vertical, --ink-2 — so this adds only the three it differs on: the flex the
   arrow needs a gap in, its 13px sides, and its slightly lifted hairline. */
.btn-back:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:inline-flex;align-items:center;justify-content:center;
gap:var(--s4);padding:9px calc(13px * var(--ui));border-color:var(--hair-2)}

/* WHAT THE REPORT IS WORTH, AS THE PAGE'S HEADLINE FIGURE. It was a 22px mono
   chip in a bordered box down in the fireteam's action row; the mockup sets it
   at 40px Cormorant, unboxed, at the top right of the title block. There is no
   type token at 40 (--fs-9 is 26, --fs-10 is 44), so it is a literal scaled by
   --ui. The colour stays on .dc: {{ dScoreCo }} is a live binding. */
.aar-worth:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding:0 var(--s3)}

/* THE STATE, AS A PILL RATHER THAN A LOOSE WORD. The mockup has no state word
   in its title block at all — it has a headline, and the app has no headline
   binding to draw. So the word stays and takes the shape the mockup DOES use
   for a state, its verdict chip in THE RECORD's head; that also gives
   {{ dBannerBd }} an edge to colour, which is what it was computed for. */
.aar-banner:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-radius:var(--r1);padding:5px var(--s5)}

/* ---- the panel's body ----------------------------------------------------
   THE MOCKUP PADS THE PLATE, NOT THE BLOCKS INSIDE IT: 18px 20px 20px on the
   panel, with the head adding 9px down to its rule and 14px of margin under
   it. The app splits that between .plate-head and .pad-card, which is fine
   until a panel stacks two bodies — then the plate's single 20px foot gets
   paid twice and the 14px under the rule gets paid to a block that is not
   under the rule. These two say it once each: the first body after the head
   takes the 14, the last body in the panel takes the 20, and a body in the
   middle keeps --s6. */
.plate:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .plate-head + .pad-card{padding-top:calc(14px * var(--ui))}

.plate:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .pad-card:last-child{padding-bottom:var(--s8)}

/* THE ONE PANEL THE MOCKUP DOES NOT LIGHT. Every other plate it draws has the
   lifted top edge .plate now carries; THE FIRETEAM is a uniform hairline,
   because it is the one panel whose contents are themselves cards. */
.aar-team:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-top-color:var(--line-3)}

/* THE HEAD BLOCK: title, bar and gate line, closed by ONE hairline.
   The mockup pads this whole block 17/20/14 and rules it off; .plate-head
   already pays the 18 above and the 9 down to its brass rule, so the block
   adds only the 14 underneath and the cool hairline that ends it. The bar and
   the sentence take the side padding the head has, so all three start on the
   same 20px line. 11 and 14 are off the --s ladder and are literals scaled by
   --ui, the way .plate-head's own 18 and 9 already are. */
.aar-team-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-bottom:calc(14px * var(--ui));
border-bottom:1px solid var(--hair)}

.aar-team-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .meter{margin:calc(11px * var(--ui)) var(--s8) 0}

.aar-team-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .gate-line{margin:var(--s5) var(--s8) 0}

/* Keep the compact record legible and its history available on demand.
   The .plate-head-sub/-meta pair that sat here is now the component's own
   default -- every head on the site reads the way this page's did. */
.aar-record:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .decide-k,.aar-record:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .meta-ash{font-size:max(10px,var(--fs-2));color:var(--ink-3)}

.aar-record:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .record-hdr-mid{color:var(--gold)}

.aar-chronicle:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) summary{cursor:pointer;line-height:1.6}

.aar-chronicle:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) summary:hover{color:var(--ink)}

.chronicle-hint:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink-3);font-size:max(10px,var(--fs-3));margin-left:var(--s4)}

.chronicle-body:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){padding-top:var(--s6)}

.chronicle-value:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){white-space:pre-line;overflow-wrap:anywhere}

.record-dispatch:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){margin:0;white-space:pre-wrap;overflow-wrap:anywhere}

.crew-meta:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.crew-owner:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){overflow-wrap:anywhere;line-height:1.5}

.crew-card:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-meta,.crew-card:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-owner{color:var(--ink-3);font-size:max(11px,var(--fs-3))}

@media(max-width:600px){
.aar-head-row:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){gap:var(--s4);align-items:center}
.aar-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .aar-banner{flex:1;min-width:0;padding:var(--s3);gap:var(--s3)}
.aar-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .aar-banner>span{white-space:normal;font-size:11px;letter-spacing:.05em}
.aar-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .aar-worth{padding:0}
.aar-team:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .plate-head,.aar-evidence:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .plate-head{flex-wrap:wrap;gap:var(--s3) var(--s5)}
.aar-team:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .plate-head > .plate-head-meta{flex-basis:100%;margin-left:0}
.crew-identity:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:grid;grid-template-columns:auto minmax(0,1fr);align-items:start}
.crew-identity:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-state{grid-column:2;justify-self:start;padding:var(--s2) var(--s4)}
}

/* zoom-in is a promise, and a tile whose bytes were never fetched cannot keep
   it. .shot-slot is what a tile with no picture draws, so it is the test. */
.gallery:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .shot:has(.shot-slot){cursor:default}

/* TWO CHIPS IN ONE CORNER HAVE TO BE ONE HEIGHT. .mark grew to 6px/10px for
   the auspex verdict, which put a 28px pill beside the 19px number badge on
   every tile. Inside a tile it keeps the badge's own 2px/6px. */
.shot-tags:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .shot-mark{gap:var(--s2);padding:var(--s1) var(--s3)}

/* ---- the auspex verdict --------------------------------------------------
   5px/10px is the mockup's pill, and it draws NO halo: the lamp on .mark-lamp
   marks a machine at work, and a verdict that has already been reached is not
   one. The dot stays 6px, which is the mockup's exactly. */
.aar-auspex:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .shot-mark{padding:5px var(--s5);box-shadow:none}

.aar-auspex:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .shot-mark[data-verdict]::before{box-shadow:none}

/* ---- the ledger notes well -----------------------------------------------
   A fixed well the log sits on the FLOOR of and grows upward out of, so a
   report with two notes and a report with twenty are the same height and the
   newest line is always in the same place. --sink-2 is inset 0 2px 8px
   rgba(0,0,0,.6) against the mockup's .65, near enough not to mint a seventh
   shadow for. */
.notes-well:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){height:calc(188px * var(--ui));
border:1px solid rgba(var(--hue-1,178,201,177),.12);border-top-color:rgba(0,0,0,.5);
border-radius:var(--r1);background:rgba(6,11,9,.62);box-shadow:var(--sink-2);
padding:calc(13px * var(--ui)) calc(14px * var(--ui)) calc(14px * var(--ui))}

.log-line:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){line-height:1.65}

/* The composer's edge is a hairline here, not the app's gold field edge: the
   mockup keeps gold for the button beside it, and two golds in one row is the
   emphasis spent twice. */
.aar-notes:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .field{border-color:var(--hair);background:rgba(8,14,11,.6)}

.aar-notes:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .btn-outline{background:rgba(201,164,92,.12);color:var(--gold-warm);
letter-spacing:var(--ls-ctl)}

/* The held / barred sentences under the action row. Mono at 1.7, which is the
   mockup's gate line; the sheet's .lead is 1.45 and has no looser step. */
.gate-line:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){line-height:1.7;text-wrap:pretty}

/* ---- the brother card ----------------------------------------------------
   TWO UTILITIES WERE EATING THE COMPONENT. .dbg is the `background` SHORTHAND,
   so on `plate plate-bare crew-card dbg dbd` it reset background-image to none
   and background-color to {{ m.dbg }} — the literal string "transparent" for
   every man not yet credited. The cards had no fill at all. .dbd did the same
   to the border, painting the saturated status colour on all four sides where
   the mockup wants a hairline and a 3px inset bar down the left. Both are
   repaired at (0,2,0), which wins without reordering and without touching the
   markup, so {{ m.dbg }} and {{ m.bd }} keep their jobs — the tint becomes the
   underlayer the mockup actually draws it as, and the status colour becomes
   the accent rail it actually draws it as. */
.crew-card.dbg:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:linear-gradient(180deg,rgba(var(--hue-29,32,49,39),.85),rgba(var(--hue-80,15,25,20),.55)),var(--dbg)}

.crew-card.dbd:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:var(--line-3);border-top-color:rgba(var(--hue-8,210,225,210),.16);
box-shadow:inset 3px 0 0 var(--dbd),var(--lip),var(--cast-1)}

@media (max-width:1199px){
.desk-cards:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-column:1 / -1}
}

@media (max-width:799px){
.desk-cards:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){grid-column:1}
}

.desk-pane:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;min-width:0;border-radius:var(--r2);
  background:var(--panel-3);background-image:var(--face-1);
  border:1px solid var(--line-3);border-top-color:rgba(var(--hue-8,210,225,210),.14);
  box-shadow:var(--bevel),var(--cast-2)}

.desk-pane-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:center;gap:var(--s4);
  padding:var(--s5) var(--s5) var(--s3);border-bottom:1px solid var(--rule-head);
  font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-meta);
  text-transform:uppercase;color:var(--ink-5)}

.desk-cards:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column}

/* A CARD PER JOB, SHUT UNTIL YOU OPEN IT. Owner: "each one of those things
   would be a card that can be expanded / opened." */
.desk-card:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-bottom:1px solid var(--hair)}

.desk-card:last-child:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-bottom:none}

/* ROOM BETWEEN THE TWO THINGS ON THE ROW, AND AT THE TOP AND BOTTOM.
 * Owner: "please fix the padding on these windows... its bad." It was --s4 /
 * --s5 on a card carrying a title, a sentence of explanation and a count, so
 * the count sat flush to the border and the sentence ran into it. The SIDES
 * are the recess's now (--s7, from `.ledger :is(.cmp,.roll) .rail`, which is
 * (0,2,0) and beats the shorthand below); what is set here is the vertical
 * room and the gap before the count, and the count may never be squeezed by
 * a long note. */
.desk-card-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;align-items:flex-start;gap:var(--s6);cursor:pointer;
  padding:var(--s6) var(--s7);transition:background var(--t)}

.desk-card-head:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) > .mono{flex:none;padding-top:1px}

.desk-card-head:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(var(--hue-1,178,201,177),.05)}

.desk-fold:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:none;width:12px;text-align:center;font-family:var(--mono);
  font-size:var(--fs-4);color:var(--ink-5)}

.desk-card-open:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(201,164,92,.05)}

.desk-card-open:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .desk-fold{color:var(--gold)}

/* ========================== THE SETTINGS PANES ==========================
 * Owner: "can you make them both look a little better. Seems like a lot in
 * the settings page is a bit of an afterthought." Then, with a picture of the
 * Codex: "and i think it should all be laid out like this... this is
 * beautoful." And, of the version before this one: "please fix the padding on
 * these windows... its bad."
 *
 * SO THEY ARE CODEX CARDS, AND ALMOST NONE OF THAT IS WRITTEN HERE.
 * A Codex card is a `.plate-head` holding `.codex-n` + `.heading` +
 * `.plate-head-meta`, and a `.roll` under it. The sheet already draws every
 * part of that: `.ledger .plate-head` (ledger.css:589) is a CENTRED COLUMN,
 * gothic.css turns its `::after` into the line-✠-line cross rule, block 1
 * carves the heading in Cinzel with an illuminated initial, block 4 puts the
 * `[ ]` around the meta line, and `.ledger .roll` (ledger.css:726) is the
 * recess, with block 9's four brass rivets. The two panes had their own
 * flat-left head fighting all of that, and they were the only two heads on
 * the site that did.
 *
 * WHAT IS LEFT TO SAY IS THE THREE THINGS THE SHEET CANNOT KNOW: the panes
 * keep their own depth (owner, 2026-09-11: "it needs sepertion the middle and
 * the left both need tiehr own depth panels"), so the plate's transparent
 * ground is not wanted; the top edge turns gold and brackets at both ends,
 * which is what makes a plate read as a hanging plaque; and the roll needs a
 * margin, or the recess is flush to the pane's own border.
 *
 * AND THE PADDING IS NOW THE SHEET'S. `.ledger :is(.cmp,.roll) .rail`
 * (ledger.css:733) pads every rail inside a recess to --s7 a side. The card
 * head IS a `.rail`, so putting it in the roll settles the horizontal padding
 * at sixteen pixels, the same as every other row on the site -- which is what
 * the ten it used to carry was wrong about.
 * ===================================================================== */
.desk-pane:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){
  border-top-color:var(--gold-line)}

/* THE HANGING PLAQUE, from gothic.css block 3. The top hairline turns down at
   both ends, so the pane reads as a plate rather than a box. Its own copy
   because `.desk-pane` is not a `.plate`: it keeps the depth panel the owner
   asked for, and `.ledger .plate` would take that away. */
.desk-pane:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before,
.desk-pane:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{
  content:"";position:absolute;top:-1px;width:calc(10px*var(--ui));height:calc(10px*var(--ui));
  pointer-events:none;z-index:1;border-top:1px solid var(--gold)}
.desk-pane:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{left:-1px;border-left:1px solid var(--gold)}
.desk-pane:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{right:-1px;border-right:1px solid var(--gold)}

/* THE RECESS, OFF THE PANE'S OWN EDGE. Same margin the company roll takes
   (ledger.css:754), so the two read as the same object. */
.desk-pane:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .roll{
  margin:0 var(--s6) var(--s6)}

/* ONE HAIRLINE BETWEEN ROWS, and it is the recess's own. `--hair` is the
   colour for a line on the panel face; inside a roll every other row on the
   site is divided by `--edge-line`, and mixing the two is visible. */
.desk-pane:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .desk-card{
  border-bottom-color:var(--edge-line)}

/* `.perm-rung` STOOD HERE and went with the Site Permissions pane -- owner:
   "remove the site permissions as well since their not really needed". It was
   the only markup that ever wore the class. */
.desk-card-body:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){display:flex;flex-direction:column;gap:var(--s4);
  padding:0 var(--s7) var(--s6) calc(var(--s7) + var(--s6) + 12px)}

/* HIS POSITION, BESIDE HIS COMPANY. Gold and boxed when it is the reader's own
   squad, so "who is in my squad" is one glance rather than three lines of
   "awaiting". Owner, 2026-09-11. */
.crew-place:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){font-family:var(--mono);font-size:var(--fs-2);letter-spacing:var(--ls-1);
  color:var(--ink-4);white-space:nowrap}

.crew-place-mine:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-warm);border:1px solid var(--gold-line);border-radius:2px;
  padding:0 var(--s3);background:rgba(201,164,92,.10)}

.crew-group-mine:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .decide-k{color:var(--gold-warm)}

.crew-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .bg-bad{border-color:var(--warn);background:rgba(181,112,90,.3)}

.crew-lead:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;font-weight:600}

/* The lit top edge and the ring are what carry it at a glance; the fill alone
   reads as a hover on a dark ground. */
.crew-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-lead{box-shadow:inset 0 1px 0 rgba(255,255,255,.3),inset 0 -1px 0 rgba(0,0,0,.35),
  0 3px 0 rgb(var(--hue-84,7,16,11)),0 5px 7px rgba(0,0,0,.3),0 0 12px -5px var(--lead-ring);
  background-color:var(--lead-bg);border-color:var(--lead-bd);color:var(--lead-ink)}

/* Raised keys become recessed only after a recorded decision. */
.crew-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .btn.crew-pressed{
  transform:translateY(2px);background-image:linear-gradient(180deg,rgba(0,0,0,.35),rgba(0,0,0,.06) 60%,rgba(var(--hue-49,215,240,220),.04));
  box-shadow:inset 0 3px 6px rgba(0,0,0,.7),inset 1px 0 3px rgba(0,0,0,.3),0 1px 0 rgba(var(--hue-47,205,230,210),.18);
  border-top-color:rgba(0,0,0,.6)}

/* ACCEPT LEADING: the report is clean and he is owed his points. */
.crew-lead-good:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){--lead-bg:rgba(var(--hue-2,127,180,137),.32);--lead-bd:var(--good);
  --lead-ring:rgba(var(--hue-2,127,180,137),.45);--lead-ink:rgb(var(--hue-24,234,245,234))}

/* REFUSE LEADING: somebody has already said no, and the card should say so
   louder than the button that would undo it. */
.crew-lead-bad:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){--lead-bg:rgba(155,44,36,.42);--lead-bd:var(--bad-lit);
  --lead-ring:rgba(196,104,95,.45);--lead-ink:#F6E6E4}

/* ESCALATE LEADING: it is with the CO, and neither of the other two applies. */
.crew-lead-esc:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){--lead-bg:rgba(122,146,196,.30);--lead-bd:var(--esc-lit);
  --lead-ring:rgba(122,146,196,.45);--lead-ink:#E4EBF8}

.crew-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-lead-live{animation:crew-breathe 2.4s ease-in-out infinite}

.aar-accept-fx:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:relative;isolation:isolate}

.aar-accept-locked:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){cursor:default;pointer-events:none}

/* The original green plate stays. A contained power sweep charges its edges. */
.aar-accept-ready:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before,.aar-accept-ready:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{content:"";position:absolute;pointer-events:none}

.aar-accept-ready:not(.crew-lead):where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(var(--hue-2,127,180,137),.32);border-color:var(--good);color:rgb(var(--hue-24,234,245,234))}

.aar-accept-ready:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before{inset:-1px;border:1px solid transparent;
  background:linear-gradient(110deg,transparent 28%,rgba(var(--hue-18,124,239,169),.65) 44%,rgb(var(--hue-51,237,255,241)) 49%,rgba(var(--hue-18,124,239,169),.65) 54%,transparent 70%) border-box;
  background-size:320% 100%;padding:1px;
  mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:exclude;
  animation:seal-charge 3.6s cubic-bezier(.4,0,.2,1) infinite}

.aar-accept-ready:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{inset:1px;overflow:hidden;
  background:linear-gradient(110deg,transparent 34%,rgba(var(--hue-16,111,229,160),.07) 43%,rgba(var(--hue-46,201,255,220),.25) 49%,rgba(var(--hue-16,111,229,160),.07) 55%,transparent 64%);
  background-size:320% 100%;animation:seal-charge 3.6s cubic-bezier(.4,0,.2,1) infinite}

.approval-veil:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){position:fixed;inset:0;z-index:65;display:grid;place-items:center;padding:24px;background:rgba(var(--hue-81,4,12,8),.84)}

.approval-dialog:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){width:min(100%,480px);padding:24px;border:1px solid var(--gold);background:rgb(var(--hue-53,19,35,27));box-shadow:0 20px 80px #000}

@media (prefers-reduced-motion:reduce){
.aar-accept-ready:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::before,.aar-accept-ready:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))::after{display:none}
}

/* A guttering candle is still a lit candle: the movement goes, the light stays
   -- the same rule the sheet applies to every other animation it carries. */
@media (prefers-reduced-motion:reduce){
.crew-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .crew-lead-live{animation:none}
}

/* THE TWO THAT ARE NOT LEADING still have to be legible: the quiet base is a
   hairline on nothing, which beside a lit button reads as disabled. */
.crew-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .btn:not(.crew-lead){background-color:rgba(var(--hue-1,178,201,177),.05)}

.crew-acts:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .btn:not(.crew-lead):hover{background-color:rgba(var(--hue-1,178,201,177),.12);
  border-color:var(--tile-edge)}

/* An unclaimed handle's mark. It was an EMPTY .mark — a 24x15 orange-bordered
   rectangle with a halo and nothing in it, because .mark sizes itself off a
   label and this one has none. The mockup's state markers are all small round
   dots, so it becomes one. */
.pip-warn:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--warn-2);border-color:var(--warn-2)}

/* ==========================================================================
HOVER — was 16 distinct style-hover attributes on 42 elements.
========================================================================== */
.h-ink:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--ink)}

.h-warm:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--gold-warm)}

.h-bad:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--bad-lit)}

.h-bad-line:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){color:var(--bad-lit);border-color:var(--bad)}

.h-gold-line:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-color:rgba(201,164,92,.5);color:var(--gold-warm)}

/* FIVE HOVERS THAT DIFFERED ONLY IN THE ALPHA OF ONE GREEN -- .7, .6, .5, .8,
   .6 -- across eighteen elements. Nobody chose that; it accreted, one element
   at a time, each new one guessing at a value near the last.
   
   There is one lamp in this room now and it is WARM, because the reader's own
   attention is the light and light has a source. Green-on-green said "the
   surface changed"; brass says "you are here". Two shapes, not five values:
   a lift, and a lift with a rail down the edge for a row in a list.
   
   The names are kept as they are so no markup moves; they are aliases of one
   behaviour rather than five behaviours with one look. --lift and --lift-hard
   are the only two values, and .rail-act:hover uses the same --lift, so a row
   in a list and a tile in a grid answer the hand identically. */
.h-lift-soft:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--lift)}

.h-bad-fill:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--bad);color:var(--ink)}

.h-gold-fill:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--gold);color:var(--ink-on-gold)}

.h-good-fill:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:rgba(var(--hue-5,121,196,141),.16)}

.h-ink-fill:hover:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){background:var(--ink)}

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

/* ==========================================================================
THE GATE CLOSING

Each piece of the boot panel replays ITS OWN ENTRANCE BACKWARDS -- the same
four keyframes with direction:reverse, not four new ones written to look
like the opposite. Two sets would drift the first time one was tweaked; one
set cannot.

The !important is doing real work rather than papering over specificity:
every one of these elements carries its entrance in an inline style
attribute (they are one-off, screen-specific motion), and an inline
animation is otherwise unbeatable by a stylesheet.

Delays run outward from the middle: the mark goes first, the rule and title
follow, the lines last, and the panel's own fade starts at 280ms over the
top of all of it.
========================================================================== */
.boot-leaving:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .boot-mark{animation:bootmark .34s cubic-bezier(.5,0,.75,0) reverse both!important}

.boot-leaving:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .boot-line{animation:bootline .3s .06s cubic-bezier(.5,0,.75,0) reverse both!important}

.boot-leaving:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .boot-title{animation:track .34s .04s cubic-bezier(.5,0,.75,0) reverse both!important}

.boot-leaving:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .boot-row{animation:tickin .26s .1s ease-in reverse both!important}

@media (prefers-reduced-motion:reduce){
.boot-leaving:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .boot-mark,.boot-leaving:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .boot-line,.boot-leaving:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .boot-title,.boot-leaving:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)) .boot-row{animation:none!important}
}

/*
 * AND EVERY ANIMATION WRITTEN IN THE MARKUP, which is most of them.
 *
 * An inline `style="animation:…"` beats a stylesheet rule unless the rule
 * carries !important -- the block above already knows this, and says so, and
 * then the lesson is applied nowhere else in the file. So a reader who asked
 * for no motion still got the boot mark scaling in, the reticle pulsing, the
 * rule drawing itself, the title's tracking collapsing, the rows ticking in,
 * the loading hairline, the sign-in scanline, the button's glow, the nav badge,
 * the spinner and the reader panel's shimmer. FORTY-FOUR inline animations, and
 * the one rule that existed killed only the boot gate's EXIT -- so they saw the
 * whole entrance and then a hard cut. The asymmetry, backwards.
 *
 * `--danim` is not the string "animation:", so the two hooks have to be named
 * separately. Nothing in the app waits on animationend, so cancelling them
 * cannot strand a state: leaveBoot() runs on a plain 720ms timer and says so.
 */
@media (prefers-reduced-motion:reduce){
.danim:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none!important}
/* The title's tracking is the END STATE of `track`, not a declaration --
   nothing in the sheet gives .boot-title a letter-spacing -- so cancelling the
   animation would leave CALIBAN LEDGER at default. Declared here it also fixes
   the snap-back the .boot-leaving rule above already causes. */
.boot-title:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){letter-spacing:.22em}
/* Stops the movement and keeps the light, which is the candles' own rule. */
.scanning:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.scan-glyph-reading:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)),.tally-scan:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none}
}

/* ==========================================================================
THE PHONE

Owner: "the mobile view is a little fucked... its missing the bottom nav it
used to have. and the headers and such are ugly."

Measured at 390px before touching anything, because two of the three were
worth checking rather than assuming:

- THE SIZE IS NOT THE PROBLEM. --ui is 1 below 1000px, so the desktop 10%
never reached a phone. Left alone.
- THE BOTTOM NAV IS THERE and correctly pinned: six items, 62px tall,
sitting at 782-844 in an 844 viewport. Nothing to restore.
- THE HEADERS REALLY ARE UGLY, and the measurement says why. On the
reports screen the tool bar is 135px TALL and STILL 555px wide inside a
381px column: a search box, a five-chip group 370px wide and another
539px wide. flex-wrap cannot fix that -- one group is wider than the
phone on its own -- so wrapping only built a three-storey tower that
overflowed anyway.

A tool bar on a phone is therefore ONE SCROLLING LINE, which is what every
other application does with a filter strip and what the wrap was failing to
be. The spacer goes: it exists to push the count to the far right of a wide
bar, and inside a scrolling line it is 255px of nothing.
========================================================================== */
@media (max-width:999px){
/* THE ONE BAR THAT MUST NOT SCROLL. A .pop is positioned against its control
   and an overflow:auto parent clips it, so a filter menu opened inside a
   scrolling line would be cut off at the bar's own edge. It no longer needs
   the scroll anyway -- that rule exists because seventeen chips could not fit
   on a phone, and four controls wrap onto two comfortable rows. */
.bar-filt:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex-wrap:wrap;overflow:visible}
.bar-filt:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *))>.spacer{display:none}
.search:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:150px}
/* A column that fills the screen has nothing to its right to be divided
from, and its 1px edge was the whole of a 5px overflow on statistics. */
.hair-r:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){border-right:none}
/* The band of big numbers is two-up on a phone rather than four-across at
130px each, which is narrower than the numbers it carries. */
.total:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){min-width:44%}
}

@media (prefers-reduced-motion:reduce){
.ember:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none!important;opacity:0;filter:none}
}

/* ==========================================================================
   § 4 · MOTION — the 29 @keyframes the new sheet lacks.

   The inline block defines 32 (a 33rd match is the words "@keyframes name"
   inside a comment, not a rule). ledger.css defines three of them — enter,
   flicker and shimmer — and those three are NOT carried: the redesign gets to
   redefine its own motion, and a duplicate here would win on source order and
   quietly overrule it.

   TWO OF THE THREE ARE NOT THE SAME ANIMATION, and a reader should know which
   way that cuts. `shimmer` costs nothing — the old one slides a highlight
   (translateX -140% → 340%), the new one slides a background-position, and no
   carried rule names it. `enter` is a fade-and-rise in both; the new one
   rises 6px instead of 9px, so .anim-enter, .drawer and .forge arrive very
   slightly shallower. The skull's fire now uses a canvas field and does not
   depend on either sheet's flicker keyframes.
   ========================================================================== */

@keyframes scanrow{0%,100%{background:rgba(122,146,196,.05)}50%{background:rgba(122,146,196,.14)}}

@keyframes scanmark{0%,100%{opacity:.4}50%{opacity:1}}

@keyframes crackout{
  0%   {opacity:1;   transform:translate(-50%,-50%) scale(.12)}
  100% {opacity:0;   transform:translate(-50%,-50%) scale(1)}}

@keyframes bloomout{0%{opacity:1}100%{opacity:0}}

@keyframes rattle{
  0%   {transform:translate(0,0)}
  12%  {transform:translate(-6px,3px)}
  26%  {transform:translate(5px,-4px)}
  42%  {transform:translate(-4px,-2px)}
  58%  {transform:translate(3px,3px)}
  74%  {transform:translate(-2px,-1px)}
  88%  {transform:translate(1px,1px)}
  100% {transform:translate(0,0)}}

@keyframes shatterfade{
  0%   {opacity:0}
  4%   {opacity:.92}
  34%  {opacity:.66}
  100% {opacity:0}}

@keyframes smokeup{
  0%   {transform:translate(0,0) scale(.45);opacity:.55}
  100% {transform:translate(-6px,-30px) scale(2.1);opacity:0}}

@keyframes skullbeam{0%{top:0;opacity:0}12%{opacity:1}88%{opacity:1}100%{top:100%;opacity:0}}

@keyframes quotein{
  0%{opacity:0;filter:blur(6px);transform:translateY(10px)}
  100%{opacity:1;filter:blur(0);transform:translateY(0)}
}

@keyframes quoteout{
  0%{opacity:1;filter:blur(0);transform:translateY(0)}
  100%{opacity:0;filter:blur(6px);transform:translateY(-14px)}
}

/*
 * AND THE RECOMMENDED ONE BREATHES. Owner, 2026-09-04: "the buttons that
 * recomend the sgt judgment needs to shimmer green on the accepts or red on the
 * refuse."
 *
 * ONLY WHEN IT IS A RECOMMENDATION, which is the distinction the animation has
 * to carry: a card that is already settled lights its button to say SO, and a
 * settled state that pulsed would read as asking to be pressed again. So the
 * shimmer rides .crew-lead-live, set only where nothing has been decided yet
 * and the auspex has an opinion -- see the bindings.
 *
 * Brightness carries the pulse so the raised key keeps its bevel and shadow.
 * Settled controls keep their colour and sit recessed without pulsing.
 */
@keyframes crew-breathe{
  0%,100%{filter:brightness(1)}
  50%{filter:brightness(1.17)}
}

@keyframes seal-charge{0%,18%{background-position:110% 0;opacity:0}24%{opacity:1}
  70%{background-position:-10% 0;opacity:1}82%,100%{background-position:-10% 0;opacity:0}}

@keyframes led{0%,100%{opacity:.28}50%{opacity:1}}

@keyframes scanv{0%{transform:translateY(-12%)}100%{transform:translateY(1100%)}}

@keyframes rise{0%{opacity:0;transform:translateY(6px)}100%{opacity:1;transform:translateY(0)}}

@keyframes glow{
  0%,100%{box-shadow:inset 0 1px 0 rgba(255,236,180,.55),inset 0 0 10px -3px rgba(255,236,180,.28)}
  50%    {box-shadow:inset 0 1px 0 rgba(255,236,180,.55),inset 0 0 18px 0 rgba(255,236,180,.66)}}

@keyframes fadein{0%{opacity:0}100%{opacity:1}}

@keyframes spin{100%{transform:rotate(360deg)}}

@keyframes bootmark{0%{opacity:0;transform:scale(.55) rotate(-10deg)}70%{opacity:1;transform:scale(1.04) rotate(0)}100%{opacity:1;transform:scale(1)}}

@keyframes bootline{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}

@keyframes bootout{0%{opacity:1;transform:scale(1);filter:blur(0)}100%{opacity:0;transform:scale(1.045);filter:blur(3px);visibility:hidden}}

@keyframes track{0%{letter-spacing:.6em;opacity:0}100%{letter-spacing:var(--ls-brand);opacity:1}}

@keyframes tickin{0%{opacity:0;transform:translateY(-7px)}100%{opacity:1;transform:translateY(0)}}

@keyframes glowtext{0%,100%{text-shadow:none}50%{text-shadow:0 0 16px rgba(var(--hue-4,59,200,178),.55)}}

@keyframes hairline{0%{transform:translateX(-100%)}100%{transform:translateX(400%)}}

@keyframes reticle{0%,100%{opacity:.35}50%{opacity:1}}

@keyframes emberrise{0%{transform:translate3d(0,0,0);opacity:0}4%{opacity:var(--peak,.6)}50%{transform:translate3d(var(--dx,12px),-47vh,0)}66%{opacity:var(--peak,.6)}86%{opacity:0}100%{transform:translate3d(0,-94vh,0);opacity:0}}

@keyframes emberglow{0%,100%{filter:brightness(.74)}50%{filter:brightness(1.16)}}

@keyframes flame{0%,100%{transform:scaleY(1) scaleX(1);opacity:.92}30%{transform:scaleY(1.14) scaleX(.92);opacity:1}55%{transform:scaleY(.9) scaleX(1.06);opacity:.8}78%{transform:scaleY(1.06) scaleX(.97);opacity:.97}}

/* THE REDUCED-MOTION GUARD, which is not a class rule and is carried anyway.
   `[style*="animation:"]` cancels animations written into a style attribute —
   the boot sequence and the embers set theirs from JS, so a class-based
   override cannot reach them. ledger.css has no equivalent. Dropping it would
   not unstyle anything; it would just quietly stop honouring a preference,
   which is worse, because nothing would look wrong. */
/* AND IT HAD FALLEN OUT OF THE MEDIA QUERY IT BELONGS TO, which is the whole
   of why the owner said "i want the embers back". At top level it did not
   honour a preference, it enforced one: `animation:none!important` on every
   element inside `.ledger` carrying an inline animation, for every reader,
   always. Twenty-two embers were in the page the entire time with their
   animations cancelled, and the boot sequence with them. Silent in exactly the
   way the rest of this cutover has been silent -- a thing that does not move
   looks the same as a thing that was never there -- so the wrapper is written
   explicitly around it here rather than left to whichever block precedes it. */
@media (prefers-reduced-motion:reduce){
  [style*="animation:"]:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){animation:none!important}
}

/* The archive accepts dispatches longer than a single thought.  Keep its
   expanding textarea inside the compact fixed panel, with the transmit key
   anchored to its lower edge as the field grows. */
.skull-chat .skull-chat-compose{align-items:flex-end}
.skull-chat textarea.skull-chat-input{resize:vertical;min-height:58px;max-height:180px;line-height:1.5}

/* ============================================================================
   4. ledger-extra.css
   ========================================================================== */
/* ==========================================================================
   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(var(--hue-34,63,140,102),.10) 0%,rgba(var(--hue-30,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(var(--hue-37,84,168,126),.062) 0%,rgba(var(--hue-31,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}
}
/* THE NAME KEY ON A CREW CARD IS A PILL, NOT AN EGG. Owner, 2026-09-27, of the
   Reports screen: "the padding there is terrible". `.btn-sm` gives it 8px a
   side and `.btn-thumb` raises it to a finger's 44px, and a 44px-tall pill
   with 8px sides draws as an oval around its words. */
.ledger .crew-id .btn-thumb{padding-left:var(--s8);padding-right:var(--s8)}

/* THE NUMBER BESIDE A NEEDED PICTURE IS AS WIDE AS A NUMBER, ON A PHONE TOO.
   `.kv-need` states its own columns, but at (0,1,0) it lost to the phone's
   `.ledger .kv` (0,2,0), which gives every key an 88px column -- so "1" sat
   alone and "Victory screen" and its sentence were squeezed into the right
   half of the row. Owner, 2026-09-27: "when its adking you questions the
   padding there is terrible". */
@container ledger (max-width:720px){
  .ledger .kv.kv-need{grid-template-columns:calc(24px*var(--ui)) minmax(0,1fr)}
}

/* WHAT IS NOT A LIST GETS THE LIST'S GUTTER. A ledger plate has no side
   padding of its own -- a .roll puts it back for every .rail inside -- so in
   the walk everything that is not a list sat on the plate's edge: each stats
   card's name line, the points so far (its big numeral clipped by the frame),
   and the sentence under each step. Owner, 2026-09-27, with the review step:
   "please fix these padding issues". The same inset a rail's words have. */
.ledger.filing .plate-body{padding-bottom:var(--s7)}
.ledger.filing .plate-body > :is(.filing-card,.auspex-total,.meta){padding-left:var(--s7);padding-right:var(--s7)}
@container ledger (max-width:720px){
  .ledger.filing .plate-body > :is(.filing-card,.auspex-total,.meta){padding-left:var(--s5);padding-right:var(--s5)}
}
/* QUESTS AND OATHS (core/quests.ts). The relic reads in the display face,
   the deed under it in the body face; a row is the relic on the left and
   what he may do about it on the right. The loose blocks take a rail's
   gutter, as everything in a ledger plate that is not a list must. */
.ledger .quest-relic{font-family:var(--display);font-size:var(--fs-5);color:var(--ink);letter-spacing:.02em}
.ledger .quest-task{color:var(--ink-2);font-size:var(--fs-3);line-height:1.45}
.ledger .quest-row{grid-template-columns:calc(48px*var(--ui)) minmax(0,1fr) auto;align-items:center;column-gap:var(--s6)}
.ledger .quest-art{display:block;width:calc(48px*var(--ui));height:auto;aspect-ratio:145/207;object-fit:cover;border:1px solid var(--gold-line);border-radius:var(--r1,2px);background:rgba(0,0,0,.4)}
.ledger .quest-art-lg{width:calc(96px*var(--ui))}
.ledger .quest-art-slot{display:block;min-height:1px}
.ledger .quest-row .roll-name{white-space:normal}
.ledger .quest-row.quest-held{opacity:.72}
.ledger .quest-row.quest-sworn{box-shadow:inset 3px 0 0 var(--gold)}
.ledger .quest-oath .plate-body,.ledger .quest-build .plate-body{padding-left:var(--s7);padding-right:var(--s7);padding-bottom:var(--s7)}
.ledger .quest-steps .label{min-width:calc(16px*var(--ui))}
.ledger .quest-checks{font-variant-numeric:tabular-nums}
.ledger .quest-build-fields{grid-template-columns:repeat(auto-fill,minmax(calc(180px*var(--ui)),1fr));gap:var(--s5)}
.ledger .quest-build-fields .quest-wide{grid-column:1/-1}
.ledger .quest-offer{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:var(--s6);
  margin:0 0 var(--s6);padding:var(--s6) var(--s7);border:1px solid var(--gold-line);border-radius:var(--r2);background:rgba(0,0,0,.28)}
.ledger .oath-due{grid-template-columns:calc(48px*var(--ui)) minmax(0,1fr) minmax(calc(200px*var(--ui)),auto);align-items:start;column-gap:var(--s7)}
.ledger .oath-acts .field{min-width:calc(200px*var(--ui))}
@container ledger (max-width:720px){
  .ledger .quest-row{grid-template-columns:calc(48px*var(--ui)) minmax(0,1fr)}
  .ledger .quest-row > .row.end{grid-column:2;justify-content:flex-start}
  .ledger .oath-due{grid-template-columns:calc(48px*var(--ui)) minmax(0,1fr)}
  .ledger .oath-due > .oath-acts{grid-column:1/-1}
  .ledger .quest-oath .plate-body,.ledger .quest-build .plate-body{padding-left:var(--s5);padding-right:var(--s5)}
  .ledger .quest-offer{padding:var(--s5)}
}

/* AND THE SEAL STAYS OUT FROM UNDER THE SKULL. The walk's own bar runs the
   full width, and where the console stands (the same widths and heights that
   show it, above) the machine, the skull and his toggle drew over ACCEPT AS
   IS -- the one key the walk exists to reach. The bar stops short of them. */
@media (min-width:1000px) and (min-height:700px){
  .ledger.filing .filing-foot{padding-right:calc(var(--con-r) + var(--con-w) + var(--s7))}
}

/* ==========================================================================
   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(var(--hue-90,9,19,15),.30) 8%,
    rgba(var(--hue-90,9,19,15),.26) 88%,
    rgba(4,10,8,.42) 100%);
  border:1px solid rgba(var(--hue-9,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(var(--hue-10,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(var(--hue-9,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(var(--hue-10,226,240,230),.075),rgba(var(--hue-10,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(var(--hue-9,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.

   AND THIS IS THE ONE RULE THAT DECIDES IT. Owner, three times: "nake this
   wider please...", "PLEASE MAKE THIS WIDER!", "why do you refuse to make
   this wider???" -- twice over I widened `.muster-roll` in ledger-carry.css,
   shipped it, and it did nothing, because THIS line is in a sheet loaded
   after that one at the same specificity and wins on order. The carry sheet
   now says so where the old value was, so the next person to look does not
   lose the same two rounds.

   MEASURED, NOT GUESSED, at the end of it: 26% of a 1,224px split is 318px,
   floored back to the 330 it has always been -- so on any display under about
   1,300px the share never bound and the floor was the whole rule. That is why
   nothing he could see ever changed. 34% and a 420 floor put the second row
   on ONE line at every width the sheet is used at, which is what he has been
   asking for: "14 places 745 aars 3,819 pts" and the DISCORD lamp beside the
   tally instead of stacked under it.
   ========================================================================== */
.muster-roll:where(.ledger,.ledger *):not(:where(.oldskool,.oldskool *)){flex:0 1 clamp(calc(420px * var(--ui)), 34%, calc(640px * 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}
}

/* ============================================================================
   5. gothic.css -- the Dark Angels layer
   ========================================================================== */
/* ==========================================================================
   gothic.css — the Dark Angels layer. Load AFTER ledger.css.
   Nine small blocks; each stands alone — delete any you don't want.
   Needs one extra font: Cinzel 600 (Google Fonts) for block 1.
   ========================================================================== */

/* 1 · INSCRIBED TYPE — carved capitals for the words that name things; an illuminated initial */
.ledger{--display:'Cinzel','Cormorant Garamond',Georgia,serif;isolation:isolate}
.ledger .heading,.ledger .wordmark{font-family:var(--display);font-weight:600;letter-spacing:var(--ls-3)}
.ledger .heading::first-letter{color:var(--gold-warm);font-size:1.15em}

/* 2 · THE CROSS RULE — every section's underline becomes line · ✠ · line */
.ledger .plate-head::after{content:"\2720";width:calc(120px*var(--ui));height:auto;margin-top:var(--s2);
  font-family:var(--serif);font-size:var(--fs-4);line-height:1;color:var(--gold);text-align:center;box-shadow:none;text-shadow:0 0 10px var(--glow-live);
  background:linear-gradient(90deg,transparent,var(--gold-line) 18%,var(--gold) 40%,transparent 41%,transparent 59%,var(--gold) 60%,var(--gold-line) 82%,transparent);
  background-size:100% 1px;background-position:center;background-repeat:no-repeat}
.ledger :is(.a-head,.r-head,.s-head) .plate-head::after{display:none}

/* 3 · BRACKETED PLATES — a hanging plaque: the top hairline turns down at both ends */
.ledger .plate::before,.ledger .plate::after{content:"";position:absolute;inset:auto;top:-1px;width:calc(10px*var(--ui));height:calc(10px*var(--ui));
  background:none;pointer-events:none;z-index:1;border:0;border-top:1px solid var(--gold)}
.ledger .plate::before{left:0;border-left:1px solid var(--gold)}
.ledger .plate::after{right:0;border-right:1px solid var(--gold)}
.ledger .crew-card::before,.ledger .crew-card::after{border:0}

/* 4 · READOUTS IN BRACKETS — the meta line reads like a stencilled plate */
/* THE FIRETEAM'S CREDIT RING. The arc fills as each man is paid, in the
   chapter's own gold and glow; a full ring breathes, unless motion is asked
   not to. */
.ledger .credit-ring{position:relative;display:grid;place-items:center;width:calc(86px*var(--ui));height:calc(86px*var(--ui));border-radius:50%;
  background:radial-gradient(circle at 50% 50%,rgba(0,0,0,.6) 0 56%,var(--glow-live) 64%,transparent 72%);box-shadow:0 0 26px -10px var(--glow-live)}
.ledger .credit-ring-svg{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg);overflow:visible}
.ledger .credit-ring-track{fill:none;stroke:var(--gold-line);stroke-width:3.5}
.ledger .credit-ring-arc{fill:none;stroke:var(--gold-lit);stroke-width:4;stroke-linecap:butt;
  filter:drop-shadow(0 0 4px var(--glow-live)) drop-shadow(0 0 12px var(--glow-live)) drop-shadow(0 0 22px var(--glow-live));transition:stroke-dasharray .7s cubic-bezier(.2,.7,.2,1)}
.ledger .credit-ring.is-none .credit-ring-arc{opacity:0}
.ledger .credit-ring.is-none{background:radial-gradient(circle at 50% 50%,rgba(0,0,0,.6) 0 56%,transparent 64%);box-shadow:none}
.ledger .credit-ring.is-full .credit-ring-arc{stroke:var(--gold-warm)}
.ledger .credit-ring-n{position:relative;font-family:var(--serif);font-size:calc(30px*var(--ui));font-weight:600;line-height:1;color:var(--gold-warm);
  text-shadow:0 0 14px var(--glow-live);font-variant-numeric:lining-nums tabular-nums}
.ledger .credit-ring.is-none .credit-ring-n{color:var(--ink-3);text-shadow:none}
.ledger .credit-ring-of{font-size:.58em;color:var(--gold-ash);margin-left:1px;font-weight:400}
.ledger .credit-ring-word{font-family:var(--mono);font-size:max(10px,var(--fs-3));letter-spacing:var(--ls-meta);color:var(--ink-3);text-transform:uppercase}
@media (prefers-reduced-motion:no-preference){
  .ledger .credit-ring.is-full{animation:credit-ring-breathe 3.4s ease-in-out infinite}
}
@keyframes credit-ring-breathe{50%{box-shadow:0 0 30px -4px var(--glow-live)}}
.ledger .plate-head-meta::before{content:"[ ";color:var(--gold-ash)}
.ledger .plate-head-meta::after{content:" ]";color:var(--gold-ash)}

/* 5 · THE CHAPTER WATERMARK — the winged sword, ghosted behind the head of every page
   TWO CHANGES FROM THE DROP, BOTH THE OWNER'S: "make this graphic dark angel
   logo static in the background. I like it but more faint."
   · `fixed`, not `absolute`. On a handoff page `.ledger` IS the document and
     the sword scrolls away with it. Here the app scrolls an inner pane, so
     absolute would have pinned it near enough by accident -- fixed says it.
   · .045 -> .026. Faint enough to be a watermark rather than a picture. */
.ledger::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:url(assets/chapter-sigil.png) center calc(64px*var(--ui))/min(52vw,calc(560px*var(--ui))) no-repeat;opacity:.026}

/* 6 · GILT EDGE — a gold line and its shadow under the nav and the filing bar */
.ledger .topbar,.ledger .filing-bar{border-bottom:1px solid var(--gold-dim);box-shadow:0 2px 0 rgba(0,0,0,.6),0 3px 0 rgba(201,164,92,.16)}
.ledger .navtab-on{color:var(--gold-warm);text-shadow:0 0 10px var(--glow-live)}

/* 7 · THE WAX SEAL — a credited man carries the chapter's seal, pressed at an angle */
.ledger .crew-credited .crew-state .mark{width:calc(56px*var(--ui));height:calc(56px*var(--ui));justify-content:center;padding:0;border-radius:50%;transform:rotate(-8deg);
  border:2px solid #6A4A12;font-size:calc(7px*var(--ui));letter-spacing:.08em;color:var(--ink-on-gold);text-shadow:none;
  background:radial-gradient(circle at 35% 30%,#F3DA9A,#C9A45C 45%,#8A6A32 82%,#6A4A12);
  box-shadow:0 4px 10px rgba(0,0,0,.65),inset 0 0 0 4px rgba(255,236,180,.28),inset 0 0 0 5px rgba(90,60,10,.55)}
.ledger .crew-credited .crew-state .mark-lamp::before{display:none}

/* 8 · CANDLELIT GLOOM — the edges of the page fall into shadow */
.ledger::after{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(ellipse 85% 70% at 50% 28%,transparent 50%,rgba(0,0,0,.42))}

/* 9 · BRASS RIVETS — four studs on the corners of every recess */
.ledger .roll,.ledger .tiles-hero{position:relative}
.ledger .roll::before,.ledger .tiles-hero::before{content:"";position:absolute;inset:4px;pointer-events:none;z-index:2;
  background:
    radial-gradient(circle at 0 0,rgba(222,206,164,.6) 0 1.3px,rgba(0,0,0,.6) 1.3px 2.3px,transparent 2.5px),
    radial-gradient(circle at 100% 0,rgba(222,206,164,.6) 0 1.3px,rgba(0,0,0,.6) 1.3px 2.3px,transparent 2.5px),
    radial-gradient(circle at 0 100%,rgba(222,206,164,.6) 0 1.3px,rgba(0,0,0,.6) 1.3px 2.3px,transparent 2.5px),
    radial-gradient(circle at 100% 100%,rgba(222,206,164,.6) 0 1.3px,rgba(0,0,0,.6) 1.3px 2.3px,transparent 2.5px)}

/* ============================================================================
   6. THE CHAPTER'S PALETTE, filled in by core/hosted.ts when it serves this file
   ========================================================================== */
/* Caliban clarity: green-black armour, warm gold, and an amber forge.
   The palette is assembled inside site.css. Explicit root selectors also
   cover the shell and skull outside .ledger, ahead of legacy rules. */
html:root,.ledger{
  --bg:#010403;--bg-deep:#000;--row-alt:rgba(205,230,212,.035);--pop:#050A07;
  --panel:rgba(3,8,5,.56);--panel-2:rgba(8,14,10,.64);--panel-3:rgba(0,5,2,.5);--panel-4:rgba(11,18,13,.7);
  --ink:#F5F6EE;--ink-cool:#EAEFE8;--ink-2:#DCE5DD;--ink-3:#BDCBC0;
  --ink-4:#A6B5A9;--ink-5:#87998B;--ink-6:#66796B;--ink-on-gold:#080A05;
  --gold:#D4AF62;--gold-lit:#EEC976;--gold-warm:#FFE5A5;
  --gold-dim:#98783A;--gold-ash:#CDB882;--gold-line:rgba(212,175,98,.56);
  --rule-head:rgba(212,175,98,.58);--raise-line:rgba(212,175,98,.42);
  --recess-bg:linear-gradient(180deg,rgba(0,0,0,.08),rgba(0,0,0,.2));
  --tile:linear-gradient(150deg,rgba(20,30,23,.56),rgba(0,4,2,.4) 65%);
  --lift:rgba(238,201,118,.16);--glow-live:rgba(238,190,85,.62);--glow-read:rgba(151,224,175,.38);
  --glow:0 0 64px -14px rgba(238,190,85,.6);--glow-sm:0 0 28px -6px rgba(238,201,118,.58);
  --chat-ground:#020604;--chat-field:#010302;
  --chat-them:rgba(9,19,12,.86);--chat-them-soft:rgba(7,16,10,.8);
  --chat-them-lit:rgba(126,210,153,.15);--chat-them-line:rgba(126,210,153,.48);
  --chat-glow:rgba(126,210,153,.24);--chat-focus:rgba(126,210,153,.22);
}
html:root,html body,html .shell,.ledger{background:#010403;background-image:none;text-shadow:none}
html .shell .phosphor,html .shell .forge-glass{display:none}
html .shell .bezel{box-shadow:none}
.ledger::after{content:none}

/* Keep the existing forge's independent sway, drift and flare behind all
   lettering, including the sign-in controls rendered before the ember field. */
html .ember-field{
  z-index:-1;
  background:radial-gradient(ellipse 100% 90% at 50% 25%,
    rgba(94,193,133,.09),rgba(52,130,85,.065) 60%,rgba(28,70,44,.035));
}
html .ember-field::after{content:none;animation:none}
html .ember-field::before{
  content:"";position:absolute;left:-20%;right:-20%;bottom:-12%;top:auto;height:82%;
  background:radial-gradient(ellipse 58% 96% at 50% 100%,
    rgba(255,180,65,.4) 0%,rgba(238,139,36,.18) 38%,transparent 73%);
  pointer-events:none;animation:forge-breathe 41s ease-in-out infinite;
}
html .ember-field > .forge-wander::before{
  background:radial-gradient(ellipse 34% 40% at 50% 72%,
    rgba(255,201,115,.72) 0%,rgba(248,141,42,.32) 34%,rgba(206,100,20,.09) 60%,transparent 80%);
}
@media (prefers-reduced-motion:reduce){
  html .ember-field::before{animation:none;opacity:.8;transform:none}
}

/* Shallow translucent recesses keep nested report rolls from blocking the
   fire. Text itself stays opaque. */
.ledger :is(.plate,.crew-card,.honour,.post,.tiles-hero){
  background:linear-gradient(180deg,rgba(17,26,20,.8) 0,rgba(3,9,5,.62) 48px,rgba(0,4,2,.5) 140px);
  box-shadow:inset 0 1px 0 rgba(255,226,163,.24),inset 0 0 0 1px rgba(212,175,98,.18),0 12px 26px -12px #000;
}
.ledger .roll{background:var(--recess-bg);--ink-2:#F1F4EA;--ink-3:#E4EBDF;--ink-4:#D7E0D3;--ink-5:#C4CFBF}
.ledger .plate{border-top-color:rgba(238,201,118,.72)}
.ledger .plate::before,.ledger .plate::after{filter:drop-shadow(0 0 5px rgba(238,201,118,.7))}
.ledger :is(.topbar,.bar,.glass,.filing-bar,.filing-foot){
  background:linear-gradient(180deg,rgba(15,25,18,.88),rgba(0,4,2,.72));backdrop-filter:none;-webkit-backdrop-filter:none;
}
.ledger :is(.topbar,.filing-bar){box-shadow:inset 0 -1px 0 rgba(238,201,118,.5),0 4px 24px -9px rgba(238,190,85,.5)}
.ledger .brand .sigil{filter:drop-shadow(0 0 8px rgba(151,224,175,.4))}
.ledger :is(.field,.field-n){background:#010403;border-color:#586043}
.ledger :is(.field,.field-n):focus{border-color:var(--gold-lit);box-shadow:0 0 18px -6px var(--glow-live)}
.ledger :is(.heading,.navtab-on,.crew-name,.worth-n,.pts,.tile-n,.hero-n){text-shadow:none}
.ledger .crew-mine .crew-name{text-shadow:none}
.ledger .heading{color:var(--gold-lit);text-shadow:0 0 18px rgba(238,201,118,.4)}
.ledger :is(.worth-n,.tile-hot .tile-n,.hero-n){text-shadow:0 0 24px rgba(238,201,118,.42)}
.ledger :is(.navtab-on,.subtab-on){
  color:var(--gold-warm);border-bottom-color:var(--gold-lit);background:linear-gradient(0deg,rgba(238,201,118,.14),transparent);
  text-shadow:0 0 14px rgba(238,201,118,.4);box-shadow:0 8px 16px -10px rgba(238,201,118,.8);
}
.ledger .plate-lit,.ledger .a-team.plate-lit{
  border-color:var(--gold-line);border-top-color:var(--gold-lit);box-shadow:inset 0 2px 0 var(--gold-lit),var(--glow-sm);
}
.ledger .rail-you{background:linear-gradient(90deg,rgba(238,201,118,.1),rgba(5,15,9,.28) 45%);box-shadow:inset 3px 0 0 var(--gold-lit)}
.ledger .crew-first{border-color:var(--gold-line);box-shadow:inset 0 1px 0 rgba(255,226,163,.5),0 0 32px -12px rgba(238,190,85,.4)}
.ledger .crew-mine{
  border-color:var(--gold-line);background:linear-gradient(160deg,rgba(24,31,18,.76),rgba(3,9,5,.6) 22%,rgba(0,4,2,.38) 70%);
  box-shadow:inset 0 1px 0 rgba(255,226,163,.42),var(--glow);
}
.ledger .seal{
  background:linear-gradient(180deg,#FFF0C1,#EEC976 40%,#B88B32);color:var(--ink-on-gold);border-color:var(--gold-lit);
  text-shadow:none;box-shadow:inset 0 1px 0 #FFF5D4,inset 0 -2px 0 rgba(100,70,15,.3),0 3px 0 #70551E,
    0 0 24px -4px rgba(238,201,118,.7),0 0 64px -12px rgba(238,190,85,.42);
}
.ledger .seal:not(:disabled):hover{
  background:linear-gradient(180deg,#FFF5D4,#FFDB8C 40%,#D6AA51);color:var(--ink-on-gold);filter:none;
  box-shadow:inset 0 1px 0 #FFF9E4,0 3px 0 #70551E,0 0 30px -3px rgba(238,201,118,.75),0 0 70px -12px rgba(238,190,85,.45);
}
.ledger .seal:not(:disabled):active{box-shadow:inset 0 2px 6px rgba(95,64,15,.35),0 0 30px -4px rgba(238,201,118,.55)}
.ledger .seal:disabled{background:#111813;color:#8B968C;border-color:#3F4D42;box-shadow:none}
.ledger .btn.btn-gold{
  color:var(--ink-on-gold);border-color:var(--gold-lit);background:linear-gradient(180deg,#FFF0C1,#EEC976 42%,#B88B32);
  box-shadow:inset 0 1px 0 #FFF5D4,0 2px 0 #70551E,0 0 22px -6px var(--glow-live);
}
.ledger .btn.btn-gold:not(:disabled):hover{background:linear-gradient(180deg,#FFF5D4,#FFDB8C 42%,#D6AA51)}
.ledger .btn.btn-gold:disabled{background:#111813;color:#8B968C;border-color:#3F4D42;box-shadow:none}
.ledger .chip-on{color:var(--gold-warm);border-color:var(--gold-line);background:linear-gradient(180deg,#222819,#060C08);box-shadow:inset 0 1px 0 rgba(255,226,163,.25)}
.ledger .gate .shield{filter:drop-shadow(0 0 12px rgba(151,224,175,.38)) drop-shadow(0 0 44px rgba(238,190,85,.28))}
.ledger .gate .wordmark{color:var(--gold-warm);text-shadow:0 0 24px rgba(238,201,118,.5)}
.ledger .gate-or::before,.ledger .gate-or::after{background:linear-gradient(90deg,transparent,var(--gold-line),transparent)}
.ledger :focus-visible{outline:2px solid var(--gold-lit);outline-offset:3px}
html .skull-say,html .skull-say::after{background:#040A06;text-shadow:none}


/* ============================================================================
   7. the page runtime's atomic classes (support.js ATOMIC_CSS)
   ========================================================================== */
.fx{display:flex}.col{display:flex;flex-direction:column}.grid{display:grid}.ac{align-items:center}.jc{justify-content:center}.jb{justify-content:space-between}.f1{flex:1}.noshrink{flex-shrink:0}.wrap{flex-wrap:wrap}.fw5{font-weight:500}.fw6{font-weight:600}.fw7{font-weight:700}.fw8{font-weight:800}.fs11{font-size:11px}.fs12{font-size:12px}.fs13{font-size:13px}.fs14{font-size:14px}.fs15{font-size:15px}.fs16{font-size:16px}.fs20{font-size:20px}.fs22{font-size:22px}.upper{text-transform:uppercase}.tc{text-align:center}.nowrap{white-space:nowrap}.gap8{gap:8px}.gap10{gap:10px}.gap12{gap:12px}.gap16{gap:16px}.gap24{gap:24px}.m0{margin:0}.mt8{margin-top:8px}.mt12{margin-top:12px}.mt16{margin-top:16px}.mb8{margin-bottom:8px}.mb12{margin-bottom:12px}.mb16{margin-bottom:16px}.posrel{position:relative}.posabs{position:absolute}.round{border-radius:50%}.ohide{overflow:hidden}.bbox{box-sizing:border-box}.pointer{cursor:pointer}.w100{width:100%}.b0{border:none}

/* ============================================================================
   8. the sheet that was inline in index.html's helmet
   ========================================================================== */
*:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-sizing:border-box}html,body{margin:0;height:100%;overflow:hidden;background:rgb(var(--hue-106,7,13,11));background-image:radial-gradient(ellipse 120% 80% at 50% -10%,rgba(var(--hue-33,63,122,92),.16),transparent 60%),radial-gradient(ellipse 90% 60% at 50% 108%,rgba(196,138,58,.11),transparent 62%),radial-gradient(ellipse 60% 50% at 8% 45%,rgba(var(--hue-52,46,92,74),.1),transparent 70%)}body{font-family:system-ui,-apple-system,'Segoe UI',sans-serif;color:rgb(var(--hue-65,223,230,220));-webkit-font-smoothing:antialiased;
/* EVERY WORD ON THE PAGE SITS A HAIR ABOVE IT. Owner: "raise ALL the text off the page a tiny tiny bit."
   One hard pixel of shadow directly under the glyph and nothing else -- no blur, no offset sideways --
   which is what letters cut into a dark surface do, and it is inherited so there is one declaration for
   the whole site rather than a rule per component. Anything that wants a real glow states its own
   text-shadow and wins; nothing that wants none has ever asked. */
text-shadow:0 1px 1px rgba(0,0,0,.55)}a:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:#C9A45C;text-decoration:none}a:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:#E6C682}input:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),button:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:inherit;color:inherit}input:focus:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){outline:1px solid #C9A45C}:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::-webkit-scrollbar{width:9px;height:9px}:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::-webkit-scrollbar-track{background:rgba(var(--hue-107,8,16,13),.72)}:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::-webkit-scrollbar-thumb{background:#4A3F22}:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::-webkit-scrollbar-thumb:hover{background:rgb(var(--hue-66,58,74,64))}/* ==========================================================================
THE CALIBAN LEDGER — one stylesheet, one set of names.

Written from a census of what the markup was already doing: 778 inline
style= attributes, 3,801 declarations, 128 distinct colour literals, 52
distinct text colours, 20 font sizes and 79 paddings. Nearly all of that was
the same handful of decisions typed out again slightly differently, which is
why two lists that should match never quite did.

VERSION 2. The first pass converted 372 of those attributes and stopped
dead on 382 more, because twelve agents each hit the same missing pieces and
all twelve refused to invent a class name for them rather than guess. Their
reports are what this version is built from: no justify-content, no
flex:none, no standalone letter-spacing, no background or border utility, no
margin at all, and a cascade in the wrong order. Every one of those is
filled in below.

ORDER MATTERS AND IT IS THE SECOND THING THEY CAUGHT. Components used to be
declared after the utilities, so .btn's own colour beat .c-gold and no
button could be recoloured by a class. Components now come FIRST and the
single-purpose utilities come LAST, which is the only order in which
"component, then adjust it" works.
========================================================================== */

:root{
/* ---- the size of the whole interface ----------------------------------
Owner: "the whole app is SO small. can we make everything for the desktop
view a little larger... just need to increase the size of everything by
about 10%".

ONE NUMBER. Every size below is a multiple of --ui, so the desktop bump is
a single line rather than 355 edits, and it is the reason the type scale
was collapsed to ten steps in the first place. Phones keep 1: the
complaint was about a desktop display with room going spare, and a phone
has none. */
--ui:1;
/* THE PAGE'S OWN SCALE, under a second name. .aar-record re-declares --ui to
   draw itself smaller, and a custom property cannot refer to its own inherited
   value -- `--ui:calc(var(--ui) * .7)` is a cycle and resolves to nothing. This
   is the value it multiplies. The two move together; see the media query below. */
--ui-page:1;

/* ---- ink: the text ramp, brightest to faintest ---- */
--ink:rgb(var(--hue-67,228,235,226));          /* body text, the parchment                       */
--ink-cool:rgb(var(--hue-45,196,208,196));     /* body text on a green ground                    */
--ink-2:rgb(var(--hue-21,169,188,175));        /* secondary                                      */
--ink-3:rgb(var(--hue-19,142,158,147));        /* explanatory prose                              */
--ink-4:rgb(var(--hue-14,109,125,112));        /* the most-used muted tone: counts, meta         */
--ink-5:rgb(var(--hue-12,95,112,102));        /* faint                                          */
--ink-6:rgb(var(--hue-36,79,95,84));        /* faintest: glyphs, rules                        */
--ink-on-gold:#120E06;  /* text ON a gold fill                            */

/* ---- gold: the chapter's own colour ---- */
--gold:#C9A45C;
--gold-lit:#D8AE4A;
--gold-warm:#E2C079;
--gold-dim:#8A6A32;
--gold-ash:#8C7A52;
--gold-line:rgba(201,164,92,.35);

/* ---- status ---- */
--good:rgb(var(--hue-2,127,180,137)); --good-2:rgb(var(--hue-15,110,158,122));
--warn:#B5705A; --warn-2:#C98B74;
--bad:#9B2C24;  --bad-lit:#C4685F;
--cyan:rgb(var(--hue-4,59,200,178));
/* WITH THE CO. Neither a credit nor a refusal, so neither green nor red: a man
   sent up is not a verdict, he is a question. Both values are lifted from the
   owner's own mockup, which draws an escalated brother in #7a92c4 with #9db4e0
   on his label and #d3ddf2 where it needs to carry over the pill. */
--esc:#7A92C4;  --esc-lit:#9DB4E0;

/* ---- surfaces ---- */
--bg:rgb(var(--hue-104,11,18,16));
--bg-deep:rgb(var(--hue-108,7,16,13));
--row-alt:rgb(var(--hue-75,16,30,24));
--panel:rgba(var(--hue-72,9,18,14),.62);
--panel-2:rgba(var(--hue-104,11,18,16),.7);
--panel-3:rgba(var(--hue-107,8,16,13),.6);
--panel-4:rgba(var(--hue-53,19,35,27),.55);
--panel-on:rgb(var(--hue-25,23,44,35));
--pop:rgb(var(--hue-76,13,26,20));                      /* a menu floating over the page      */
--tile:linear-gradient(150deg,rgba(var(--hue-35,70,100,80),.4),rgba(var(--hue-73,19,30,24),.75)); /* an avatar */
--track:rgba(var(--hue-26,24,44,34),.5);          /* the unfilled part of a bar         */
--line:rgb(var(--hue-27,26,42,32));
--line-2:rgb(var(--hue-11,43,58,38));
--line-3:rgba(var(--hue-1,178,201,177),.13);
--hair:rgba(var(--hue-1,178,201,177),.10);
--hair-2:rgba(var(--hue-1,178,201,177),.16);
--hair-3:rgba(var(--hue-40,120,170,140),.1);
--tile-edge:rgba(var(--hue-1,178,201,177),.22);
--rule:rgba(var(--hue-1,178,201,177),.10);   /* a divider between blocks and rows   */
--rule-head:rgba(201,164,92,.22);/* the brass rule under a panel title  */

/* ---- light: what a warm effect is made of ----------------------------------
   Every bevel, rivet and lamp in the sheet is one of these six. The sheet
   already writes rgba(222,206,164,.72) at four rivets and rgba(var(--hue-54,24,44,32),.6)
   five times; these are those values, given names so a rule can be enforced. */
/* WARMER, not just brighter. At .10 this composited to rgb(var(--hue-55,36,45,36)) over the
   panel -- red and blue within a point of each other, so "what a warm effect is
   made of" was hue-neutral grey, and dimmer than the --line-3 border painted on
   top of it. Raising the alpha alone does not fix that: at .16 it is still only
   +3 red over blue. The gold itself was too pale (blue 164 of 255), so the
   colour is warmed AND lifted, to +14 red over blue at a brightness that sits
   with the border rather than under it. */
--edge-lit:rgba(255,228,158,.20);
--edge-dark:rgba(0,0,0,.48);
--rivet:rgba(222,206,164,.72);
--rivet-shade:rgba(0,0,0,.62);
--lift:rgba(74,52,20,.32);
--lift-hard:rgba(92,66,26,.44);

/* ---- THE FOUR LAMPS. A glow in this sheet is one of these and nothing else.
   --glow-spent is transparent ON PURPOSE: a spent thing is drawn with the same
   declaration as a live one and simply gives off no light, so "cold" can never
   drift into being a colour of its own. */
--glow-live:rgba(216,174,74,.36);   /* ember   -- live, selected, yours     */
--glow-read:rgba(var(--hue-5,121,196,141),.32);  /* auspex  -- a machine read it, clear  */
--glow-owed:rgba(180,101,58,.36);   /* warning -- a discrepancy, or owed    */
--glow-spent:rgba(0,0,0,0);         /* cold    -- spent things do not glow  */

/* ---- type ---- */
--serif:'Cormorant Garamond',serif;
--mono:'JetBrains Mono',monospace;
--sans:system-ui,-apple-system,'Segoe UI',sans-serif;
--fs-1:calc(9px * var(--ui));   --fs-2:calc(10px * var(--ui));
--fs-3:calc(11px * var(--ui));  --fs-4:calc(12px * var(--ui));
--fs-5:calc(13px * var(--ui));  --fs-6:calc(14px * var(--ui));
--fs-7:calc(16px * var(--ui));  --fs-8:calc(20px * var(--ui));
--fs-9:calc(26px * var(--ui));  --fs-10:calc(44px * var(--ui));
--ls-1:.1em; --ls-2:.14em; --ls-3:.2em;
--ls-head:.26em;   /* a panel title            */
--ls-key:.18em;    /* a field label, a column  */
--ls-ctl:.12em;    /* a button, a state word   */
--ls-meta:.06em;   /* a mono meta line         */
--ls-brand:.22em;  /* the wordmark, and the boot animation that lands on it */

/* ---- space ---- */
--s1:calc(2px * var(--ui));  --s2:calc(4px * var(--ui));
--s3:calc(6px * var(--ui));  --s4:calc(8px * var(--ui));
--s5:calc(10px * var(--ui)); --s6:calc(12px * var(--ui));
--s7:calc(16px * var(--ui)); --s8:calc(20px * var(--ui));
--s9:calc(26px * var(--ui));--gut:clamp(12px,2.1vw,34px);

--r1:2px; --r2:3px;
--t:.16s ease;

/* ---- how things arrive, and how they are lit ------------------------------
   THE SETTLE CURVE is deliberately not named --ease-out: cubic-bezier(.2,.8,
   .2,1) is NOT the CSS `ease-out` keyword, and nine literal `ease-out`s remain
   in this sheet meaning something else. The durations are not --t-in either,
   because --t is a duration AND an easing -- a same-prefix duration-only token
   invites `transition:opacity var(--t-in)`, which silently falls back to
   `ease`. */
--ease-settle:cubic-bezier(.2,.8,.2,1);
--dur-in:.22s;         /* something appearing under your hand */
--dur-screen:.4s;      /* a whole screen arriving             */

/* THE BEVEL, once. It was written out four times, and the two STATE variants
   quietly dropped the contact shadow when they lit up -- so a panel went
   flatter at the moment it became important. */
--bevel:inset 0 1px 0 var(--edge-lit),
        inset 0 -1px 0 var(--edge-dark),
        0 1px 0 var(--edge-dark);
/* THE PLATE'S OWN LIP, and it is NOT --bevel's. The owner's mockup lights every
   panel with one faint COOL line -- inset 0 1px 0 rgba(var(--hue-23,228,240,226),.07) -- and
   draws nothing else inset: no dark bottom lip, no hard 1px drop. --bevel's
   warm gold at .20 is three times that alpha and carries two black edges the
   mockup has no counterpart for, so a panel came out rimmed rather than lit.
   --bevel is unchanged and still lights .card, .banner, .btn-gold and the
   .raise-* steps; only the PLATE family takes this. */
--lip:inset 0 1px 0 rgba(var(--hue-23,228,240,226),.07);
}

/* The desktop bump. 1000px is the same threshold the interface already calls
`wide`, so the type grows exactly when the two-column layouts appear. */
/*
 * THE DESK IS DRAWN AT 1.0 NOW, WHICH IS THE MOCKUP'S OWN SCALE.
 *
 * This was 1.1 -- the owner, months back: "increase the size of everything by
 * about 10%" -- and every parity pass since has left it alone and named base
 * tokens around it, because it was his instruction and not a mistake. Seeing
 * the finished page against his mockup he settled it the other way: "its ALL a
 * little too big too."
 *
 * At 1.1 every --fs-* and --s* on the screen was 10% over what he drew, so the
 * page could never be pixel for pixel however exactly the tokens were assigned.
 * At 1 they land on his numbers with nothing else to change: 12px headings,
 * 9px rules, 24px column gap, 20px panel padding, the lot.
 *
 * The phone keeps 1 as it always did, so nothing below 1000px moves at all.
 */
@media (min-width:1000px){ :root{ --ui:1; --ui-page:1 } }

/* ==========================================================================
COMPONENTS — declared BEFORE the utilities so a utility can adjust one.
========================================================================== */

.panel:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel)}
/* THE SAME MATERIAL AS A PLATE. A card and a banner were iron with a gold
   edge and no bevel, sitting beside plates that had one -- so two boxes a few
   pixels apart caught the light differently for no reason anybody chose. They
   take the plate's lit top lip and dark bottom now. Their gold border stays:
   a card and a banner CARRY something (a notice, a rule you can edit), and the
   plate's quieter --line-3 edge is for a panel that merely holds a section. */
.card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--gold-line);background:var(--panel-2);
padding:var(--s6) var(--s7);display:flex;flex-direction:column;gap:var(--s5);
box-shadow:var(--bevel)}
.banner:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--gold-line);background:var(--panel-4);
padding:var(--s6) var(--s7);border-radius:var(--r1);
box-shadow:var(--bevel)}

/* ==========================================================================
   FORGE -- the one inline edit.

   Three of the banners on this site were the same thing spelled three times:
   `col g4 banner r1 bd-gold danim` with a 300ms entrance, opened under a
   control to edit the thing above it. A sergeant met that shape three times
   and had to learn it three times.

   ORNAMENT AUDIT
     left rail + faint ember  STATE  this, and only this, is being edited.
                              EDITORS_SHUT already guarantees that at the
                              level of state; this is the same sentence said
                              where the reader can see it.
     the 220ms entrance       STATE  it appeared just now, under your hand.
                              It is the only motion in the shared layer, it
                              plays once, and it is never on something being
                              read -- only on the thing you just opened.
   It reuses the sheet's existing `enter` keyframe rather than adding one.
   ========================================================================== */
.forge:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;display:flex;flex-direction:column;gap:var(--s4);
padding:var(--s5) var(--s6);
border:1px solid var(--gold-line);border-left:2px solid var(--gold);
border-radius:var(--r1);
background:var(--panel-3);
box-shadow:var(--bevel),0 0 24px -14px var(--glow-live);
animation:enter var(--dur-in) var(--ease-settle)}
/* A menu that floats over the page: the roster's rank and operative pickers,
and anything else that has to sit above a row without being part of it. */
.pop:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;z-index:30;top:100%;left:0;max-height:260px;overflow:auto;
border:1px solid var(--gold);border-radius:var(--r1);background:var(--pop);
box-shadow:0 14px 40px -10px rgba(0,0,0,.9);padding:var(--s3);
display:flex;flex-direction:column;gap:var(--r2)}
/* A ROW OF A MENU. Laid out rather than run as text because half of them
   carry something on the right -- a count, a worth -- and the mark that says
   which one is set needs a column of its own so the words below it line up
   with the words above. */
.pop-item:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;gap:var(--s3);
cursor:pointer;padding:5px var(--s4);font-size:var(--fs-5);color:var(--ink-cool)}
.pop-item:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(var(--hue-28,27,48,36),.8);color:var(--ink)}
.pop-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold)}
.pop-mark:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;width:9px;font-size:var(--fs-2);color:var(--gold)}
/* A brother's face. Round like Discord's, because that is what it is a picture
of. The initials are the fallback for the ones with no avatar, and the same
element takes the picture as a background once there is one. */
.avatar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;justify-content:center;flex:none;
border-radius:50%;overflow:hidden;
width:calc(28px * var(--ui));height:calc(28px * var(--ui));
background:var(--tile) center/cover no-repeat;
border:1px solid var(--tile-edge);
font-family:var(--mono);font-size:var(--fs-1);color:var(--ink-2)}
.avatar-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:calc(22px * var(--ui));height:calc(22px * var(--ui));font-size:var(--fs-2)}
.avatar-lg:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:calc(30px * var(--ui));height:calc(30px * var(--ui));font-size:var(--fs-5)}
.account-avatar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;overflow:hidden}
.account-avatar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;background:var(--bg)}
/* THE PLACES STAND APART. Owner, on the strategium's company beside the
   judging page: "give it some nice seperation and such to look like this."
   What that page does is not more ink -- it is air. A fireteam's groups carry
   a margin above them and its men sit in cards with real gaps, so YOURS TO
   JUDGE and OTHER SQUADS read as two things rather than as one list with a
   line in it. This aside had ten pixels above a place and two between its men,
   which is a single dense block however many rules run through it.
   The rule stays and the space around it grows. */
.cmd-tier:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:baseline;gap:var(--s4);
  padding:var(--s5) 0 var(--s2);margin-top:var(--s6);
  border-bottom:1px solid var(--hair)}
.cmd-tier:first-child:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-top:0}

/* NO RULE UNDER A MAN HERE, AND THIS IS THE THIRD TIME THAT ANSWER WAS WRONG.
   A hairline was put under each name to "get some seperation here like in the
   reports", and the result was lined paper: a place head already carries a
   rule, so a company of one-man places became heading-rule, name-rule,
   heading-rule, name-rule down the whole column. Owner: "please fix this!!"

   THE PATTERN, WRITTEN DOWN SO IT STOPS REPEATING. cbeea05 ruled the muster's
   rows and made a spreadsheet. This ruled a company's men and made a ledger.
   Both times the request was for SEPARATION and both times ink was the wrong
   currency for it. What the judging page does -- the page the owner keeps
   pointing at -- is air: a margin above each group and a real gap between
   cards, and its only hairline is the one beside a GROUP heading.

   So separation here is the gap above a place and the gap between its men, and
   the single rule under the place head. Nothing else. */

/* AND THE RHYTHM IS THE RECORD'S. Owner, pointing at the judging page: "needs
   the right spacing." What that panel actually does is give a row ROOM --
   .record-row pays --s5 top and bottom and .record-grid holds a 34px floor --
   and its rule is rgba(var(--hue-1,178,201,177),.05), half the strength of --hair, which is
   why you have to look for it rather than at it.

   A man here was 4px of padding in a 24px line, which is why nothing but ink
   ever looked like separation. He gets the record's floor instead. No rule:
   with a ruled place head above him, even a .05 hairline reads as lined paper
   at this column width. */
.cmd-aside:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .rail-sm{padding:var(--s4) var(--s3);min-height:calc(34px * var(--ui))}
.cmd-tier-name:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-3);
  letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--gold-dim);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/*
 * THE COMMAND SCREEN, ON THE SHARED LAYER.
 *
 * Owner, twice: "the layout is great maybe jhust too small and the usrs should
 * have their aviitards?" and then, when it was, "its HUGE right now." Both are
 * fair and they are not opposites. The first complaint was that a 26px initial
 * in a circle is a bullet point on a screen you scan looking for a PERSON; the
 * answer to that is a face, not more pixels everywhere. Scaling the type and
 * the padding with it turned a company of nine into a page you have to
 * scroll -- and this panel is CONTEXT. The queue beside it is the work.
 *
 * So: the face is real and legible, and everything around it is tight. A tier
 * is a hairline and a word rather than a heading with a card around it, and a
 * man is one line high. THAT WAS RIGHT, AND IT WAS PRIVATE: this screen and
 * the roster's Companies tab drew the same thing -- a company's places with
 * the brothers standing in them -- out of two unrelated component sets, and
 * only this one grouped by rank, opened a man's reports, or marked the reader.
 *
 * So the vocabulary is gone into the shared layer rather than kept:
 *   .cmd-tier, .cmd-tier-name  ->  .plate-head plate-head-sm + .heading
 *   .cmd-row                   ->  .rail rail-sm rail-act   (this screen's
 *                                  own one-line metric, promoted)
 *   .cmd-row-you               ->  .rail-you                (ground, rail,
 *                                  and the word YOU: still marked three ways)
 *   .cmd-standing              ->  .label c-5 + .rail-group (below)
 *   .cmd-rank                  ->  .meta-ash t2
 *   .cmd-tag                   ->  .mark mark-sm mark-live
 * and the roster can now draw a company the way the screen that invented the
 * drawing draws it.
 *
 * TWO RULES EARNED STAYING FOR NOW.

 * .cmd-tier and .cmd-tier-name are kept above deliberately. Their markup does
 * not move onto .plate-head-sm in this pass, and deleting a rule whose element
 * is still on the page is how a screen loses its styling silently. They go
 * when the tier heading goes, not before.
 *
 * A RANK WITHIN A PLACE. "1st Squad / Sergeant / him / Brothers / the rest" --
 * the squad keeps its one heading and holds two ranks inside it, indented so
 * the nesting is visible without a second border. It is the only two-deep
 * grouping in the app, so the shared layer has no name for it; it is now an
 * INDENT and nothing else, because .label already says everything it used to
 * spell out about the type. Named for the family it groups.
 */
.rail-group:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s4) 0 0 var(--s4)}
/* A DATE IS TYPED OR PICKED, and the browser's own calendar is the picker.
   Owner: "click by date and a calendar pops up to select start and end dat".
   A hand-built calendar is a month of work and a worse one -- it would not
   know his locale, his week start, or how his keyboard reaches a date. */
.field-date:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-3);
  padding:var(--s2) var(--s3);width:auto;color-scheme:dark}
.field-date:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::-webkit-calendar-picker-indicator{filter:invert(.7) sepia(1) saturate(3) hue-rotate(5deg);
  cursor:pointer}

/* THE FACE ON HIS OWN RECORD, at the size a portrait wants rather than the
   size a list row wants. */
.avatar-me:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:calc(64px * var(--ui));height:calc(64px * var(--ui));
  font-size:var(--fs-8);border-color:var(--gold-line)}

.hair-b:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-bottom:1px solid var(--hair)}
.hair-t:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-top:1px solid var(--hair)}
.hair-r:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-right:1px solid var(--line)}
.rule-b:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-bottom:1px solid var(--rule)}
.notch:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){clip-path:polygon(13px 0,100% 0,100% 100%,0 100%,0 13px)}
.notch-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){clip-path:polygon(7px 0,100% 0,100% 100%,0 100%,0 7px)}

/* THE NAV BAR. Flat, and that is the point: it is chrome, it is opaque, and
   nothing passes behind it, so the gradient, the 40px cast and the 7px blur
   were three effects with no light to answer to. The gold rule under it went
   with them -- one brass line at the top of every screen competed with the
   brass rule under every panel title, which is the line that means something. */
.topbar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;z-index:5;background:rgba(var(--hue-109,9,15,12),.9);
border-bottom:1px solid var(--hair)}
.topbar-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s6);padding-top:var(--s6);padding-bottom:var(--s6);row-gap:0}
@media (min-width:1000px){ .topbar-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:30px} }

/* The tool bar every list screen opens with. */
.bar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;gap:var(--s4);padding:9px var(--s7);
border-bottom:1px solid var(--hair-3);background:var(--panel);
/* GLASS, LIKE THE NAV. --panel is 62% opaque and this bar is STICKY over
   scrolling rows, so table rows read straight through the toolbar. The nav
   above and the mobile nav below both solve it with blur(7px); the same number
   here makes all three pinned surfaces one material, and keeps the body's
   radial gradients showing through, which an opaque fill would kill. */
backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);flex-wrap:wrap}

/* The band of big numbers at the top of a tab. Owner liked it on the roster
and asked for it on reports and statistics, so it is one shape in one place
rather than three that drift. */
.totals:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;flex-wrap:wrap;border-bottom:1px solid var(--line)}
.total:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1;min-width:130px;padding:var(--s6) var(--s7);display:flex;
flex-direction:column;gap:5px;border-right:1px solid var(--line)}
.total:last-child:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-right:none}
.total-n:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-9);font-weight:500;
font-variant-numeric:tabular-nums}

/* ---- controls ---- */
.act:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){cursor:pointer;transition:background var(--t),box-shadow var(--t),
color var(--t),border-color var(--t)}

.btn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){cursor:pointer;transition:background var(--t),box-shadow var(--t),
color var(--t),border-color var(--t);
/* A QUIET EDGE BY DEFAULT, AND GOLD ONLY WHEN THE BUTTON IS THE ANSWER. The
   base border was --gold-line, so every "never mind" and "keep it" on the site
   was drawn in brass as loudly as the control beside it that does the work.
   .btn-outline is what puts the gold back, on the one button that wants it. */
border:1px solid var(--line-3);border-radius:var(--r1);
font-family:var(--mono);font-size:var(--fs-2);font-weight:500;
letter-spacing:var(--ls-ctl);text-transform:uppercase;
padding:9px var(--s6);color:var(--ink-2);text-align:center;background:transparent}
.btn-quiet:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-3)}
.btn-lg:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s7) calc(34px * var(--ui));font-size:var(--fs-6);letter-spacing:var(--ls-3)}
.btn-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s3) var(--s4);font-size:var(--fs-1);letter-spacing:var(--ls-1)}
.btn-gold:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:none;background:linear-gradient(180deg,var(--gold-lit),#A9832A);
color:var(--ink-on-gold);
/* INSET, BECAUSE THE CLIP EATS EVERYTHING OUTSIDE. This declared a 20px outer
   glow and then clipped itself to a notched polygon, which throws that glow
   away on every save, sign-in and confirm on the site. filter:drop-shadow is
   clipped just as hard -- filter runs before clip -- so the light has to be
   INSIDE: a rim of it, which the clip keeps. */
box-shadow:inset 0 1px 0 rgba(255,236,180,.55),
           inset 0 0 12px -2px rgba(255,236,180,.45);
clip-path:polygon(7px 0,100% 0,100% 100%,0 100%,0 7px)}
.btn-bad:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--bad);color:var(--bad-lit)}
.btn-outline:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold-line);color:var(--gold)}

.chip:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){cursor:pointer;transition:background var(--t),color var(--t),border-color var(--t);
border:1px solid var(--gold-line);font-size:var(--fs-2);font-weight:500;
font-family:var(--mono);letter-spacing:var(--ls-ctl);text-transform:uppercase;
padding:var(--s3) var(--s7);
color:var(--ink-3);background:transparent;white-space:nowrap}
.chip-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel-on);color:var(--ink)}
/* THE FEW ROWS OF THE AUDIT LOG THAT MATTER. A kick, a ban and a timeout are
   what a commander is scanning for; they sit in a wall of 420 nickname
   changes, so those three get the one loud chip on the screen. */
.chip-bad:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--bad-lit);border-color:var(--bad)}
/* ONE ROW OF THE GUILD'S LOG. A grid and not a flex row so the time and the
   kind hold their columns down the whole list -- a log whose left edge moves
   per row cannot be scanned, which is the complaint about reading it in
   Discord. The name and the change take what is left and ellipsis; nothing
   here wraps, and the plate scrolls rather than the page. */
.audit-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:96px auto minmax(0,1fr);
align-items:center;gap:var(--s5);padding:var(--s4) 0;
border-bottom:1px solid var(--rule)}
.audit-row:last-child:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-bottom:none}
.audit-when:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-variant-numeric:tabular-nums}
/* THE KIND COLUMN SIZES TO ITS OWN WIDEST LABEL, and it has to. At a fixed
   190px the chip for "CHANNEL PERMISSIONS CHANGED" -- which is `nowrap`, as
   every chip on this site is -- ran straight out of its column and the
   brother's name was drawn ON TOP OF IT. Caught in a real browser at 1440px,
   which is the width this was designed at; no narrower window was needed.
   `auto` is measured across the whole grid, so every row still shares one
   edge, which is the property that makes a log scannable at all. */
.audit-what:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;min-width:0}
/* AND THE 860px QUERY THAT WAS HERE COULD NEVER FIRE. This tab is in
   DESKTOP_ONLY and the strip is gated on `wide = s.w >= 1000`, so the
   narrowest width at which the screen exists AT ALL is 1000. Everything below
   it was dead CSS pretending to be responsive. */
/* FOUR BROTHERS SIDE BY SIDE. Owner, 2026-09-12: "infact be able to open up 4
   people at once and see EVERYTHING they did and everything dont to them and
   by who."

   AS MANY COLUMNS AS THERE ARE MEN, supplied by the markup as --dn, the way
   .grid-hdr takes --dcols. Not auto-fit: two men asked for must be two wide
   squares and not two narrow ones beside two empty tracks.

   AND NOT ONE MEDIA QUERY, which is a correction. The tab is in DESKTOP_ONLY
   and its strip is gated on `wide = s.w >= 1000`, so the 860px rule directly
   above this can never fire on this screen -- it was written for a width that
   cannot reach the tab. The real narrow case IS 1000px, where four panes are
   about 230px each (about 340px at 1440), and that is why a pane holds a rail
   and a count of rungs rather than a ladder: nothing here needs a breakpoint
   because the narrowest live case is the case it is drawn for. */
.panes:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:repeat(var(--dn),minmax(0,1fr));
gap:var(--s5);align-items:start}
/* THE PANE SCROLLS, NOT THE PAGE. Same rule .audit-row states for the feed:
   704 rows is the thickest file on the live ledger and a square that grows to
   fit its man would put the other three off the bottom of the screen, which is
   the one thing a comparison view may not do. 58vh keeps all four bottom edges
   in the same place whatever is in them. */
.pane-rail:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){overflow-y:auto;max-height:58vh;min-height:0}
/* ONE LINE OF A PANE, STACKED WHERE THE FEED'S ROW IS COLUMNED. .audit-row
   spends 96px and 190px on fixed columns before it reaches the name, which is
   most of a 230px square; a pane row stacks instead -- when and kind, then
   what was said, then who -- so the brother's name, the thing the walk clicks,
   always has the full width of the square to be read in. */
.pane-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;flex-direction:column;gap:var(--s1);min-width:0;
padding:var(--s3) 0;border-bottom:1px solid var(--rule)}
.pane-row:last-child:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-bottom:none}
/* ======================================================== TRACE A BROTHER --
   Owner, 2026-09-12, on the audit log exactly as it shipped: "yeah as i thought
   nothing but text shit everywhere i have no idea what the fyuck im looking
   at." Then what he actually wanted, in his own words: "this needs to be member
   related not fucking role or action related", "I need to know what heppened to
   THIS GUY!! who dhd THIUS GUY effect?", "and when i see what what done to him
   i want to see by who?!? i need ot be able to trace through events."

   TWO COLUMNS, AND THE LEFT ONE IS PEOPLE. 268px is the narrowest column that
   holds a face, a name of the length this guild really uses -- "Grand Master
   Chaplain Sapphon | Legion" is 38 characters -- and a count; the right column
   takes the rest and its two panels split that between them.

   AND NOT ONE MEDIA QUERY BELOW 1000px. The tab is in DESKTOP_ONLY and the
   strip is gated on `wide = s.w >= 1000`, so the narrowest window that can
   reach this screen at all is 1000 -- I shipped an 860px breakpoint for this
   very tab once that could never fire. At 1000 the two panels are about 330px
   each, which is the width the rows below are drawn for. */
.trace:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:268px minmax(0,1fr);
gap:var(--s6);align-items:start}
/* THE ROLL STAYS WHERE IT IS AND THE LIST INSIDE IT SCROLLS. Sticking the
   whole plate would take the search box and the four orders out of reach the
   moment a reader scrolled 572 men, and they are the only way back. */
.trace-roll:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:sticky;top:var(--s5)}
.trace-list:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){overflow-y:auto;max-height:64vh;min-height:0;
border-top:1px solid var(--hair)}
/* THE BOX SITS INSIDE THE PLATE'S RIVETS AND NOT ON THEM. .plate has no
   padding of its own and draws four rivets on its ::before at inset 5px, so a
   full-width control flush to its edge is drawn over them. The rows below have
   their own padding and clear it already. */
.trace-roll:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .search{margin:var(--s4) var(--s5)}
/* ONE MAN ON THE ROLL. A grid and not a flex row, for the reason .audit-row
   states: a list whose left edge moves per row cannot be scanned. The count
   column is `auto` and sized to its own content -- a fixed column here is how
   "CHANNEL PERMISSIONS CHANGED" came to be painted over a brother's name at
   1440px on the feed. */
.trace-man:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:calc(22px * var(--ui)) minmax(0,1fr) auto;
align-items:center;gap:var(--s4);padding:var(--s3) var(--s5);
border-bottom:1px solid var(--rule);border-left:2px solid transparent}
.trace-man:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--row-alt)}
.trace-man-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel-on);border-left-color:var(--gold)}
.trace-nm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:var(--fs-6);line-height:1.15}
.trace-co:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-meta);
text-transform:uppercase;color:var(--ink-4)}
/* A MAN WHO HAS GONE AND A BOT ARE MARKED, NEVER DROPPED. House rule: nothing
   is hidden because a man has left -- his file is often the one somebody
   wants. 15 of the 567 have left and 11 are bots (measured against the live
   ledger 2026-09-12 21:15Z; the mock-up he approved was built on an earlier
   sweep that read 572/20/11, which is why the two disagree). */
.trace-gone:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .trace-nm,.trace-bot:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .trace-nm{color:var(--ink-3)}
.trace-gone:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .trace-co{color:var(--bad-lit)}
.trace-ct:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-2);color:var(--ink-4);
text-align:right;white-space:nowrap}
/* 476 OF 572 HAVE NOTHING ON THEM (measured 2026-09-12), so his row is quiet
   rather than a pair of noisy zeroes down two thirds of the list. */
.trace-ct-quiet:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-6)}
/* THE MAN, AND THEN THE MEN HIS LOG NAMES. Owner, typing his own name and
   getting thirteen rows: "how come when i trype revelation i get all this
   shit?" The thirteen are a real answer to a different question, so they are
   banded rather than dropped. It spans all three columns of the row it is
   carried by -- see `band` in traceRoll for why it is not a row of its own --
   and the rule above it is the seam. */
.trace-band:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){grid-column:1/-1;font-family:var(--mono);font-size:var(--fs-1);
letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--ink-5);
padding-bottom:var(--s3);margin-bottom:var(--s3);
border-bottom:1px solid var(--hair)}
/* WHY HE MATCHED, WHEN IT WAS NOT HIS NAME. A man found by a role nobody can
   see on his row reads as a wrong answer. */
.trace-lit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(196,138,58,.28);color:var(--ink);border-radius:2px;padding:0 1px}
.trace-hit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);color:var(--gold);
letter-spacing:var(--ls-1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* HOW THE ROLL IS ORDERED. Busiest answers "who is being worked on", latest
   answers "what just happened", a-z finds a man already in mind, and roster is
   the order the chapter itself musters in. Four questions, one control. */
.trace-sorts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;border-bottom:1px solid var(--hair)}
.trace-sort:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1;text-align:center;cursor:pointer;
font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-2);
text-transform:uppercase;color:var(--ink-5);padding:var(--s3) var(--s2);
border-right:1px solid var(--rule);background:none}
.trace-sort:last-child:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-right:none}
.trace-sort:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-2)}
.trace-sort-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold);box-shadow:inset 0 -2px 0 var(--gold)}
/* FOURTEEN DROPDOWNS, OVER BOTH PANELS AND INSIDE NEITHER. Owner: "filters
   would be rolls server actions etc etc all the filter dropdown you can think
   of", and then, on the two panels: "actually who he evffected and and dont to
   him is perfect." A filter that only applied to half the screen would be a
   filter nobody could trust, so they sit above both. auto-fit rather than a
   fixed count: fourteen controls at 146px wrap to four rows at 1000px and
   three at 1440 without a breakpoint for either. */
.trace-drops:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:repeat(auto-fit,minmax(146px,1fr));
gap:1px;background:var(--hair);border:1px solid var(--line)}
.trace-drop:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--bg);border:none;color:var(--ink-3);
font-family:var(--mono);font-size:var(--fs-2);letter-spacing:var(--ls-1);
padding:var(--s3) var(--s4);cursor:pointer;min-width:0;text-overflow:ellipsis}
.trace-drop:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink)}
.trace-drop:focus:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){outline:1px solid var(--gold);outline-offset:-1px}
/* A SET FILTER SAYS SO. Fourteen controls is exactly the number at which one
   left on by accident becomes "the screen is broken". */
.trace-drop-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold);background:var(--panel-on)}
/* TWO PANELS, SIDE BY SIDE AND EQUAL. Owner: "the right side needs to be the
   action", then "actually who he evffected and and dont to him is perfect."
   Neither half is the more important, so neither gets more room. */
.trace-sheet:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
gap:var(--s5);align-items:start}
.trace-side:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:0}
.trace-rail:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){overflow-y:auto;max-height:64vh;min-height:0}
/* THE DAY, ONCE, OVER THE ROWS THAT HAPPENED IN IT. Owner: "need to be able to
   sort by date as well" -- and before that, the fault it fixes: the feed
   printed "09-12" against two hundred consecutive rows, which is the same fact
   two hundred times and no help at all. The rows carry the time only. */
.trace-day:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-2);
text-transform:uppercase;color:var(--gold-ash);
padding:var(--s5) var(--s6) var(--s2);border-bottom:1px solid var(--rule)}
.trace-ev:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:calc(38px * var(--ui)) minmax(0,1fr);
gap:var(--s4);align-items:start;padding:var(--s4) var(--s6);
border-bottom:1px solid var(--rule)}
.trace-ev:last-child:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-bottom:none}
/* A KICK, A BAN AND A TIMEOUT GET THE EDGE. Those three are what a commander
   is scanning a wall of nicknames for; a rename is not one of them and stays
   quiet, which is the whole point of giving only three of them a colour. */
.trace-ev-heavy:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(155,44,36,.10);box-shadow:inset 2px 0 0 var(--bad)}
.trace-t:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-2);color:var(--ink-5);
font-variant-numeric:tabular-nums;padding-top:2px}
.trace-said:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-5);line-height:1.5;min-width:0}
/* THE VERB LEADS AND THE OTHER MAN IS THE LAST WORD ON THE LINE, because he is
   the next step of the trace. Owner, on the first cut: "need to be more clear
   what happened" -- the rows read "- Battle Brother by Caine", which is the
   payload said aloud rather than an account of an event. */
.trace-lead:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-weight:600;color:var(--ink)}
.trace-verb:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-3)}
.trace-mono:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-4)}
/* HOW MANY DAYS TO SET A MAN ASIDE FOR. Owner: "remind me in X days let me
   specify that number" -- so it is a box he types in, not a menu of guesses.
   Sized to three digits because the server refuses anything over a year, and
   tabular figures so 7 and 30 do not jump the button beside them about. */
.days-box:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:calc(46px * var(--ui));text-align:center;
font-family:var(--mono);font-size:var(--fs-2);font-variant-numeric:tabular-nums;
color:var(--ink-2);background:var(--panel);border:1px solid var(--rule);
border-radius:2px;padding:var(--s2) var(--s3)}
.days-box:focus:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){outline:none;border-color:var(--gold);color:var(--ink)}
/* THE ONE THING THIS SCREEN CANNOT ANSWER, SAID RATHER THAN LEFT BLANK.
   Discord records who DID a voice drag, the room and a count, and never the
   brother. Set quieter than the sentence it ends and allowed to wrap, because
   it is a caveat and not the event -- but it is on the line, not in a tooltip:
   the owner went looking for a move he had made and found nothing, which is
   exactly what an unexplained gap costs. */
.trace-unsaid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-5);font-size:var(--fs-2);font-style:italic}
/* A ROLE THAT MOVED, AND WHICH WAY IT WENT. Green is given, red is taken --
   the two colours the rest of this site already uses for those two meanings.
   Mono, because a role name is a token and not prose. */
.trace-pill:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:inline-block;font-family:var(--mono);font-size:var(--fs-3);
padding:0 var(--s3);margin:1px 2px 1px 0;border:1px solid;white-space:nowrap}
.trace-add:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--good);background:rgba(var(--hue-2,127,180,137),.12);
border-color:rgba(var(--hue-2,127,180,137),.34)}
.trace-rem:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--bad-lit);background:rgba(196,104,95,.11);
border-color:rgba(196,104,95,.32)}
/* EVERY NAME IS A FACE AND EVERY FACE IS THE NEXT STEP. Owner: "and when i see
   what what done to him i want to see by who?!? i need ot be able to trace
   through events." So the other man in the act is drawn as himself -- his own
   Discord picture where he has one, 111 of 567 do (measured 2026-09-12
   21:15Z: `authors.avatar_path` joined to guild_members) -- and
   clicking him opens his file. That IS the feature. */
.trace-peer:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:inline-flex;align-items:center;gap:var(--s3);
vertical-align:-4px;font-family:var(--serif);font-size:var(--fs-6);
font-weight:600;color:var(--ink);padding:1px var(--s3) 1px 2px;border-radius:999px}
.trace-peer:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel-on);color:var(--gold-warm)}
/* A MAN WHO IS ON NO ROLL -- Discord's own acts, and the handful of ids this
   guild has never seen -- is still named and still drawn, and is not a door,
   because there is no file behind him to open. */
.trace-peer-gone:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-3);cursor:default}
.trace-reason:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:block;font-family:var(--mono);font-size:var(--fs-3);
color:var(--ink-4);margin-top:2px}
/* AN EMPTY PANEL IS THE COMMON CASE -- 294 of 567 men have nothing on them at
   all, measured 2026-09-12 -- so it says so in a sentence, with the floor
   named. A blank box reads as a broken screen. */
.trace-none:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s7) var(--s6);color:var(--ink-3);font-size:var(--fs-5);
margin:0}
/* THE WALK, ACROSS THE TOP. Every step back is one click, and the trail is the
   only record of where a reader has been -- the panels hold one man. */
.trace-trail:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:var(--fs-5);
color:var(--ink-2);background:none;border:1px solid var(--line);
padding:1px var(--s4);cursor:pointer}
.trace-trail:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-warm);border-color:var(--gold-line)}
.trace-trail-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink);border-color:var(--gold-line)}
.trace-who:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;flex-wrap:wrap;gap:var(--s5);
padding:var(--s6) var(--s7)}
.trace-who-name:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:var(--fs-8);font-weight:600;
line-height:1.05}
.trace-tally:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-left:auto;display:flex;gap:var(--s8)}
.trace-tally-n:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-8);font-weight:500;
display:block;text-align:right}
.trace-tally-k:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);
letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--ink-4);
display:block;text-align:right}
/* EVERY LANE LEAVES A LIGHT. Owner: "a flashing blue symbol on them".
   The fleet reads six reports at once; its single current id cannot name all
   six. Claimed rows pulse blue, queued rows wait dimly, and the glyph makes
   the work visible beside the words without taking away the row's tint. */
.scanning:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;background:rgba(122,146,196,.07);
box-shadow:inset 2px 0 0 var(--esc);animation:scanrow 1.4s ease-in-out infinite}
.scan-queued:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;background:rgba(122,146,196,.03);
box-shadow:inset 2px 0 0 rgba(122,146,196,.4)}
.scan-glyph:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:inline-block;color:var(--esc-lit)}
.scan-glyph-reading:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:scanmark 1.4s ease-in-out infinite}
.scan-glyph-queued:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--esc);opacity:.5}
@keyframes scanrow{0%,100%{background:rgba(122,146,196,.05)}50%{background:rgba(122,146,196,.14)}}
@keyframes scanmark{0%,100%{opacity:.4}50%{opacity:1}}
/* THE COUNT ON A QUEUE THAT IS WORK. It used to hang off a chip's top-right
   corner so the chips kept their widths as the numbers changed; with the
   filters now dropdowns there is a line for it to sit on, both on the shut
   control and on the menu row inside it, and a badge floating over the border
   of a dropdown is the same mark in the wrong place. */
.tally:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;min-width:17px;padding:0 4px;font-family:var(--mono);
font-size:var(--fs-2);line-height:16px;text-align:center;color:rgb(var(--hue-104,11,18,16));
background:var(--gold);border:1px solid var(--gold);border-radius:9px}
/* The reader's own backlog pulses, because it drains by itself and the only
   question about it is whether it is moving. */
.tally-scan:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgb(var(--hue-4,59,200,178));border-color:rgb(var(--hue-4,59,200,178));
animation:led 2.2s ease-in-out infinite}

/* ---- A FILTER SAYS ITS QUESTION AND ITS ANSWER ----
   Owner: "all the damn filters are crazy... lets combine them into their
   groups and make them dropdowns so its not so cluttered." Seventeen chips in
   four unlabelled groups are four controls, each the width of what it says.
   Built on .decide-v's shape -- the same gold-lined box with the same caret --
   because opening a list of words and picking one is the same gesture the
   override on a report already is, on the same screen. */
.filt:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;display:flex;align-items:center;gap:var(--s3);
flex:none;cursor:pointer;border:1px solid var(--gold-line);
background:var(--panel-3);padding:var(--s3) var(--s5);
transition:border-color var(--t)}
.filt:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold)}
.filt:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{content:"▾";font-size:var(--fs-3);color:var(--gold-dim)}
.filt-k:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
letter-spacing:var(--ls-key);text-transform:uppercase;color:var(--ink-6)}
.filt-v:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-5);color:var(--gold-warm);white-space:nowrap}
/* Lit once it is narrowing the list, so a bar with something set on it reads
   as set at a glance and nobody has to open four menus to find out why the
   list is short. */
.filt-set:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold)}
.filt-set:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .filt-v{color:var(--gold)}

/* .chip-group is gone with the last row of filter chips. .chip itself stays:
   the rank scope on the permissions screen is one, and it is a control that
   says a value rather than a filter that says a choice. */

.search:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;gap:7px;border:1px solid var(--hair-2);
background:var(--panel-3);padding:5px 9px;min-width:190px}
.search:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) input{background:transparent;border:none;outline:none;padding:0;
font-size:var(--fs-5);color:var(--ink);width:100%}
.field:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--gold-line);background:var(--bg);padding:5px var(--s4);
font-family:var(--mono);font-size:var(--fs-6);color:var(--ink);outline:none;
border-radius:var(--r1)}
.field-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold)}
.field-num:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:calc(60px * var(--ui));text-align:right}
.field-min:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:calc(48px * var(--ui));text-align:right;border-color:var(--hair-2);
background:var(--panel-3);padding:3px 7px;font-size:var(--fs-5)}
.field-wide:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:100%;font-size:var(--fs-4);padding:3px var(--s3)}
/* Owner: "i dont want the arrows on the fields strictly fill in". type=number
is kept -- it is what gives a phone a numeric keypad and refuses letters --
and only the spinner goes. */
input[type=number]:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::-webkit-outer-spin-button,
input[type=number]:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
input[type=number]:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){-moz-appearance:textfield;appearance:textfield}
.tick:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:11px;height:11px;flex:none;border:1px solid var(--line-3);background:transparent}
.tick-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold);background:var(--gold)}

/* ---- the evidence gallery ----
Thumbnails that do not move when one is clicked. auto-fill rather than a
fixed count so a report with one screenshot and a report with five are the
same screen, and a fixed aspect so the row cannot go ragged. cover on the
thumbnail because a thumbnail is a handle, contain in the lightbox because
that is where the picture is actually read. */
.gallery:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:repeat(auto-fill,minmax(calc(268px * var(--ui)),1fr));
gap:var(--s6)}
/* A TENTH TALLER THAN THE SCREEN IT SHOWS. A results screen is 16:9, so a
   16:9 tile fits it exactly and leaves the numbers at the size the game drew
   them -- which is small. Owner: "make the evidence tab initially about 10%
   taller." The extra band is dead space around a contained image and readable
   pixels on a cover-fitted one. */
.shot:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;aspect-ratio:4/3;overflow:hidden;cursor:pointer;
border:1px solid var(--hair-2);border-radius:var(--r1);
background-color:rgb(var(--hue-110,11,19,16));
background-image:repeating-linear-gradient(135deg,rgba(var(--hue-56,200,220,205),.05) 0 13px,rgba(var(--hue-56,200,220,205),0) 13px 26px);
transition:border-color var(--t),transform var(--t)}
.shot:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold);transform:translateY(-2px)}
/* THE WORD IN AN EMPTY TILE. One type for both lines -- the slot it wants and
   the reason the picture is not here -- so the second reads as the quieter
   half of one sentence rather than as a different kind of text. */
.shot-slot:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:calc(9.5px * var(--ui));line-height:1.6;
letter-spacing:var(--ls-ctl);color:rgb(var(--hue-38,85,100,87))}
/* A CSS BACKGROUND, NOT AN <img>, and that is not a style preference.
The template is served as ordinary HTML and the browser parses it before the
runtime ever sees it, so an img whose src is a binding gets fetched verbatim
by the preload scanner -- two 404s for the unresolved placeholder text on
every single page load. An unresolved binding inside url() is invalid CSS
instead, so the declaration is dropped and nothing is requested. */
.shot-img:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:100%;height:100%;background-position:center;
background-repeat:no-repeat;background-size:cover}
.shot-tags:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;top:var(--s4);left:var(--s4);
display:flex;align-items:center;gap:var(--s3)}
.shot-badge:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;top:var(--s4);left:var(--s4);padding:calc(3px * var(--ui)) var(--s3);
font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
border-radius:var(--r1);color:var(--gold-warm);
border:1px solid var(--gold-line);background:rgba(var(--hue-111,10,16,13),.85)}
.shot-tags:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .shot-badge{position:static}
/* ==========================================================================
   MARK -- the one state pill, in the four lamps and nothing else.

   .shot-mark and .shot-mark-on are second names on the base and on the green
   one: skull.js points at `.aar-auspex .shot-mark` to place its verdict, so
   the class has to stay in the DOM. Whether a stats card came off a picture is
   one use of this pill, not a component of its own.

   ORNAMENT AUDIT: every one of these SHOWS A STATE, which is the whole
   component. The default is the COLD one -- a mark with no job given to it
   gives off no light, because --glow-spent is transparent. Four colours, four
   meanings, and the word inside says what the colour says, so nothing here is
   carried by hue alone.
   ========================================================================== */
.mark:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.shot-mark:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:inline-flex;align-items:center;gap:var(--s4);flex:none;
padding:var(--s3) var(--s5);font-family:var(--mono);font-size:var(--fs-1);
font-weight:500;letter-spacing:var(--ls-2);text-transform:uppercase;white-space:nowrap;
color:var(--ink-5);border:1px solid var(--line-2);border-radius:var(--r1);
background:var(--panel-2);box-shadow:0 0 14px -6px var(--glow-spent)}

/* the size for a tag riding inside a row rather than answering for a panel */
.mark-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-1);padding:0 var(--s2)}

.mark-live:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-warm);border-color:var(--gold-line);
background:var(--lift);box-shadow:0 0 14px -6px var(--glow-live)}
.mark-read:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.shot-mark-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--good);border-color:var(--good-2);
box-shadow:0 0 14px -6px var(--glow-read)}
.mark-owed:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--warn-2);border-color:var(--warn);
box-shadow:0 0 14px -6px var(--glow-owed)}
.mark-spent:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-6);border-color:var(--line-3);background:transparent;
box-shadow:0 0 14px -6px var(--glow-spent)}

/* THE LAMP, opt-in. A dot in currentColor with its own halo, for the marks
   reporting a MACHINE's state -- the auspex verdict, a live sweep. Never on an
   ordinary label: a lamp on every pill is a string of lights, which is the
   noise the rule exists to stop. */
.mark-lamp:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::before,.shot-mark[data-verdict]:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::before{content:"";flex:none;
width:6px;height:6px;border-radius:50%;
background:currentColor;box-shadow:0 0 7px currentColor}
/* THE AUSPEX VERDICT TAKES ITS OWN HUE, EDGE AND ALL. The pill used to be a
   neutral green box with a coloured word in it, so the one component on the
   screen whose whole job is a verdict said it in text alone. data-verdict is
   the fact the page already states for skull.js -- "reading" / "bad" / "good"
   -- so the border and the wash read the same fact rather than a second copy
   of it, and no script has to hand out a class for it. */
.shot-mark[data-verdict="bad"]:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--warn-2);
border-color:rgba(181,112,90,.35);background:rgba(181,112,90,.08)}
.shot-mark[data-verdict="good"]:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:rgba(var(--hue-2,127,180,137),.3);
background:rgba(var(--hue-2,127,180,137),.07)}
/* HOW FAR THE JUDGEMENT HAS GOT. A report is settled a man at a time, and this
   is read at a glance a hundred times an evening, so it is a line rather than
   a sentence. */
/* 3px on a rounded ash track, which is the mockup's own bar. It draws ONE
   fill rather than the mockup's one pip per brother: a pip row needs an sc-for
   over the men, and {{ dSettledPct }} -- the fraction the sentence beside it
   quotes -- would then have nothing reading it. A dropped binding costs more
   than three gaps in a 3px line. */
.meter:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;height:3px;border-radius:var(--r1);
background:rgba(var(--hue-1,178,201,177),.14);overflow:hidden}
.meter-fill:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:0;top:0;bottom:0;border-radius:var(--r1);
background:linear-gradient(90deg,rgb(var(--hue-33,63,122,92)),rgb(var(--hue-5,121,196,141)));transition:width var(--t)}
/* THE TWO COLUMNS OF AN OPEN REPORT: what was filed on the left, what is being
   decided on the right. They were three columns in one wrapping row -- team,
   filing, evidence -- which put the buttons above the pictures they are about
   and left the sergeant scrolling between them. Both fall to one column on a
   phone, where the order is evidence first and the decision under it. */
/* THE SPACE BETWEEN THE COLUMNS IS THE CONTAINER'S, and it was seven panels'
   margins instead. Every panel carried `mt7 mx7`, so the gap between the two
   columns was two 16px margins meeting in the middle -- 32px that no single
   edit could retune -- and each panel sat --gut PLUS 16px in from the glass
   while the gutter's whole reason for existing is to be the one inset. Gaps
   here, no margins there: the columns are 24px apart, the panels in a column
   22px, and all three numbers are stated once. */
/* ONE TRACK GROWS AND IT IS THE FILING. The mockup's page is a grid --
   `minmax(620px,1fr) 560px` -- so the deciding column is 560px at every width
   and the filing column absorbs all the slack. Both tracks were flex:1 1 with
   grow enabled, which made the side column a function of the viewport: 565px
   at 1600 and 491px at 1280, against a drawn 560 at both. flex:0 1 lets it
   shrink below 560 when there is genuinely no room and never above it.
   The page's own top and bottom room is here too: 22px under the title block's
   rule and the mockup's deep 72px foot, which the panels used to answer for
   with margins and then stopped answering for at all. */
/* NOWRAP ABOVE THE BREAKPOINT, and that is a condition of the fixed track. A
   wrapping row breaks its line on the items' BASIS, not on their shrunk size,
   so the moment the deciding column stopped being a percentage and became a
   fixed 560px the two columns would have STACKED anywhere under about 1390px
   -- in source order, with no phone reordering, which puts the men and the
   buttons below everything a sergeant has already read. Both children carry
   min-width:0, so nowrap shrinks them instead and nothing overflows. Under
   1000px the media query below turns this into a single column properly. */
/*
 * THE MOCKUP IS A GRID, AND APPROXIMATING IT IN FLEX WAS WRONG.
 *
 * Its line 68, verbatim: display:grid; grid-template-columns:minmax(620px,1fr)
 * 560px; gap:24px; align-items:start. This was built as flex:1 1 620px against
 * flex:0 1 560px, which lands within a few pixels of the grid at 1280px and at
 * 1600px -- the two widths the mockup is drawn at, and the two I measured. It
 * does NOT land anywhere else: flex-basis is a starting point and the free
 * space is then shared, so on a 2526px monitor the filing column took ~1900px
 * and dragged every panel in it wide. The owner saw that before I did.
 *
 * AND IT IS NOT CAPPED. It was, briefly, at the mockup's own 1636px and
 * centred -- on the reasoning that "pixel for pixel" past 1600px can only mean
 * the page stops growing at the width it was drawn for. The owner looked at it
 * full width and settled it: "make them full width but respect the page
 * padding."
 *
 * Which is right, and the grid is why. Under the old flexbox BOTH columns grew
 * and the deciding column came out at 616px against a drawn 560; under
 * minmax(620px,1fr) 560px only the filing column takes the slack, so the
 * fireteam holds its drawn width at any monitor and the extra room goes to the
 * evidence and the record, which are the things that want it. The sides come
 * from .gutter on the scrollport, so the page's own padding is already
 * respected and nothing here needs to restate it.
 */
.aar-cols:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;
grid-template-columns:minmax(calc(620px * var(--ui)),1fr) calc(560px * var(--ui));
align-items:start;
gap:calc(22px * var(--ui)) calc(24px * var(--ui));
padding-top:calc(22px * var(--ui));padding-bottom:calc(72px * var(--ui))}
.aar-main:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:0;gap:calc(22px * var(--ui))}
/* IT STICKS, and the header above it does not -- the mockup pins the deciding
   column at top:20px and lets the filing scroll under it, which is the whole
   reason its header is a flat block rather than a bar. .aar-cols already sets
   align-items:flex-start, which is the precondition: a stretched item is full
   height and sticky never engages. On a phone .aar-side is display:contents
   and position is inert, so there is no narrow-width case to answer for. */
.aar-side:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:0;gap:calc(20px * var(--ui));
position:sticky;top:calc(20px * var(--ui));
/* CAPPED, OR THE PINNING HIDES THE BUTTONS IT EXISTS TO KEEP ON SCREEN.
   The deciding column holds the fireteam head, a ~140px card per brother
   with its own accept/refuse row, THE UNBOUND and the notes well: with
   three brothers it is taller than the scrollport at the mockup's own
   1600x1000. Sticky without a height cap pins the TOP of that column, so
   the later men's buttons sit below the fold for the whole scroll -- the
   exact inverse of the reason the column was made sticky. The cap makes
   the column scroll inside itself instead. No phone guard needed:
   .aar-cols > .aar-side is display:contents under 1000px, where position
   is inert. */
max-height:calc(100dvh - calc(40px * var(--ui)));overflow:auto}
/*
 * THE ORDER A PHONE READS AN AAR IN, which is not the order a desk reads it in.
 *
 * Owner, 2026-09-01: "fornthe phone view lets have the fire team at the top
 * then the evidence then the as written then the call then the notes." On a
 * desk the two columns stand side by side and there is no order to argue
 * about; folded into one, the columns simply concatenate -- evidence, filing,
 * notes, then the fireteam and the call underneath -- so the men he is
 * deciding about ended up below everything he had already read.
 *
 * AS WRITTEN IS GONE FROM THAT SEQUENCE and its order:5 with it. Owner,
 * 2026-09-04: "the as written hiddin shit is not needed!! at the bottom." THE
 * RECORD's first column carries what the brother filed, field by field, so the
 * panel was repeating what was already above it behind a click. The gap in the
 * order values is deliberate and costs nothing -- `order` sorts, it does not
 * count -- and leaving it means the panels that remain keep the numbers this
 * comment has always given them.
 *
 * `display: contents` is what makes this possible without a second copy of the
 * markup: the two column wrappers stop generating boxes, their panels become
 * direct children of .aar-cols, and one `order` on each puts them in his
 * sequence. One set of panels, one set of bindings, two readings.
 *
 * The breakpoint is 1000px because that is where `wide` flips in renderVals,
 * and a layout that disagreed with the script about which shape it is in would
 * be worse than either shape.
 */
@media (max-width: 999px) {
  /* CHILD SELECTORS, because both wrappers also carry `.col`, which sets
     display:flex at the same specificity and wins on source order -- measured
     in the browser: a wrapper went to contents and these two stayed flex, so
     nothing reordered and the side column kept a fixed width wider than the
     phone and clipped its own text. */
  .aar-cols:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > .aar-main, .aar-cols:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > .aar-side { display: contents; }
  /* STRETCH AND DO NOT WRAP. .aar-cols is a wrapping row with
     align-items:flex-start, which is right for two columns side by side and
     wrong for one column of panels: measured, the panels came out 433, 433,
     433, 311 and 205 wide inside a 366 phone -- each sized to its own content,
     some overflowing and some stranded. In one column every panel is the width
     of the screen. */
  /* One column. Was flex-direction, which a grid does not read -- with
     .aar-main and .aar-side both display:contents their panels are the
     grid's own children, so the track count is what stacks them. */
  .aar-cols:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) { grid-template-columns: 1fr; align-items: stretch; }
  /* THE AUSPEX WAS LEFT OUT, so on a phone it kept source order and led the
     report -- the machine's reading arriving before the men it is about. It
     belongs directly under the pictures it read. Anything unordered defaults to
     0 and jumps to the front, which is the bug, so every panel is named. */
  .aar-team:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) { order: 1 }
  /* Directly under the men, because that is what it is about: somebody who
     played and whom the ledger cannot name. Above the pictures, because the
     answer is a person on this report and not a detail of the evidence. */
  .aar-unbound:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) { order: 2 }
  .aar-evidence:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) { order: 3 }
  /* THE STRIP UNDER THE PICTURES WAS THE ONE PANEL NOBODY NAMED, and the law
     three lines up is exactly what it broke: unordered, it defaulted to 0 and
     led the phone -- "nobody has read these screenshots yet" arriving above THE
     FIRETEAM, before the screenshots it is about. It draws only when
     dUnchecked is true, which is why it was never seen. Same order as the
     evidence, so source order keeps it directly underneath. */
  .aar-unread:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) { order: 3 }
  /* THE RECORD IS THE CALL, so it keeps the call's place in the owner's phone
     sequence -- "fornthe phone view lets have the fire team at the top then the
     evidence then the as written then the call then the notes". The auspex's
     order:4 went with the panel that carried it: its findings are inside THE
     RECORD now, and a rule naming .aar-auspex would ALSO match this panel (it
     still carries the class for skull.js) and fight this one on source order. */
  .aar-record:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) { order: 6 }
  .aar-notes:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) { order: 7 }
}
/* One brother, one card, because every control on it answers for him alone. */
/* A crew card is a plate that carries its own colours: dbg and dbd are
   utilities and land after the components, so the per-man tint still wins. */
.crew-card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s6);padding:var(--s7) calc(18px * var(--ui)) calc(17px * var(--ui));
transition:border-color var(--t),background var(--t)}
/* A menu hung off a control near the right edge opens inward. */
.pop-right:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){left:auto;right:0}

.lightbox:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;inset:0;z-index:70;display:flex;align-items:center;
justify-content:center;padding:var(--s7);
background:rgba(var(--hue-105,4,8,7),.94);backdrop-filter:blur(6px);
animation:fadein .18s ease}
.lightbox-panel:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;flex-direction:column;gap:var(--s5);
max-width:min(96vw,1500px);max-height:94vh}
/* contain, because this is where the picture is actually read: a tall phone
grab and a wide monitor capture are both shown whole rather than cropped to
a box that suits neither. */
.lightbox-img:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:min(96vw,1500px);height:78vh;background-position:center;
background-repeat:no-repeat;background-size:contain;
border:1px solid var(--gold-line);background-color:var(--bg-deep)}
/* THE BROTHER'S CARD, which is a lightbox holding a panel rather than a
picture. It borrows the overlay and the click-away-to-close, and parts company
after that: a picture wants the whole viewport and a card wants to be a card,
so this one is narrow, has the panel's own ground under it, and scrolls
INSIDE itself rather than growing past the screen. */
/* A BROTHER ON AN AAR, and how far his approval has got. Three of these sit
where the filer's name used to, because an aar is settled by each man on it
being answered for separately -- so the row has to show all three and which of
them are done. Bordered rather than filled: three solid green blocks in a
sixty-row table is a wall, and the point is to be scannable. */
/* THE ONE CONTROL ON A FIRETEAM ROW. Owner, 2026-08-31: "simple checkbox next
to their name that only the sgt or above in THEIR company can click." Ticking
it credits him at what the aar is worth; the cross beside it refuses him and
asks why. It is drawn, not an <input>, for the same reason the WHO MAY ACT grid
is: a native checkbox cannot be given this palette on every browser. */
.tickbox:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;justify-content:center;
width:22px;height:22px;border:1px solid;border-radius:2px;
font-size:calc(13px * var(--ui));line-height:1}
/* .crew-chip is gone. It was the rounded pill a brother wore on a report, and
   it is the thing the owner pointed at when he asked for the roster to look
   like the reports section -- so it became .ident, the plate, and both of its
   users took the plate instead. */
.battle-link:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:inherit;text-decoration:underline;text-decoration-style:dotted;
text-underline-offset:.25em;cursor:pointer}
.battle-link:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.battle-link:focus-visible:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink);text-decoration-style:solid}
.card-panel:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:min(96vw,620px);gap:0;
background:var(--bg);border:1px solid var(--gold-line);
box-shadow:0 18px 60px rgba(0,0,0,.6)}
/* auto-fit rather than a fixed count: the same grid carries six facts on a
desktop and two across on a phone with nothing to switch on. */
.grid-facts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
gap:1px;background:var(--line);border-top:1px solid var(--line);
border-bottom:1px solid var(--line)}
@media (prefers-reduced-motion:reduce){
.lightbox:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none}
.shot:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transform:none}
/* No component in the shared layer has an infinite animation, so this is the
   whole of it. The four perpetual ones already on the site (.scanning, the
   tally scan, the candles, the skull) all say "a machine is working" and that
   meaning is spent; nothing here competes with them. */
.rail:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.rail-act:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.mark:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:none}
/* The forge's entrance is the only animation the shared layer adds, and it
   plays once. It still goes: a reader who asked for no motion asked for none. */
.forge:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none}
}

/* ---- lists: every table on the site is these two rules plus a template ---- */
.grid-hdr:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;align-items:center;gap:9px;padding:var(--s4) var(--s8);
border-bottom:1px solid var(--hair);background:rgba(var(--hue-72,9,18,14),.66);
color:var(--ink-4)}
/* The colour is INHERITED from .grid-hdr above rather than set here, so a
.c-gold on one header cell still wins. Setting it on this selector made it
(0,1,1) and it silently beat every colour utility -- which is how the points
column came out grey. */
.grid-hdr:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>div{font-size:var(--fs-2);font-weight:600;letter-spacing:var(--ls-2);
text-transform:uppercase;white-space:nowrap}
/* The plain-English line under a column heading -- the only thing inside a
header cell that is a SENTENCE rather than a label. The rule above hands every
cell uppercase, tracked-out, non-wrapping label type, which is right for
"APPROVE" and useless for "decide whether a brother's aar counts": that arrived
in capitals on one line and ran straight out of its column. Those three
declarations are inherited rather than matched, so a class on the span itself
undoes them; nothing else in the header changes. */
.cap-note:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){text-transform:none;letter-spacing:normal;white-space:normal;
font-size:var(--fs-2);font-weight:400;line-height:1.35;color:var(--ink-4)}
/* ==========================================================================
   RAIL -- the one list row.

   Every row on the site: a report on the board, a brother in a company, a
   statistics line, an item in a floating menu. One padding, one rule, one
   hover, one selected state, one column for state.

   ORNAMENT AUDIT
     hover: warm ground + 3px ember rail   STATE  the row under your hand. It
            is WARM where everything else is green-black, because the reader's
            own attention is the lamp and light has a source. This is the most
            visible single change in the layer.
     .rail-on   brighter rail + faint ember  STATE  selected / open
     .rail-you  gold rail + a MARK in the margin  WHOSE. Deliberately a
                DIFFERENT SHAPE from .rail-on, not a different brightness:
                "this row is selected" and "this row is me" are two facts, and
                a reader must not have to compare two golds to tell them apart.
     .rail-state fixed column  STATE  a column of states is only scannable if
                it is a column; a state that floats to wherever the name ran
                out is read one row at a time.
   NO RIVETS ON A ROW -- rivets say "panel", and sixty of them say nothing.
   NOTHING HERE PULSES: a row is being read.

   .grid-row is GONE, not aliased. Aliasing it onto a bare .rail-grid would
   have taken the board's padding and bottom rule away silently, so .rail-grid
   carried the whole body instead and the two ran independently until every
   table had moved. All four have, so the second name has nothing left to
   protect and the rule is deleted rather than kept out of caution.
   ========================================================================== */
.rail:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;display:flex;align-items:center;gap:var(--s4);
min-width:0;padding:var(--s4) var(--s8);
border-bottom:1px solid var(--rule);
box-shadow:inset 3px 0 0 transparent;
transition:background var(--t),box-shadow var(--t),color var(--t)}

/* The table shape -- a rail laid out in columns. --dcols supplies them. */
.rail-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;align-items:center;gap:9px;padding:var(--s4) var(--s8);
border-bottom:1px solid var(--rule)}

/* THE TIGHT ROW. A company panel is CONTEXT beside the work, so a man there is
   one line high. This is .cmd-row's own metric, kept. */
.rail-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s2) var(--s3);gap:var(--s3);border-bottom-color:transparent}

.rail-act:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){cursor:pointer}
.rail-act:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--lift);box-shadow:inset 3px 0 0 var(--gold-dim)}

.rail-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel-3);
box-shadow:inset 3px 0 0 var(--gold-lit),0 0 20px -12px var(--glow-live)}
.rail-you:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel-3);box-shadow:inset 3px 0 0 var(--gold)}
.rail-owed:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:inset 3px 0 0 var(--warn)}
.rail-spent:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-5)}

/* .rail-act:hover is (0,2,0) and .rail-you is (0,1,0), so without these three
   the hover wins and YOUR OWN ROW DIMS under the hand -- the "whose" mark
   getting fainter exactly when you reach for it, which is backwards. */
.rail-you.rail-act:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:inset 3px 0 0 var(--gold-lit)}
.rail-on.rail-act:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:inset 3px 0 0 var(--gold-lit),0 0 20px -12px var(--glow-live)}
.rail-owed.rail-act:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:inset 3px 0 0 var(--warn)}

/* min-width, not width: "YOU" + "Battle-Brother" runs to about 145px and a
   fixed 96px column drives it back into the name in a 380px aside. */
.rail-state:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;display:flex;align-items:center;justify-content:flex-end;
gap:var(--s3);min-width:calc(96px * var(--ui))}
@media (max-width:999px){.rail-state:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:auto}}

/* The affordance that appears under the hand -- opacity only, so a screen
   reader and a touch device never lose it, and :focus-within so a keyboard
   can reach it, which .co-slot's version could not. */
.rail:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .on-hover{opacity:0;transition:opacity var(--t)}
.rail:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .on-hover,.rail:focus-within:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .on-hover{opacity:1}

.empty:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;flex-direction:column;align-items:center;justify-content:center;
gap:var(--s5);padding:calc(60px * var(--ui)) var(--s8);text-align:center}

/* A shape, a line, and what to do next. The mark's job is BOUNDARY: something
   belongs in this space and is not here. It is UNLIT on purpose -- an empty
   state must never be the brightest thing on a screen, or absence reads as an
   event. The only light in the block is on the button, if the reader may act
   at all. (No wash behind the glyph: the border already bounds it and the
   glyph already says "missing", so a gradient there would mark nothing.) */
.empty-mark:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;justify-content:center;flex:none;
width:calc(44px * var(--ui));height:calc(44px * var(--ui));
border:1px solid var(--line-2);
font-family:var(--serif);font-size:var(--fs-8);line-height:1;color:var(--ink-6)}
.empty-title:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:var(--fs-7);
letter-spacing:var(--ls-2);color:var(--ink-3)}
.empty-line:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){max-width:46ch;font-size:var(--fs-6);line-height:1.45;color:var(--ink-4)}

/* ---- the three text patterns typed out 39 times between them ---- */
.label:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-2);font-weight:600;letter-spacing:var(--ls-key);
text-transform:uppercase;color:var(--gold-ash)}
.navtab:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:calc(10.5px * var(--ui));font-weight:400;
letter-spacing:var(--ls-2);text-transform:uppercase;
/* The underline and the space above it were three inline declarations on
   the tab itself, none of them a d* hook. The colour still arrives on --dbd. */
padding-bottom:3px;border-bottom:1px solid transparent}
.navtab-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-3);font-weight:600;letter-spacing:var(--ls-1);
text-transform:uppercase}
.meta:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-2);letter-spacing:var(--ls-meta);
color:var(--ink-4)}
.meta-ash:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-2);letter-spacing:var(--ls-meta);
color:var(--gold-ash)}
/* The glow is part of the heading and was lost when the first version left it
out: an agent converting the report screen folded five headings onto this
class and reported the loss rather than quietly keeping it inline. */
.heading:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:var(--fs-4);font-weight:600;
/* 12px, AND THE SUB CAME DOWN WITH IT. The old note here refused 12 because
   .plate-head-sub -- the half-line that EXPLAINS a heading -- was 13px, so a
   12px title would have been optically smaller than its own subtitle. The
   refusal was about the PAIR, not about 12: the sub is --fs-3 now and the
   title is --fs-4, so the hierarchy the note protects is intact and 12 is
   free. Move one of these two and you have to move the other. */
text-transform:uppercase;
/* SAID IN FULL CAPS, not left to the family. This used to lean on Cinzel
   rendering lowercase as small capitals; Cormorant Garamond does not, so the
   declaration is now the only thing making a Title Case binding match its
   sixteen full-caps siblings -- and it was already the right way to say it. */
letter-spacing:var(--ls-head);color:var(--gold);
text-shadow:0 0 14px rgba(216,180,90,.35)}
.wordmark:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:calc(15px * var(--ui));font-weight:500;
/* 15px and rgb(var(--hue-92,233,239,228)) are the mockup's, and neither is on a scale: --fs-7 is 16px
   and --ink is rgb(var(--hue-67,228,235,226)), one step cool of the wordmark's own white. */
letter-spacing:var(--ls-brand);color:rgb(var(--hue-92,233,239,228))}

/* ==========================================================================
   ROUND THREE — new components, not conversions.

   Meant to be appended into ledger.css's COMPONENTS region (before the
   UTILITIES block), the same place .card/.pop/.totals/.bar live, so that a
   utility class placed alongside one of these still wins. Nothing in this
   file writes a raw hex or rgba() colour and nothing sizes text off a raw
   px value -- every colour and font size below is one of the tokens already
   declared in :root. No @keyframes name here collides with an existing one
   (led, shimmer, scanv, rise, glow, fadein, spin, enter, bootmark, bootline,
   track, wipe, tickin, glowtext, hairline, reticle, emberrise, emberglow,
   flame, halo, breathe, phosphor) -- this file's two are quotein/quoteout.
   ========================================================================== */

/* ==========================================================================
   THE QUOTE FOOTER

   Owner: "a fade in then fade and slide out making room for another to fade
   in", with "a blur and acceleration effect". One line rotates every 60s;
   the strip is atmosphere, so it gets a fixed height (no page jump when a
   longer quote replaces a shorter one) and stays out of the interface's way.

   .quote-in and .quote-out are the two overlapping instances themselves --
   each is an inset:0 layer inside .quote, which is why .quote only needs
   position + a real height and neither instance needs its own class for
   that. The entrance reuses the sheet's own decelerating settle curve
   (cubic-bezier(.2,.8,.2,1), the same one .anim-enter already uses); the
   exit reuses the sheet's own accelerating "pulled away" curve
   (cubic-bezier(.5,0,.75,0), the same one every .boot-leaving reversal
   already uses) rather than inventing a third feel for the same motion.
   ========================================================================== */
/*
 * THE SHELL IS AS TALL AS THE VISIBLE WINDOW, NOT AS TALL AS 100vh.
 *
 * Owner, 2026-09-01: "the buttons at the bottom dont exist just the underline
 * are they under the cool quote bar?"
 *
 * They were under the URL BAR. On a phone `100vh` is the viewport with the
 * browser's chrome HIDDEN -- the biggest it ever gets -- so while the address
 * bar is showing, the bottom of a 100vh column sits below the glass. The nav
 * strip is the last child, so it went first, and its top border was the last
 * thing still on screen: exactly "just the underline".
 *
 * `100dvh` is the viewport as it is RIGHT NOW and resizes as the chrome slides
 * away. The 100vh line stays above it as the fallback for a browser that does
 * not know dvh, which gets today's behaviour rather than a collapsed page.
 *
 * And the strip clears the home indicator. A row of buttons a thumb cannot
 * reach without hitting the system gesture area is a row of buttons that
 * misfires, so the inset is added to its padding rather than assumed to be
 * zero.
 */
.shell:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;flex-direction:column;overflow:hidden;background:rgb(var(--hue-104,11,18,16));
  height:100vh;height:100dvh}
.navbar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-bottom:env(safe-area-inset-bottom,0px)}

.quotebar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;flex:none;width:100%;overflow:hidden;
  /* TALL ENOUGH FOR THE TWO LINES IT ALLOWS. The cross-fade needs both quotes
     absolutely positioned over one another, so the bar cannot grow to its
     content and the height has to be stated. 56px fits one line and the
     attribution; a phone wraps far more often than a desk does, so it gets the
     second line's worth. Measured at 390x664: two lines at this size plus the
     attribution need 78. */
  height:calc(56px * var(--ui));
  padding:var(--s2) var(--s9);
  border-top:1px solid var(--hair-3);
  background:var(--bg-deep)}

/*
 * ══ THE CANDLES ═══════════════════════════════════════════════════════════
 *
 * Owner, 2026-09-01: "i do wanna generate some candles at the bottom of the
 * page to start turning it into a gothic feel."
 *
 * A rank of votive candles burnt down to different heights, along the bottom
 * edge of the quote bar. Drawn, not generated: no image, no request, and they
 * survive any theme change because they are made of the palette's own colours.
 *
 * IT MARKS A BOUNDARY, which is the rule everything ornamental here has to
 * meet -- it says the page ends here, the way candles at the foot of a shrine
 * say the same thing. It sits BEHIND the quote (z-index 0 against the quote's
 * 1) and takes no clicks, so it can never cost a reader a word.
 *
 * WHAT KEEPS IT FROM LOOKING CHEAP is that no two flames agree. Real candles
 * never flicker in unison, and a rank that pulses together reads instantly as
 * one animation applied N times. So every candle gets its own duration AND its
 * own delay, deliberately not multiples of each other, and its own height.
 */
.candles:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:0;right:0;bottom:0;z-index:0;height:26px;
  display:flex;align-items:flex-end;justify-content:space-between;
  pointer-events:none;padding:0 var(--s9)}
/* GROUPED, NOT RANKED. Owner: "too many of them theres shold be smaller fewer
   clusters of them", then "3-5 of different sizes in 2 clusters on the left and
   right side." Twenty-two evenly spaced candles read as a fence. Two groups
   flanking the quote read as candles someone lit, and they frame the line
   instead of running under it. Four on the left, five on the right -- matched
   pairs look placed, uneven ones look lit. */
.cluster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:flex-end;gap:4px}
.candle:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;width:3px;border-radius:1px 1px 0 0;
  /* Wax, lit from its own flame: pale at the top, lost in the dark by the
     bottom, so the rank fades into the edge rather than sitting on a line. */
  background:linear-gradient(180deg,
    rgba(232,219,188,.52) 0%, rgba(176,158,118,.32) 45%, rgba(94,84,62,.08) 100%)}
/* The wick's flame. Not a circle -- a teardrop, wide at the base and drawn to a
   point, which is the shape the eye actually reads as fire. */
.flame:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:50%;bottom:100%;width:4px;height:7.5px;
  margin-left:-2px;border-radius:50% 50% 45% 45% / 62% 62% 38% 38%;
  background:radial-gradient(ellipse at 50% 72%,
    #FFF2CE 0%, #F6D98A 26%, #D8A63C 55%, rgba(190,120,30,.55) 78%, transparent 100%);
  transform-origin:50% 100%;
  animation:flicker 2.3s ease-in-out infinite}
/* The light it throws. Kept wide and very faint: this is what makes the bar
   feel lit from below instead of having stickers on it. */
.flame:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{content:"";position:absolute;left:50%;top:50%;width:30px;height:26px;
  margin:-13px 0 0 -15px;border-radius:50%;
  background:radial-gradient(circle, rgba(232,176,74,.20) 0%, rgba(216,150,50,.07) 45%, transparent 72%)}

@keyframes flicker{
  0%,100%{transform:scaleY(1)    scaleX(1)    translateY(0);   opacity:.92}
  18%    {transform:scaleY(1.14) scaleX(.93)  translateY(-.5px);opacity:1}
  37%    {transform:scaleY(.9)   scaleX(1.05) translateY(.4px); opacity:.8}
  58%    {transform:scaleY(1.08) scaleX(.96)  translateY(-.3px);opacity:.97}
  79%    {transform:scaleY(.95)  scaleX(1.02) translateY(.2px); opacity:.86}}

/* Burnt down unevenly, and the two clusters share no silhouette. */
.cluster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(1){height:15px}
.cluster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(2){height:8px}
.cluster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(3){height:19px}
.cluster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(4){height:11px}
.cluster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(5){height:22px}
.cluster:nth-child(2):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(1){height:10px}
.cluster:nth-child(2):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(2){height:17px}
.cluster:nth-child(2):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(3){height:9px}
.cluster:nth-child(2):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(4){height:21px}
.cluster:nth-child(2):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .candle:nth-child(5){height:13px}
/* OUT OF STEP, TWELVE WAYS. Duration comes from the cluster and delay from the
   candle's place in it, so every flame gets its own pair without twelve rules.
   The numbers share no factors -- a rank that flickers together reads instantly
   as one animation applied N times, which is what makes this look cheap. */
.cluster:nth-child(1):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .flame{animation-duration:2.9s}
.cluster:nth-child(2):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .flame{animation-duration:2.3s}
.candle:nth-child(1):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .flame{animation-delay:-.3s}
.candle:nth-child(2):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .flame{animation-delay:-1.7s}
.candle:nth-child(3):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .flame{animation-delay:-1.1s}
.candle:nth-child(4):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .flame{animation-delay:-2.3s}
.candle:nth-child(5):where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .flame{animation-delay:-.9s}
/* A guttering candle is still a lit candle. Reduced motion stops the movement
   and keeps the light. */
@media (prefers-reduced-motion: reduce){ .flame:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none} }

/*
 * ══ THE SERVITOR CONSOLE ══════════════════════════════════════════════════
 *
 * Owner, 2026-09-04: "in my downloads i made a console. I want that to sit down
 * in the bottom right of the page big enough for the servo skull to work on
 * next to the candles but to the left of them quite a bit. when a new aar comes
 * in i want that to light up with green matrix type text in the black part of
 * the console."
 *
 * A gothic CRT in a rusted iron frame, standing on the quote bar in the bottom
 * right. It is DEAD until a report arrives -- and dead means the picture
 * exactly as it was drawn, with no filter and no dimming over it, because the
 * artwork already IS a dead machine. What an AAR adds is light: see console.js,
 * which drives --lit and paints the rain.
 *
 * IT STANDS ON THE QUOTE BAR AND NEVER ON IT. The bar is the one strip this
 * sheet protects four separate times -- an opaque ornament reaching over it
 * would cost a reader a word of the quote -- so the feet land exactly on its
 * top edge and the bar, its candles and its line are a plinth underneath. The
 * relationship is named here rather than shared through a token, because the
 * bar's own height was set by the pixel-parity pass and is not ours to move.
 */
:root{
  /*
   * A TINY BIT BIGGER THAN HIM, AND NOTHING ELSE DECIDES IT.
   *
   * Owner, seeing the first one: "lol that console is WAY too huge" ... "it
   * needs to be a tiny bit bigger than the servo skull."
   *
   * He is SKULL_PX = 105px tall on screen (core/../skull.js), and that number
   * is a constant in the rig rather than a token, so this must NOT scale with
   * --ui or the two drift apart at the one size that matters -- standing next
   * to each other.
   *
   * The picture is 768x600, so height is width x 0.78125. 164px was the first
   * try -- 128px tall, 1.22x his height -- and the owner, seeing it on the real
   * page: "the console is still a little too big shrink by 20%". So 131px wide,
   * 102px tall, which is almost exactly his own 105px: the two of them stand
   * shoulder to shoulder, and the fixture reads as a lectern he works at rather
   * than a machine he is dwarfed by.
   *
   * The glass at 54.7% is 72px across and 41px down. Still four columns of rain
   * -- the glyph cell is 14px -- and ">> AAR RECEIVED" still fits, because the
   * rain draws at a size derived from the glass rather than a fixed one.
   *
   * IF SKULL_PX MOVES, MOVE THIS. That is the whole relationship.
   */
  --con-w:131px;
  /* Six gutters leaves the console clear of the corner flames. */
  --con-r:calc(var(--s9) * 6);
  /*
   * ON THE FLOOR OF THE BROWSER. Owner: "the bottom of the terminal should be
   * on the bottom of the browser."
   *
   * It has walked down twice: 56px, which was the quote bar's own height and
   * put its feet on the bar's top edge with nothing over the bar at all; then
   * 36px, a fifth of the way in. Zero is where it was always going -- the
   * machine stands on the floor of the page and the quote bar runs behind it,
   * which is what a fixture in a room looks like. Nothing is lost behind it:
   * the bar is 56px, the plinth covers 131px of width out of the whole page,
   * and the quote itself is centred a long way to its left.
   */
  --con-b:0px;
}
/*
 * CROPPED BY THE BOX, NOT BY A CLIP PATH. background-size:100% auto with the
 * position at 0 0 draws the picture --con-w SQUARE from the top-left corner,
 * and the box is only 600/768 of that tall -- so image rows 600 to 768, the
 * cabinet plinth there is no room for, fall outside the padding box and are
 * never painted at all. The mask takes the last thirty rows down to nothing so
 * the machine sinks into the page instead of ending on a cut line.
 */
/* ABOVE THE QUOTE BAR, BELOW EVERYTHING THAT MATTERS. 22 clears .quote (1) and
   the bar itself so the plinth draws over them, and stays under .pop (30), the
   toast (40), the skull (55) and the phosphor/bezel (60/61) -- a popup, a
   message and the servitor himself all still come out in front of it. */
.aar-console:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;z-index:22;pointer-events:none;isolation:isolate;
  --lit:0;--pop:0;
  right:var(--con-r);bottom:var(--con-b);
  /*
   * THE WHOLE PICTURE, BASE AND ALL. This was `* 600 / 768` -- the top 600 rows
   * of a 768x768 asset -- so that the plinth ran off the bottom and the machine
   * appeared to sink into the page. That was right while it sat behind the
   * quote bar. It now draws OVER the bar, and the owner saw the consequence at
   * once: "the console is missing its base." The crop was cutting rows 600 to
   * 767, which is 22% of the machine and every part of it that meets the floor.
   *
   * Square, because the asset is. Every percentage below moved with it -- they
   * were fractions of the 600 crop and are fractions of 768 now.
   */
  width:var(--con-w);height:var(--con-w);
  background:var(--console-art, none) 0 0/100% auto no-repeat;
  /* A breath of a fade at the very foot so it meets the bar rather than ending
     on a line. 2%, not the 5% that was hiding the plinth. */
  -webkit-mask-image:linear-gradient(180deg,#000 0 98%,transparent 100%);
          mask-image:linear-gradient(180deg,#000 0 98%,transparent 100%)}
/*
 * z-index 20 puts the machine above the embers, the candles and the quote, and
 * BELOW .pop (30) so a dropdown opening into the corner is never crossed and
 * below the toast (40) so a message to a reader always wins. He himself flies
 * at 55, so he is drawn in front of it -- which is the whole tableau: we watch
 * him work, over his shoulder.
 */

/*
 * THE GLASS, MEASURED OFF THE FILE RATHER THAN GUESSED. The tube's inner edge
 * traced at pixel resolution sits at x[176,596], y[152,391] of the 768px
 * picture. Against the element that is left 176/768, width 420/768, and -- the
 * box being cropped at row 600 -- top 152/600 and height 239/600.
 */
.aar-crt:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;overflow:hidden;
  /* The glass, traced at x[176,596] y[152,391] of the 768px asset. Vertical
     figures are of 768 now that the whole picture shows; the horizontal pair
     never changed, because the width never was cropped. */
  left:22.917%;top:19.792%;width:54.688%;height:31.120%;
  border-radius:4.8% / 8.4%;
  /* The phosphor wash behind the glyphs, and the spill onto the bezel. rgb(var(--hue-3,123,232,168))
     is the green the skull's own fill light already throws at this page. */
  background:radial-gradient(118% 130% at 50% 42%,
    rgba(var(--hue-3,123,232,168),calc(.11 * var(--pop,0))) 0%,
    rgba(var(--hue-3,123,232,168),calc(.04 * var(--pop,0))) 58%, transparent 100%);
  box-shadow:0 0 calc(20px * var(--pop,0)) rgba(var(--hue-3,123,232,168),calc(.20 * var(--pop,0)))}
/*
 * THE LIGHT DIES UNDER THE FRAME. This is the piece that actually sells "inside
 * the tube": the bezel's own shadow continued inward, so the rain falls off
 * beneath the metal instead of stopping flat at a rectangle. It has to be the
 * pseudo-element and not an inset shadow on .aar-crt itself, because an inset
 * shadow paints under the element's children and the canvas is a child.
 *
 * There are deliberately NO scanlines here. .phosphor already lays them over
 * the whole viewport at 3px, above both the console and the skull, so a second
 * set at a second pitch would only beat against the first.
 */
.aar-crt:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;
  box-shadow:inset 0 0 13px 4px rgba(0,0,0,.82), inset 0 2px 6px rgba(0,0,0,.9)}
/*
 * mix-blend-mode:screen is what makes the rain live IN the glass: the tube's
 * baked specular sweep and its vignette survive underneath it, and a black
 * pixel is the identity under screen, so the trails simply stop contributing as
 * they decay. The blend is confined to the machine because .aar-console is
 * position:fixed with a z-index and therefore already an isolated stacking
 * context -- isolation:isolate up there says so out loud rather than leaving it
 * to be inferred. The radial mask kills the rain before it reaches the corners,
 * which both matches the tube's own curvature and hides the pixel of
 * registration error that would otherwise show as a green line on the iron.
 */
.aar-rain:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;inset:0;width:100%;height:100%;
  opacity:var(--lit,0);mix-blend-mode:screen;
  -webkit-mask-image:radial-gradient(118% 128% at 50% 45%,
    #000 58%,rgba(0,0,0,.55) 84%,transparent 100%);
          mask-image:radial-gradient(118% 128% at 50% 45%,
    #000 58%,rgba(0,0,0,.55) 84%,transparent 100%)}
/*
 * THE WORKING SURFACE, PUBLISHED AS AN ELEMENT so that skull.js never has to
 * hold a number about this machine. It measures this box and puts his ARM TIPS
 * at its centre; everything else about where he hovers follows from the model.
 * Image x[144,624], y[462,558] -- the key deck, centred under the tube.
 */
/* The keys he stands at. Vertical figures rebased on 768 with the crop gone --
   77% and 16% of the old 600 are 60.156% and 12.5% of the whole. */
.aar-console-deck:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:18.75%;top:60.156%;width:62.5%;height:12.5%}
/* THE SAME LINE THE SKULL KEEPS, in both the places he keeps it: skullWanted()
   refuses anything under 1000px and .skull-toggle disappears there. A console
   exists FOR him to work at, and below this there is nobody to work it -- an
   AAR arriving still announces itself in the reports list. The height floor is
   the other half of the same judgement: on a short window the machine would
   swallow the page rather than stand in the corner of it. */
/* THE COMPANY GOES LAST ON A PHONE. Owner: "in the stratigum id like to put
   the company below everything else. Just want to see what I need to do."

   Side by side it is a column beside the work and reads as context, which is
   why it was put first -- but a phone stacks this row, and stacked it becomes
   a hundred-odd brothers standing between the reader and ASKED OF YOU. His
   own work is what the screen is for; the roll of the company is what he
   scrolls to when he wants it.

   ORDER, NOT A SECOND COPY OF THE MARKUP. .row-stretch is a flex row, so one
   declaration moves the whole panel and the desktop layout -- where the
   company still leads -- is untouched. */
/* A SUMMARY IS ONE LINE ON A PHONE. Owner: "I like the summaries but their way
   too big."

   Each tile is a three-line stack -- a label, a 28px figure, and a note under
   it -- which is right beside four of its kind on a wide screen and is most of
   the first viewport on a narrow one. Laid on its side the same tile is a
   label and a number on one line, and the four of them take two rows instead
   of eight.

   THE NOTE IS WHAT GOES, and nothing else. It is the line that explains the
   figure -- "Matches the filters below", "1 active vox channel" -- which is
   worth its room beside a column of numbers and is not worth a phone's screen
   under one. The FIGURES all stay, which is what was asked for.

   This rule once had to spare a hand as well: `gone quiet` was a tile carrying
   a control, drawn with the same .t4 the notes use. That tile is gone, so no
   tile holds anything but text and the exception went with it. If one ever
   does again, mark the control and spare it by name -- do not widen this to
   the class, or a hand will disappear on a phone and nothing will say so. */

/* The number drops from --fs-9 to --fs-6 because it no longer has a line to
   itself and must sit on the baseline of the words beside it. */
@media (max-width: 999px){
  .total:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1 1 auto;min-width:0;flex-direction:row;flex-wrap:nowrap;
    align-items:baseline;gap:var(--s4);
    padding:calc(7px * var(--ui)) var(--s6)}
  .total:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > .t4{display:none}
  .total:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .total-n{font-size:var(--fs-6)}
  /* THE LABEL GIVES, THE FIGURE NEVER DOES. Owner: "the summary for mobile on
     roster needs to be styled like reports" -- and the two were already the
     same rule. What differed was the words: the reports bar's longest label is
     `awaiting auspex`, the roll's is `brothers on roster`, and a nowrap label
     that long pushed its own figure onto a second line. One tile on the roster
     was two lines high while every tile on reports was one, which is the whole
     of what he was looking at.
     So the row does not wrap at all now and the LABEL is what runs out of
     room, ellipsised, because a summary whose number has been pushed out of
     sight is not a summary. Reports is untouched by this: nothing there is
     long enough to reach the ellipsis. */
  .total:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
    min-width:0;flex:0 1 auto}
  .total:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .total-n{flex:none}
}
@media (max-width: 999px){ .cmd-aside:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){order:9} }
@media (max-width: 999px){ .aar-console:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none} }

/*
 * WHAT HE IS READING, SAID OUT LOUD. Owner: "lets have that little guy when he
 * typing too put in a small chat bubble the name of the brothers, the operation
 * or mission and outcome."
 *
 * Positioned entirely from skull.js -- it rides his own coordinates -- so
 * everything here is paint. Fixed, because he is; pointer-events none, because
 * he is an ornament and a sergeant must never have to click past him.
 */
.skull-say:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;left:0;top:0;z-index:21;display:none;
  pointer-events:none;max-width:310.5px;
  padding:calc(8.05px * var(--ui)) calc(11.5px * var(--ui));
  border:1px solid var(--rule-head);border-radius:var(--r2);
  background:linear-gradient(180deg,rgba(var(--hue-57,19,34,27),.96),rgba(var(--hue-77,11,20,16),.96));
  box-shadow:0 10px 26px -12px rgba(0,0,0,.9),
             inset 0 1px 0 rgba(var(--hue-23,228,240,226),.06);
  /* The tail. Drawn on the RIGHT because the bubble sits to his left. */
  --tail:10px}
.skull-say:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{content:"";position:absolute;right:-6px;bottom:12px;
  width:10px;height:10px;transform:rotate(45deg);
  background:rgba(var(--hue-78,13,23,18),.96);
  border-right:1px solid var(--rule-head);border-top:1px solid var(--rule-head)}
.skull-say[data-side="right"]:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{left:-6px;right:auto;border-right:0;border-top:0;
  border-left:1px solid var(--rule-head);border-bottom:1px solid var(--rule-head)}
/* --ink-1 WAS NEVER DECLARED. The ladder is --ink then --ink-2..--ink-6, so
   this resolved to nothing and the title fell back to whatever it inherited --
   it has never once been the colour written here. --ink is the top of that
   ladder and what a title beside an --ink-4 line wants. */
.skull-say-k:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:calc(var(--fs-5) * 1.15);color:var(--ink);
  line-height:1.25;letter-spacing:.01em}
.skull-say-v:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:calc(var(--fs-2) * 1.15);color:var(--ink-4);
  line-height:1.5;letter-spacing:.02em;margin-top:2px}
/* The verdict of the PREVIOUS report, set quieter and ruled off, so it cannot
   be misread as a judgement on the one he is working now. */
/* --rule-2 WAS NEVER DECLARED EITHER, and an undefined colour in a border
   shorthand does not vanish -- it falls back to currentColor. So the rule that
   exists to set the previous verdict QUIETER apart has been drawing itself in
   the text's own colour, the loudest line in the bubble. --rule is the quiet
   one this asked for. */
.skull-say-v:last-child:not(:nth-child(2)):where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-5);margin-top:5px;
  padding-top:5px;border-top:1px solid var(--rule)}
/* HIS OWN LINES ARE NOT A DATA READING. The reading below him is dense --
   a title and up to four fields -- and 13px is what fits it. A quip is one
   short sentence with a temper, the owner asked for it bigger ("the quotes are
   a little too small"), and one sentence can afford the width and the size that
   four fields cannot. */
.skull-say-q:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:calc(var(--fs-7) * 1.15);color:var(--ink);
  line-height:1.3;letter-spacing:.01em}
.skull-say-big:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){max-width:391px}
@media (max-width: 999px){ .skull-say:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none!important} }
@media (prefers-reduced-motion: reduce){ .skull-say:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:none} }
@media (max-height: 699px){ .aar-console:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none} }
/* A dead screen that lights is still a screen that lights. Reduced motion takes
   the rain's movement away -- console.js never starts a frame loop at all and
   paints one still screenful instead -- and keeps the light, which is the
   candles' own rule directly above. The cross-fade is what is left of it. */
@media (prefers-reduced-motion: reduce){
  .aar-rain:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:opacity .24s ease}
  .aar-crt:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:box-shadow .24s ease}
}

/* THE QUOTE RIDES ABOVE THE RANK, and carries its own darkness with it.
   Measured at 1440: a flame landed squarely on the "40K" of the attribution.
   Ornament never gets to cost a word, so the quote lays down a soft pool of the
   deep background under itself -- the candles still burn through at the edges,
   the letters stay crisp, and nothing had to be moved or removed. */
.quotebar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > .quote{position:relative;z-index:1;
  background:radial-gradient(60% 130% at 50% 55%,
    rgba(var(--hue-104,11,18,16),.94) 0%, rgba(var(--hue-104,11,18,16),.82) 45%, rgba(var(--hue-104,11,18,16),0) 100%)}

/*
 * THE OPEN REPORT'S HEADER, ON A PHONE.
 *
 * Owner: "we see awaiting judgment back and worth its too tall and takes up too
 * much screen." It is sticky, so whatever it costs it costs on every scroll --
 * and it was costing three wrapped rows before a single screenshot.
 *
 * The state word drops to the size of a heading rather than a title, the
 * sentence under it goes on the same line, and the padding halves. Nothing is
 * removed: the word, the reason, the worth and the buttons are all still there
 * -- they stop being a title page.
 */
/* THE RULES THAT ANSWER THIS ARE WITH THE REST OF THE JUDGING PAGE'S, far
 * below, and they have to be: a media query adds no specificity, so a phone
 * override for .aar-head written HERE would be silently overruled by
 * .aar-head's own base rule declared later in the sheet. The two selectors
 * that used to sit here named a `.serif` and a `.row.g6` that this header has
 * not held since it was rebuilt -- both matched nothing at any width.
 */

/*
 * ══ THE SERVO-SKULL'S CANVAS ══════════════════════════════════════════════
 *
 * It sits UNDER the phosphor and the bezel (60 and 61), so the screen's own
 * grain falls over it and it reads as being inside the machine rather than
 * pasted on top of it. It takes no pointer events at all -- the rule the whole
 * of this file keeps is that ornament never costs a reader a click.
 */
.skull-host:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;inset:0;z-index:55;pointer-events:none}
.skull-host:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) canvas{position:absolute;inset:0;width:100%;height:100%}

/*
 * THE AUSPEX BEAM: A FIELD, AND A CONE THAT THROWS IT.
 *
 * Owner, 2026-09-02: "the beem needs to be wide then scan across something like
 * reading a barcode from top to bottom."
 *
 * So it is not a pointer. The CONE fans from the lens down onto the thing, and
 * the FIELD lies over the thing's own bounds with a bright bar travelling down
 * it -- a document being read, not a laser being aimed. Neither can be baked
 * into the model: both are sized and placed from the target's rectangle, which
 * nothing knows until the moment it is chosen.
 */
.skull-reading:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.skull-reading:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) *{
  text-shadow:0 0 2px rgba(var(--hue-20,158,255,205),.8),0 0 6px rgba(var(--hue-17,112,232,168),.5),
    0 0 12px rgba(var(--hue-13,100,220,158),.15)}
.skull-field:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:0;top:0;pointer-events:none;opacity:0;
  transition:opacity .18s ease;overflow:visible}
/* NO BACKGROUND HERE AT ALL. It used to carry the area glow, but this element
   is overflow:hidden and exactly the size of the text -- so a gradient reaching
   260% of its height was cut off flat at the box, which is the square the owner
   saw. All it does now is CLIP the travelling bar. The glow belongs on .skull-
   pool below, which has no clip and room to fade out properly. */
/*
 * IT LIGHTS THE WORDS, NOT A BOX.
 *
 * Owner: "his scanner lights up a square can it light up JUST the text and not
 * the square?" So the field itself is now invisible -- no border, no tint. All
 * it does is CLIP the travelling bar to the glyph bounds.
 *
 * The bar carries a backdrop-filter, which brightens whatever is BEHIND it
 * rather than painting over it. On this page the ground is nearly black and the
 * words are pale, so raising the brightness of everything under the bar shows up
 * almost entirely on the letters -- the text lights as the bar passes and the
 * background barely moves. mix-blend-mode cannot do this: the canvas is a fixed,
 * z-indexed layer and so an isolated stacking context, and a blend would only
 * ever reach the other ornaments inside it.
 */
.skull-field:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{content:"";position:absolute;left:0;right:0;height:13px;
  top:var(--scan,0%);margin-top:-6px;
  /*
   * SOFT ON ALL FOUR SIDES, AND IT HAS TO BE THE MASK THAT DOES IT.
   *
   * Owner: "but it looks like a square." It did, and the background gradient
   * was never going to fix it: BACKDROP-FILTER BRIGHTENS THE WHOLE OF AN
   * ELEMENT'S BOX, evenly, and a background only tints on top of that. So the
   * colour faded at the ends while the BRIGHTENING stopped dead -- a hard
   * rectangle of lit page with a soft-coloured bar sitting inside it.
   *
   * A mask does modulate the backdrop-filter, so the fade has to live there.
   * Two of them, intersected: one across the bar and one along it, which takes
   * the brightening itself to nothing on every edge.
   */
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 38%,#000 62%,transparent),
                     linear-gradient(90deg,transparent,#000 18%,#000 82%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 38%,#000 62%,transparent),
             linear-gradient(90deg,transparent,#000 18%,#000 82%,transparent);
  -webkit-mask-composite:source-in;
  mask-composite:intersect;
  backdrop-filter:brightness(2.9) saturate(1.5) hue-rotate(-18deg);
  -webkit-backdrop-filter:brightness(2.9) saturate(1.5) hue-rotate(-18deg);
  background:linear-gradient(90deg,
    transparent, rgba(var(--hue-7,170,255,205),.22) 14%, rgba(var(--hue-7,170,255,205),.22) 86%, transparent);
  box-shadow:0 0 16px rgba(var(--hue-42,130,255,190),.45)}

/* ACROSS, NOT DOWN. A row of a table is read top to bottom; a line of text is
   read left to right, so the bar turns ninety degrees and travels the other
   way. Same field, same cone, different grain. */

/*
 * BURNING IT IN, rather than reading it.
 *
 * Owner: "i would LOVE for the part of the quote that gets written following
 * the scanner it glows a tiny bit like it was burned into the background maybe
 * even with a tiny bit of smoke TINY bit. but like he burns it in leaving the
 * text?"
 *
 * So the two halves of a quote change no longer look alike. ERASING keeps the
 * cold green auspex light. WRITING runs the same bar hot -- the backdrop-filter
 * pushes brightness far harder and swings the hue toward ember, so the letters
 * flare orange-white exactly as they appear.
 *
 * ::before is the COOLING TRAIL: everything already written, tinted warm and
 * falling off with distance from the edge, so what he laid down a moment ago is
 * still glowing faintly and what he wrote first has gone cold.
 */

/*
 * THE AUSPEX PANEL IS WRITTEN, NOT FOUND.
 *
 * Its body is clipped away from the moment a report opens and drawn back in
 * left to right under the beam, so the finding appears as the skull reads it.
 * The class is only ever added by skull.js, and only when he is actually coming
 * -- so with no skull loaded (a phone, reduced motion, no WebGL, or simply no
 * ?skull=1) this rule never applies and the panel is just there, as it always
 * was. An ornament may not be the reason a reader cannot see a finding.
 */
/* IT WRITES IN THE FINDINGS, NOT THE RECORD. This used to name `> .col`, which
 * was the auspex panel's only body. THE RECORD carries three bodies now -- the
 * table, the findings and the override footer -- and `> .col` would clip all
 * three, so the six fields a sergeant opened the report to read would be blank
 * for the length of a flight. The findings are the part the skull actually
 * produces, so the findings are the part it draws in. .auspex-say is a direct
 * child of the plate: the sc-if around it compiles to a Fragment and emits no
 * box, so `>` passes straight through it. */
.aar-auspex.skull-writing:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > .auspex-say{clip-path:inset(0 var(--aw,0%) 0 0)}

/*
 * THE SWITCH, AND WHAT HAPPENS WHEN YOU THROW IT.
 *
 * A small brass plate above the bottom-right footer with a lamp in it: lit while he is
 * about, dark when he is not. It is always present even when he is not loaded
 * at all, because it is the only way back.
 */
.skull-toggle:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;right:14px;z-index:62;
  bottom:calc(72px + env(safe-area-inset-bottom,0px));
  display:flex;align-items:center;gap:7px;cursor:pointer;flex:none;white-space:nowrap;
  padding:5px 9px 5px 7px;border:1px solid var(--gold-line);
  background:linear-gradient(180deg,rgba(var(--hue-79,28,36,31),.94),rgba(var(--hue-112,14,20,17),.94));
  clip-path:polygon(7px 0,100% 0,100% 100%,0 100%,0 7px);
  font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-2);
  text-transform:uppercase;color:var(--ink-4);
  box-shadow:inset 0 1px 0 rgba(214,196,150,.14), 0 2px 10px rgba(0,0,0,.5);
  transition:color .18s ease, border-color .18s ease, box-shadow .18s ease}
.skull-toggle:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-warm);border-color:var(--gold);
  box-shadow:inset 0 1px 0 rgba(214,196,150,.2), 0 0 14px rgba(180,143,50,.28)}
/* The lamp. Lit is a live thing; dark is a glow that has gone out. */
.skull-toggle:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) i{width:8px;height:8px;border-radius:50%;flex:none;
  background:rgb(var(--hue-82,42,52,46));box-shadow:inset 0 0 3px rgba(0,0,0,.7);
  transition:background .25s ease, box-shadow .25s ease}
.skull-toggle.on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) i{background:rgb(var(--hue-3,123,232,168));
  box-shadow:0 0 7px rgba(var(--hue-3,123,232,168),.9), 0 0 16px rgba(var(--hue-3,123,232,168),.45)}
.skull-toggle.on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-warm)}
@media (max-width: 999px){ .skull-toggle:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none} }

/*
 * HITTING THE GLASS. A ring thrown out from the point of impact and a brief
 * bloom over the whole page -- the only moment anything on this site flashes,
 * which is what makes it land. Both are gone inside half a second.
 */
/* ==========================================================================
   THE TEAR -- where he comes through the gallery.

   Owner: "when looking at a screenshot and the screenshot gallery is on top of
   the plane where he is can we have him rip through the plane on one of the
   edges so h[e] doesnt hit a screenshot make a tear in the gallery that there
   until we close the gallery then have him continue his scans on that plane?"

   The lightbox is z-index 70 and he lives at 55, so a screenshot genuinely does
   bury him. Rather than raise him quietly through it -- which would look like a
   z-index change, because it is one -- he goes to an EDGE, opens the overlay
   like a sheet, and climbs out onto the front of it. The rip stays open behind
   him the whole time the gallery does, so there is always a reason he is up
   there.

   It is a real gap and not a drawn one: the fill is darker than the overlay
   above it and carries the page's own ground, so what shows through the slit
   reads as the room behind the sheet. The jagged edges are generated per tear
   in skull.js, so no two rips are the same shape.
   ========================================================================== */
/* ==========================================================================
   THE HOLE HE CAME THROUGH.

   Owner: "make it look like glass instead of a tear w[i]th a silouette of him
   left behind and some cracks eminating from the silouette."

   Two attempts at a TEAR failed the same way, and the second time I wrote down
   why without acting on it: a hole in a dark sheet over a dark room has nothing
   to show. The gallery backdrop is rgba(var(--hue-105,4,8,7),.94) and the page beneath it is
   nearly black, so there is no contrast to be had from a gap however it is
   shaded.

   Glass has no such problem. Cracks are LINES, and a line is visible against
   anything -- so the break carries its own legibility instead of borrowing it
   from whatever is behind. The silhouette is the shape he left in it, and the
   fractures run out from that shape rather than from a point, which is what
   says something went THROUGH rather than hit it.
   ========================================================================== */
.skull-glass:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;z-index:71;pointer-events:none;
  opacity:0;transition:opacity .3s ease;
  filter:drop-shadow(0 0 6px rgba(200,240,255,.55))
         drop-shadow(0 0 20px rgba(var(--hue-41,120,220,180),.3))}
.skull-glass.open:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){opacity:1;transition:none}
@media (prefers-reduced-motion:reduce){.skull-glass:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:none}}

.skull-crack:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;z-index:59;pointer-events:none;opacity:0;
  border:2px solid rgba(var(--hue-48,214,236,224),.85);border-radius:50%;
  box-shadow:0 0 22px rgba(var(--hue-22,190,255,220),.5), inset 0 0 18px rgba(var(--hue-22,190,255,220),.3)}
.skull-crack.hit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:crackout .5s cubic-bezier(.15,.7,.3,1) forwards}
@keyframes crackout{
  0%   {opacity:1;   transform:translate(-50%,-50%) scale(.12)}
  100% {opacity:0;   transform:translate(-50%,-50%) scale(1)}}
.skull-bloom:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;inset:0;z-index:58;pointer-events:none;opacity:0;
  background:radial-gradient(circle at var(--hx,50%) var(--hy,50%),
    rgba(var(--hue-50,226,255,238),.30) 0%, rgba(var(--hue-44,150,230,190),.10) 26%, transparent 58%)}
.skull-bloom.hit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:bloomout .42s ease-out forwards}
@keyframes bloomout{0%{opacity:1}100%{opacity:0}}
@media (prefers-reduced-motion: reduce){
  .skull-crack:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.skull-bloom:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none} }

/*
 * SPARKS. Struck off him when the cursor knocks into him -- bright, small, and
 * gone in under a second. Thrown from the render loop rather than by a keyframe,
 * because they have to leave from wherever he was hit.
 */
.skull-spark:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:0;top:0;width:3px;height:3px;pointer-events:none;
  opacity:0;border-radius:50%;
  background:radial-gradient(circle, #FFF6DC 0%, #FFC96A 45%, rgba(255,140,40,.5) 75%, transparent 100%);
  box-shadow:0 0 6px rgba(255,190,90,.9)}
/* A SHARD, NOT AN EMBER. The ember above is right for a knock and wrong for a
   window: glass carries no heat, so it is pale rather than amber, and it is a
   splinter rather than a dot -- longer than it is wide, lit hard along its
   length, and turning as it flies (skull.js gives each one its own spin). */
.skull-spark.glassy:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:9px;height:2.5px;border-radius:1px;
  background:linear-gradient(90deg,
    rgba(var(--hue-58,214,255,248),0) 0%, #FFFFFF 32%, rgb(var(--hue-83,230,255,250)) 62%, rgba(150,235,255,0) 100%);
  box-shadow:0 0 9px rgba(214,250,255,.95), 0 0 20px rgba(140,230,255,.5)}
/* AN ARC OFF A CUT CABLE. Owner: "the sparks are weak i wanna see big blue...
   then see the explosion".

   The ember above is a hot fleck struck off metal -- amber, 3px, right for a
   knock and wrong for this. Mains going to earth is not hot, it is BRIGHT: a
   white core that reads as overexposed, a hard cyan corona around it, and a
   blue glow thrown far enough to light the casing it is coming from. Four
   times the ember's size and three shadows deep, because the whole point is
   that you see it from behind a machine that is hiding the thing making it. */
.skull-spark.arc:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:12px;height:12px;
  background:radial-gradient(circle, #FFFFFF 0%, #DFF6FF 22%, #6FD4FF 46%,
    rgba(60,150,255,.55) 72%, transparent 100%);
  box-shadow:0 0 14px rgba(150,225,255,1), 0 0 34px rgba(80,180,255,.8),
    0 0 68px rgba(40,120,255,.45)}
@media (prefers-reduced-motion: reduce){ .skull-spark:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none} }

/*
 * HE SHOOK THE MONITOR, AND LEFT A CRACK IN IT.
 *
 * Owner: "can we add a little shake to the whole page and a little crack in the
 * glass too? like he shook the monitor crask goes away after a few seconds
 * fades away?"
 *
 * THE SHAKE IS ON THE SHELL, which carries the bezel and the phosphor with it --
 * so what moves is the SCREEN, while he stays exactly where he struck it. That
 * is the right way round: he is the one doing the hitting. It is also why he is
 * not inside the shell; his canvas hangs off the body, untouched by this.
 *
 * Small and brief. A page that lurches is a page nobody can read, and the whole
 * effect is over in a third of a second.
 */
.shell.rattled:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:rattle .34s cubic-bezier(.36,.07,.19,.97) both}
@keyframes rattle{
  0%   {transform:translate(0,0)}
  12%  {transform:translate(-6px,3px)}
  26%  {transform:translate(5px,-4px)}
  42%  {transform:translate(-4px,-2px)}
  58%  {transform:translate(3px,3px)}
  74%  {transform:translate(-2px,-1px)}
  88%  {transform:translate(1px,1px)}
  100% {transform:translate(0,0)}}

/*
 * THE CRACK. Real radiating fractures, drawn fresh each time so no two impacts
 * look alike, held for a moment and then fading out over a few seconds -- glass
 * that stayed cracked would become part of the furniture, and this is meant to
 * be something that just happened.
 */
.skull-shatter:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;z-index:59;pointer-events:none;
  opacity:0;filter:drop-shadow(0 0 4px rgba(200,240,255,.5))}
.skull-shatter.hit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:shatterfade 4.6s ease-out forwards}
@keyframes shatterfade{
  0%   {opacity:0}
  4%   {opacity:.92}
  34%  {opacity:.66}
  100% {opacity:0}}
@media (prefers-reduced-motion: reduce){
  .shell.rattled:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none}
  .skull-shatter:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none} }

/*
 * THE DAY HE FLIES TOO NEAR THE CANDLES.
 *
 * Owner: "you think randomly he can get too clos to the cangles and one of his
 * feet could catch fire then have him rush off the page with a smoke trail
 * behind him?"
 *
 * Rarely -- about one errand in fourteen -- he goes down to look at the candles
 * burning along the bottom of the page, gets too close, and one of them takes.
 * Then he bolts for the nearest edge trailing smoke, and comes back a few
 * seconds later from somewhere else, having learnt nothing.
 *
 * It is the only thing on this page that happens TO him rather than being
 * something he does, which is why it is worth having: a thing that can be
 * caught out is a thing with a life of its own.
 */
/* The screen anchor has no animation: the fire field is painted behind the
   skull by its render loop, and cannot replace the transform that follows him. */
.skull-fire:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;
  opacity:0;visibility:hidden}
.skull-fire.lit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){opacity:1;visibility:visible}
.skull-host .skull-fire:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > canvas{position:absolute;inset:auto;display:block;
  max-width:none;pointer-events:none}

/* The trail. Each puff is placed and faded from the render loop rather than by
   a keyframe, because they have to be left BEHIND him along a path nobody knows
   in advance. */
.skull-puff:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:0;top:0;width:15px;height:15px;pointer-events:none;
  opacity:0;border-radius:50%;filter:blur(4px);
  /* Owner: "leave brighter white smoke behind him and double the amount". Near
     white rather than the grey it was, because smoke off a burning thing on a
     near-black page has to be pale to read at all. */
  background:radial-gradient(circle, rgba(246,246,242,.85) 0%, rgba(226,224,216,.35) 45%, transparent 72%)}
@media (prefers-reduced-motion: reduce){ .skull-fire:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.skull-puff:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none} }

/* THE SMOKE, and it has to stay tiny -- the owner said so twice. Three puffs on
   deliberately unrelated timings, drifting up off the burn line and gone in
   under two seconds. Any more and it stops being a wisp and starts being a
   fire. */
.skull-smoke:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;width:9px;height:9px;pointer-events:none;
  opacity:0;border-radius:50%;filter:blur(3px);
  background:radial-gradient(circle, rgba(214,210,200,.34) 0%, transparent 72%)}
.skull-smoke.on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){opacity:1}
.skull-smoke:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::before,.skull-smoke:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{content:"";position:absolute;inset:0;
  border-radius:50%;background:inherit;
  animation:smokeup 1.7s ease-out infinite}
.skull-smoke:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{animation-duration:2.3s;animation-delay:-.9s}
@keyframes smokeup{
  0%   {transform:translate(0,0) scale(.45);opacity:.55}
  100% {transform:translate(-6px,-30px) scale(2.1);opacity:0}}
@media (prefers-reduced-motion: reduce){ .skull-smoke:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none} }

/*
 * THE BEAM, REBUILT AS LIGHT RATHER THAN AS A SHAPE.
 *
 * Owner: "the 'scanning area' with the lines is not great can you rethink that
 * and make it look more realistic??"
 *
 * The old one was a filled trapezoid with two drawn lines down its sides, and
 * that is the problem in one sentence: hard geometric edges read as vector art,
 * because nothing in the physical world has them. Light from a lens is brightest
 * at the lens, spreads at an angle, softens at its edges where the aperture
 * blurs it, and dims with distance.
 *
 * So it is a CONIC GRADIENT struck from the lens itself -- a true angular wedge,
 * with the two edges falling off over several degrees instead of stopping dead.
 * A radial mask over the top takes the brightness down with distance travelled.
 * No clip-path, no drawn lines, nothing with a border.
 *
 * The angles are computed per frame from the lens to the first and last letters,
 * so the wedge still lands exactly on the words -- it just no longer announces
 * where it stops.
 */
.skull-cone:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:0;top:0;pointer-events:none;opacity:0;
  transition:opacity .2s ease;
  background:conic-gradient(from var(--from,170deg) at var(--ax,50%) 0%,
    transparent 0deg,
    rgba(var(--hue-6,150,255,196),.05) var(--soft,4deg),
    rgba(var(--hue-6,150,255,196),.20) calc(var(--soft,4deg) * 2),
    rgba(var(--hue-6,150,255,196),.20) calc(var(--span,20deg) - var(--soft,4deg) * 2),
    rgba(var(--hue-6,150,255,196),.05) calc(var(--span,20deg) - var(--soft,4deg)),
    transparent var(--span,20deg));
  /* IT FADES OUT BEFORE IT LANDS, rather than stopping on a line.
  
     Owner: "theres this sharp line at the top of the scan too where his beam
     ends on the verticlt bar if that could just be a fade that would be
     perfect."
     
     The element is exactly as tall as the gap between the lens and the words,
     so the gradient simply ran out at the bottom edge and left a hard
     horizontal cut across the beam. The radial mask above was supposed to take
     care of distance, but its circle is sized to the FARTHEST CORNER of the
     box -- about 400px on a wide, short wedge -- so at the bottom edge, 130px
     down, it is still near full strength.
     
     A second mask runs along the beam instead of out from its apex, and takes
     it to nothing over the last fifth. The light thinning as it arrives is also
     what a beam does; the pool underneath is what actually lights the words. */
  -webkit-mask-image:radial-gradient(circle at var(--ax,50%) 0%,
    rgba(0,0,0,.95) 0%, rgba(0,0,0,.55) 55%, rgba(0,0,0,0) 100%),
    linear-gradient(180deg,#000 0,#000 52%,rgba(0,0,0,.55) 80%,rgba(0,0,0,0) 100%);
  mask-image:radial-gradient(circle at var(--ax,50%) 0%,
    rgba(0,0,0,.95) 0%, rgba(0,0,0,.55) 55%, rgba(0,0,0,0) 100%),
    linear-gradient(180deg,#000 0,#000 52%,rgba(0,0,0,.55) 80%,rgba(0,0,0,0) 100%);
  -webkit-mask-composite:source-in;
  mask-composite:intersect}
/* Quote light reaches the lettering before fading. Combining both distance
   masks erased the end of this beam above the dark footer. */
.skull-field.across:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) ~ .skull-cone{
  -webkit-mask-image:linear-gradient(180deg,rgba(0,0,0,.95) 0%,rgba(0,0,0,.7) 82%,transparent 100%);
  mask-image:linear-gradient(180deg,rgba(0,0,0,.95) 0%,rgba(0,0,0,.7) 82%,transparent 100%)}
.skull-cone.burn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){
  background:conic-gradient(from var(--from,170deg) at var(--ax,50%) 0%,
    transparent 0deg,
    rgba(255,190,120,.06) var(--soft,4deg),
    rgba(255,190,120,.24) calc(var(--soft,4deg) * 2),
    rgba(255,190,120,.24) calc(var(--span,20deg) - var(--soft,4deg) * 2),
    rgba(255,190,120,.06) calc(var(--span,20deg) - var(--soft,4deg)),
    transparent var(--span,20deg))}

/*
 * WHERE IT LANDS: A POOL, AND A ROUND ONE.
 *
 * Owner: "make the square in the background on the slement a little more round
 * and gradient though."
 *
 * The box is now much larger than the words it sits under, and the gradient is
 * finished well inside it -- transparent by 74% of a 42% radius -- so the light
 * has faded to nothing long before it reaches any edge. That is the whole trick
 * to a soft glow: it is not the gradient that makes a square, it is the gradient
 * running out of room. Four stops rather than two, so the falloff is a curve
 * instead of a ramp.
 */
.skull-pool:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:0;top:0;pointer-events:none;opacity:0;
  transition:opacity .2s ease;
  background:radial-gradient(ellipse 42% 44% at 50% 50%,
    rgba(var(--hue-20,158,255,205),.26) 0%,
    rgba(var(--hue-43,136,246,190),.15) 30%,
    rgba(var(--hue-17,112,232,168),.06) 52%,
    rgba(var(--hue-13,100,220,158),.02) 66%,
    transparent 74%)}
.skull-pool.burn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:radial-gradient(ellipse 42% 44% at 50% 50%,
    rgba(255,206,150,.30) 0%,
    rgba(255,182,112,.17) 30%,
    rgba(255,158,74,.07) 52%,
    rgba(240,140,60,.02) 66%,
    transparent 74%)}

/* ACROSS, NOT DOWN. A row of a table is read top to bottom; a line of text is
   read left to right, so the bar turns ninety degrees and travels the other
   way. Same field, same cone, different grain. */
/* THE BAR TURNED ON ITS SIDE NEEDS ITS MASK TURNED WITH IT.
   
   .skull-field::after above is a HORIZONTAL bar, and its mask fades the ends
   with a 90deg gradient and the long edges with a 180deg one. Standing that bar
   up for a quote without redefining the mask leaves a vertical 5px sliver
   wearing a horizontal bar's fade: the 90deg pass takes it to nothing at 18%
   and 82% of FIVE PIXELS, and the 180deg pass -- meant to soften the long edges
   -- instead cuts the travelling line off flat at top and bottom. The bar is
   almost erased across its width and hard-edged along its length, which is
   backwards on both axes.
   
   And the mask modulates the backdrop-filter, so this is not a tint going
   wrong: it is the BRIGHTENING that stops dead, which is what shows up as an
   artifact against the words rather than as a softer bar.
   
   One gradient, running the length of the bar, so its two ends fade out. The
   width is 5px and needs no fade across it. */
.skull-field.across:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{left:var(--scan,0%);top:0;bottom:0;right:auto;
  width:5px;height:auto;margin-top:0;margin-left:-2px;
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 12%,#000 88%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 12%,#000 88%,transparent);
  background:linear-gradient(180deg,
    transparent, rgba(var(--hue-7,170,255,205),.22) 14%, rgba(var(--hue-7,170,255,205),.22) 86%, transparent)}

/*
 * BURNING IT IN, rather than reading it.
 *
 * Owner: "i would LOVE for the part of the quote that gets written following
 * the scanner it glows a tiny bit like it was burned into the background maybe
 * even with a tiny bit of smoke TINY bit. but like he burns it in leaving the
 * text?"
 *
 * So the two halves of a quote change no longer look alike. ERASING keeps the
 * cold green auspex light. WRITING runs the same bar hot -- the backdrop-filter
 * pushes brightness far harder and swings the hue toward ember, so the letters
 * flare orange-white exactly as they appear.
 *
 * ::before is the COOLING TRAIL: everything already written, tinted warm and
 * falling off with distance from the edge, so what he laid down a moment ago is
 * still glowing faintly and what he wrote first has gone cold.
 */
.skull-field.burn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{
  backdrop-filter:brightness(3.6) saturate(2.4) sepia(.5) hue-rotate(-30deg);
  -webkit-backdrop-filter:brightness(3.6) saturate(2.4) sepia(.5) hue-rotate(-30deg);
  background:linear-gradient(180deg,
    transparent, rgba(255,200,130,.34) 14%, rgba(255,200,130,.34) 86%, transparent);
  box-shadow:0 0 18px rgba(255,170,80,.55), 0 0 42px rgba(255,120,40,.22)}
.skull-field.burn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::before{content:"";position:absolute;top:0;bottom:0;left:0;
  width:var(--scan,0%);pointer-events:none;
  background:linear-gradient(90deg,
    rgba(255,150,60,0) 0%, rgba(255,150,60,.05) 70%, rgba(255,186,110,.16) 100%)}

/* THE SMOKE, and it has to stay tiny -- the owner said so twice. Three puffs on
   deliberately unrelated timings, drifting up off the burn line and gone in
   under two seconds. Any more and it stops being a wisp and starts being a
   fire. */
/* (the second copy of the smoke block lived here -- byte-identical to the
   one above, and equally invisible to anyone reading either) */

/*
 * THE CONE, AND THE TWO EDGES THAT MAKE IT A BEAM.
 *
 * Owner: "he has a big triangle above the quote that just looks like a triangle
 * it needs to have solid edges that light up both the beginning and end of the
 * quote and kind of make the whole area glow."
 *
 * A filled triangle is a shape; a beam is two bright edges with light between
 * them. So the fill drops to a faint haze and the work is done by two drawn
 * lines running from the lens to the FIRST and LAST letters of the line -- which
 * is also what ties the cone to the text rather than to the box around it.
 */
/* THE OLD BEAM LIVED HERE and is deliberately gone.
   
   It was a linear gradient cut to a trapezoid with clip-path, reading --apex,
   --bot-l and --bot-r. The conic version above replaced it -- owner: "the
   'scanning area' with the lines is not great can you rethink that and make it
   look more realistic??" -- but this rule was never deleted, and being declared
   LATER it went on winning, silently, for both .skull-cone and .skull-cone.burn.
   
   skull.js was writing --ax, --from, --span and --soft to an element styled by
   a rule that reads none of them, so the polygon fell back to its own defaults:
   apex at 50% of a box that spans from the lens all the way to the target, and
   a fan-out of the full width. That is why the beam left from a point midway
   between him and the words, with hard edges -- the exact thing the rethink was
   asked for. Two rules with one name, and the dead one on top. */

/* The two drawn edge lines are gone: a beam has soft angular falloff, not
   borders. See .skull-cone above. */

/*
 * ══ GOTHIC HIGH-TECH ══════════════════════════════════════════════════════
 *
 * Owner, 2026-09-02: "I want to start transforming the site into a Gothic
 * high-tech look and feel."
 *
 * The Imperium does not build sleek. It builds iron and brass, rivets and
 * plating, engraved plates bolted onto machines that have run for ten thousand
 * years and were repaired by people who did not understand them. Screens are
 * set INTO things. Nothing floats.
 *
 * THE RULE EVERY ONE OF THESE OBEYS, and it is the owner's own from earlier
 * tonight -- his readers are young and easily lost, so an ornament has to mark
 * a boundary, show a state, or say whose something is. A rivet that means
 * nothing is one more thing between a sergeant and the answer:
 *
 *   the plate under a heading   says where a panel starts
 *   the rivets on its corners   say this is a panel and not a paragraph
 *   the phosphor wash           says the whole thing is one screen
 *   the bezel                   says where the screen ends and the machine is
 *
 * All of it is drawn with gradients and shadows on elements that already
 * exist. No images, no extra requests, and every rule is one the browser was
 * going to composite anyway.
 */

/* AN ENGRAVED PLATE, not a text label -- the letters cut into the metal rather
   than printed on it.

   THE STAMPED UNDERLINE IS GONE, and it is the same argument .plate made about
   the top lip. .plate-head's own rule is brass (--rule-head), so the head drew
   TWO brass rules four pixels apart saying the one sentence: the title's bar
   and the head's hairline. ONE RULE IN A HEAD, NOT TWO. The bar was the
   narrower and the more decorative of the pair, so the bar goes -- and the
   2px/4px padding that existed only to clear it goes with it. What stays is
   the cut: a dark shadow under the letters, which is depth and not a rule. */
.heading:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;padding:0;
  text-shadow:0 1px 0 rgba(0,0,0,.55), 0 0 14px rgba(216,180,90,.28)}

/* RIVETS. Four to a panel, at its corners, so a block of content reads as a
   plate bolted to the hull rather than as a rectangle somebody drew. Drawn on
   the ::before of the panel itself -- no markup, no elements. */
/* ==========================================================================
   PLATE -- the one panel, and the only riveted thing in the interface.

   A plate is iron with a heading bolted to it. It has a lit top lip and a dark
   bottom because the room has one lamp and it is above it; it has four rivets
   because a rectangle somebody drew and a panel bolted to the hull have to be
   told apart at a glance.

   ORNAMENT AUDIT
     bevel   BOUNDARY  where the surface starts and where it stops
     rivets  BOUNDARY  this is a panel, not a paragraph
     .plate-lit    STATE/WHOSE  this panel is live, or it is yours
     .plate-owed   STATE        this panel carries something unresolved
   There is no hover on a plate. A panel is a place, not a state, and there
   is no longer an exception to it. .plate-act existed for the one case where a
   whole panel was a control -- a company card you pressed to open -- and the
   companies are a roll of rails now, so the file's only user of it is gone.
   Its hover was backwards in any case: --panel-4 composited to --panel-3 is
   rgb(var(--hue-91,15,28,22)) going to rgb(var(--hue-72,9,18,14)), so on the one screen made of pressable
   panels, reaching for a company put it OUT.

   ONE TOP-EDGE LIGHT, NOT TWO. The proposed layer lit the top lip here AND
   ran a top sheen down .plate-head, which on every plate whose first child is
   a head -- all of them -- stacks two lights in the same two pixels saying the
   same sentence. The lip stays, the head's gradient goes. The left lip goes
   too: "light comes from above" is one direction, and lighting the left edge
   as well implies a lamp up and to the left that no other surface agrees with.

   NESTING. A plate inside a plate sets --rivet:transparent through
   .plate-bare: two riveted boxes inside each other make "this is a panel"
   ambiguous, which is the one thing the rivets are for. A GRID OF CARDS IS
   ALSO .plate-bare -- six to twelve riveted rectangles side by side is the
   sentence said twelve times, and the grid already says "these are panels".

   NO NOTCH. .notch/.notch-sm are clip-path, and clip-path clips box-shadow and
   ::before both, so a notched plate loses its bevel AND its rivets. The rivets
   say more than a cut corner does, so the corner stays a radius -- --r2, the
   3px every panel in the mockup is drawn at.
   ========================================================================== */
.plate:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;display:flex;flex-direction:column;min-width:0;
background:var(--panel-4);
border:1px solid var(--line-3);
/* THE TOP EDGE IS LIT, and the refusal further down this sheet rested on
   arithmetic that does not hold. It computed rgba(var(--hue-8,210,225,210),.16) over a
   plate face as rgb(var(--hue-59,46,59,52)) and called that DARKER than --line-3. Worked
   again over the plate's actual top ground rgb(var(--hue-60,21,36,28)): --line-3 at .13
   gives rgb(var(--hue-61,41,58,47)) and this gives rgb(var(--hue-62,53,68,59)) -- brighter on all three
   channels. The mockup draws it on every panel it has. */
border-top-color:rgba(var(--hue-8,210,225,210),.17);
border-radius:var(--r2);
box-shadow:var(--bevel)}

/* Drawn on the panel's own ::before -- no markup, no elements, no requests.
   inset:5px keeps every head inside the padding of the head plate above it. */
.plate:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::before{content:"";position:absolute;inset:5px;pointer-events:none;
border-radius:1px;
background:
  radial-gradient(circle at 0 0,      var(--rivet) 0 1.4px, var(--rivet-shade) 1.4px 2.6px, transparent 2.7px),
  radial-gradient(circle at 100% 0,   var(--rivet) 0 1.4px, var(--rivet-shade) 1.4px 2.6px, transparent 2.7px),
  radial-gradient(circle at 0 100%,   var(--rivet) 0 1.4px, var(--rivet-shade) 1.4px 2.6px, transparent 2.7px),
  radial-gradient(circle at 100% 100%,var(--rivet) 0 1.4px, var(--rivet-shade) 1.4px 2.6px, transparent 2.7px)}

/* A plate with no rivets is a plate whose rivets are unlit -- one declaration,
   and it inherits, so a whole nested region de-rivets from its root. */
.plate-bare:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){--rivet:transparent;--rivet-shade:transparent}

.plate-lit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold-line);
box-shadow:var(--bevel),0 0 26px -12px var(--glow-live)}
.plate-owed:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--warn);
box-shadow:var(--bevel),0 0 26px -12px var(--glow-owed)}

/* ==========================================================================
   PLATE-HEAD -- the one section header, replacing two idioms that split the
   app almost evenly: `row g6 pad-card rule-b` (x8, the AAR screen) and
   `row-base g5 [wrap] pad-head` (x9, everywhere else).

   THE ONE REAL DISAGREEMENT between them was align-items. The AAR headers are
   `row` (center) because ten of them hold buttons; the rest are `row-base`
   (baseline) because they are two runs of text. One component picks one, and
   it picks CENTER: a baseline row with a 30px button in it is visibly broken,
   and a centred row of pure text is not.

   ORNAMENT AUDIT
     hairline  BOUNDARY  where the head ends and the panel's work begins
   That hairline is BRASS (--rule-head) and it is now the only brass line in a
   head: .heading's stamped ::after underline drew a second one four pixels
   above it, and one rule in a head is the whole of what a rule in a head can
   say. The hairline bounds the head; nothing bounds the title separately.
   No hover, no glow, no state colour -- a header is never a state. And no top
   sheen: the plate above already lights its own top lip.

   Side padding is --s8, the same as .pad-card, .grid-hdr, .rail, .rail-grid
   and .pad-cell, so a heading, a column label and a row all begin on one
   line. It was --s7; the mockup pads a panel 20px and all six moved together.
   ========================================================================== */
.plate-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;display:flex;align-items:center;flex-wrap:wrap;
gap:var(--s3) var(--s5);
/* 18 ABOVE, 9 DOWN TO THE RULE -- the mockup's numbers, and neither is on the
   --s ladder (--s7 is 16, --s4 is 8), so both are literals scaled by --ui the
   way .grid-hdr's 9px gap already is. Sides stay --s8, which IS its 20px. */
padding:calc(18px * var(--ui)) var(--s8) calc(9px * var(--ui));
border-bottom:1px solid var(--rule-head)}

/* THE RIGHT-HAND SLOT, replacing <div class="spacer"></div><span class="meta">
   in all nineteen headers: one element instead of two, and it cannot be put in
   the wrong order. */
/* THE JUDGING PAGE'S TYPE, PROMOTED. Both of these read --fs-2/--fs-3 at
   --ink-5 everywhere, and the AAR page overrode both to one size at --ink-3 --
   a floor of 10px so the line never falls under the mockup's smallest, and the
   dimmer ink so a head's second half never competes with the heading beside
   it. Owner liked that page and asked for the roster and the companies to read
   the same way, so the override moves here and the page keeps nothing of its
   own. The 10px floor is what --fs-2 could not say: --fs-2 is 10px * --ui, so
   on a phone it went under 10 and the meta went to 9 while the sub stayed 11.
   One size, one ink, forty-five heads. */
.plate-head-meta:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-left:auto;flex:none;
font-family:var(--mono);font-size:max(10px,var(--fs-3));
letter-spacing:var(--ls-meta);color:var(--ink-3)}

/* The plain-English half-line nine of the headers carry ("who stands where"). */
.plate-head-sub:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:max(10px,var(--fs-3));color:var(--ink-3);min-width:0}

/* THE HEAD WITH NO PLATE UNDER IT: a tier in the Strategium, a place inside a
   company. Same component, no side padding, tighter, fainter rule. */
.plate-head-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s5) 0 var(--s2);gap:var(--s2) var(--s4);
border-bottom-color:var(--hair)}

@media (max-width:999px){
/* THE SIDES FOLLOW .pad-card, WHATEVER .pad-card IS. That is the rule, and it
   was written here as "--s7" back when --s7 was what every row under a head
   used. The rows moved to --s8 and this did not, which stepped the title 4px
   INBOARD of every row beneath it -- the same bug this override exists to
   prevent, re-made from the other side. Name the relationship, not the number.
   The top and bottom stay tight, which is what the narrow screen needed. */
.plate-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s4) var(--s8) var(--s3);gap:var(--s2) var(--s4)}
}

/* THE PHOSPHOR WASH. The whole app is one screen in one machine, and a screen
   has scanlines and falls off at its edges. Kept far below the threshold where
   it costs anybody a word: 3% on the lines, and a vignette that only reaches
   the corners. It sits above everything and takes no clicks. */
.phosphor:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;inset:0;z-index:60;pointer-events:none;
  background:
    repeating-linear-gradient(180deg, rgba(0,0,0,.055) 0 1px, transparent 1px 3px),
    radial-gradient(120% 100% at 50% 50%, transparent 55%, rgba(0,0,0,.38) 100%)}

/* THE BEZEL. Where the screen stops and the machine begins: a lit inner lip on
   the top and left, shadow on the bottom and right, as though the glass is set
   into a frame lit from the same corner as everything else. */
.bezel:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;inset:0;z-index:61;pointer-events:none;
  /* ONE LIT EDGE. The left lip said the light came from up and to the left,
     which no other surface in the sheet agrees with -- every plate, card and
     banner lights its top only. The sides take the shadow instead. */
  box-shadow:
    inset 0 1px 0 rgba(255,228,158,.18),
    inset 1px 0 0 rgba(0,0,0,.4),
    inset -1px 0 0 rgba(0,0,0,.55),
    inset 0 -1px 0 rgba(0,0,0,.6),
    inset 0 0 40px rgba(0,0,0,.28)}

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

/*
 * THE SERVO-SKULL, and what it is allowed to say.
 *
 * Owner: "how fucking awesome would it be to have a servo skull floating
 * around the page? small but visible. when you load up a report the servo
 * skull gives it a quick scan and says auspex clean or whatever the 40k lingo
 * would be?"
 *
 * A servo-skull is a dead servant's skull on anti-grav impellers, running one
 * task without judgement -- scouting ahead and reporting what it sees. That is
 * exactly what the reader does, so the skull is the reader's face and the
 * auspex is what it hands you.
 *
 * IT REPORTS, IT DOES NOT DECIDE. The sweep is theatre over an answer that was
 * worked out on a GPU at the owner's house hours earlier; nothing about what
 * it says is invented while it flies. If the ledger holds no reading it says
 * so rather than playing an animation and landing on "clear".
 *
 * AND IT HOLDS STILL FOR ANYONE WHO ASKS. A thing that bobs forever in the
 * corner of a screen somebody is trying to read is a cost, not a flourish --
 * particularly for the readers this site is actually for. prefers-reduced-
 * motion stops every part of it and leaves the verdict, which is the half that
 * carries the meaning.
 */
/* The drawn skull's own styles went with the drawing. */

/* The beam it drops while it reads, across the panel it is reading. */
.skull-beam:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;left:0;right:0;top:0;height:2px;
  background:linear-gradient(90deg,transparent,var(--good),transparent);
  box-shadow:0 0 12px var(--good);
  animation:skullbeam 1.15s ease-in-out;pointer-events:none}

@keyframes skullbeam{0%{top:0;opacity:0}12%{opacity:1}88%{opacity:1}100%{top:100%;opacity:0}}

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

/* AFTER .quotebar, not before it: same specificity, so source order decides,
   and a media query above the rule it overrides does nothing at all. */
@media (max-width: 999px) { .quotebar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){height:calc(78px * var(--ui))} }

.quote:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;width:100%;height:100%}

/*
 * A QUOTE THAT DOES NOT FIT WRAPS RATHER THAN STOPPING MID-WORD.
 *
 * Owner: "some ofnthe quotes are too big and dont fit how do we solve that?"
 * It was one line, nowrap, with an ellipsis -- so on a phone the longer ones
 * ended in "…" and the line nobody could read was the punchline. They are
 * quotes; the last few words are the point of them.
 *
 * Two lines, clamped. Two is the number because the bar is a fixed height --
 * the cross-fade needs both quotes absolutely positioned over each other, so
 * the bar cannot grow to fit its content -- and two lines of this size fit
 * inside it with the attribution underneath. A quote too long for two lines
 * still ends in an ellipsis, which is the honest floor rather than a promise.
 */
/* NO ITALIC. Cinzel is loaded as wght@400;600;700 -- there is no italic axis,
   so the browser was shearing the roman by hand. The same lines already render
   upright on the Settings tab, so this was inconsistent as well as synthetic. */
.quote-text:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:var(--fs-5);
  letter-spacing:var(--ls-1);color:var(--ink-3);line-height:1.35;
  max-width:100%;overflow:hidden;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;
  text-overflow:ellipsis}

.quote-who:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-2);font-weight:600;
  letter-spacing:var(--ls-2);text-transform:uppercase;color:var(--gold-dim);
  max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.quote-in:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.quote-out:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;inset:0;
  /* Both sweeps clip at the scanner, in the quote container's coordinates. */
  clip-path:inset(0 var(--wipe-r,0px) 0 var(--wipe-l,0px));
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--s2);text-align:center}
.quote-in:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:quotein .9s cubic-bezier(.2,.8,.2,1) forwards}
.quote-out:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:quoteout .7s cubic-bezier(.5,0,.75,0) forwards}
/* The servo-skull suppresses this on the layer itself when IT turns the strip
   over -- see skull.js. Toggling a rule from here restarted the animation the
   moment the toggle came off, which is worse than leaving it alone. */

/* The same hundred lines, read all at once on the Settings screen instead of
   one a minute along the bottom. It is a LISTING and not the strip: nothing
   here is a fixed height and nothing is clipped to one line, because the point
   of the screen is to read the whole of each one. The columns reflow off the
   same 280px measure the company grid uses, so a quote is never a single word
   wide on a large display or two words wide on a small one. */

/* ==========================================================================
   THE SUB-TAB BAR

   Sits under the main nav (Settings' permissions/admins/overrides/quotes,
   the Roster's brothers/companies), so resting state stays dim and small --
   only the active tab is allowed to touch gold, via its own class rather
   than a colour the row hands out for free.
   ========================================================================== */
.subtabs:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;gap:var(--s2);width:100%;
  border-bottom:1px solid var(--hair-3)}

.subtab:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){cursor:pointer;flex:none;
  padding:var(--s4) var(--s6);
  font-size:var(--fs-3);font-weight:600;letter-spacing:var(--ls-2);
  text-transform:uppercase;color:var(--ink-4);
  border-bottom:2px solid transparent;
  transition:color var(--t),border-color var(--t)}
.subtab:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-2)}

.subtab-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink);border-bottom-color:var(--gold)}

/* ==========================================================================
   THE DECISION ROW

   A sergeant correcting what the AI read: label, clickable current value,
   source chip, all on one line with the plain-English explanation running
   underneath. .decide carries position:relative so the sheet's own .pop can
   open off it without a second popover class -- no new floating menu here.
   ========================================================================== */
/* LEFT TO RIGHT, NOT STACKED. Owner: "overrides left to right not stacked."
   Each control is a small column -- its name over the value over where the
   value came from -- and the columns sit in a wrapping row, so three fields
   take one line on a desktop and stack only when there is genuinely no room.
   The grid it replaces gave each field a full-width row of its own, which put
   three dropdowns and their help text down a third of the page. */
.decide-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;flex-wrap:wrap;gap:var(--s6) var(--s7);
  padding:var(--s6) var(--s7)}

.decide:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;display:flex;flex-direction:column;
  gap:var(--s3);min-width:calc(150px * var(--ui));flex:1 1 auto}

/* THE FIELD'S NAME, SET LIKE A COLUMN HEAD AND NOT LIKE PROSE. It was the
   body face in sentence case at the size of the value beside it, so on a row
   whose whole shape is "name, then what it says" the two ran together. */
.decide-k:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:var(--ls-key);text-transform:uppercase;color:var(--ink-6)}

.decide-v:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;display:flex;align-items:center;gap:var(--s3);
  cursor:pointer;border:1px solid var(--gold-line);background:var(--panel-3);
  padding:var(--s3) var(--s5);
  font-size:var(--fs-5);color:var(--gold-warm);
  transition:border-color var(--t)}
.decide-v:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold)}
.decide-v:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{content:"▾";margin-left:auto;
  font-size:var(--fs-3);color:var(--gold-dim)}

.decide-set:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold);color:var(--gold)}

/* THE AUSPEX RETURN. A sensor names what comes back: a lock is the whole
   contact, a ghost is a reading that may not be there at all. Coloured on the
   same three-step ramp the rest of the panel uses -- brass for good, ash for
   partial, warm for a return worth doubting -- rather than a fourth palette. */
.lock:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:var(--ls-2);text-transform:uppercase;white-space:nowrap;
  padding:2px 7px;border-radius:2px;border:1px solid currentColor}
.lock-full:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--good)}
.lock-strong:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold)}
.lock-part:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-ash)}
.lock-ghost:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--warn-2)}

.decide-src:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);
  letter-spacing:var(--ls-1);text-transform:uppercase;color:var(--ink-5)}

/* ONE ROW OF THE RECORD. Its own padding rather than .py5, because the row needs
   side padding for the hover ground and the ellipsis to have air, and its own
   divider rather than .rule-b, because a rule BETWEEN ROWS is half the weight
   of the rule that ends a block -- .05 against .10. The hover is cool and very
   faint on purpose: --lift is the warm "you are here" lamp for a control, and
   a row of a record is not a control. */
.record-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s5) var(--s3);
  border-bottom:1px solid rgba(var(--hue-1,178,201,177),.05);
  transition:background var(--t)}
.record-row:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(var(--hue-1,178,201,177),.035)}

/*
 * THE RECORD DRAWS AT 70%. Owner, 2026-09-04: "lets also make the record a
 * little smaller 30% smaller."
 *
 * IT IS ONE SCALE, NOT TWENTY EDITS. Every measure in this panel is already
 * written as `calc(Npx * var(--ui))` or as a token off the same ladder, so the
 * whole thing shrinks by moving --ui underneath it -- and it keeps shrinking
 * correctly the day somebody changes a track width or a row height, which
 * hand-multiplying twenty numbers would not.
 *
 * THE LADDER HAS TO BE DECLARED AGAIN HERE, and this is the part that is easy
 * to get wrong: a custom property is substituted where it is DECLARED, not
 * where it is used. --s5 and --fs-4 are declared on :root, so they resolved
 * against the PAGE's --ui before this panel ever inherited them; redefining
 * --ui alone would shrink the four bare calc()s in the rules below and leave
 * every padding and every type size exactly as they were. Re-declaring the
 * ladder against the local --ui is what makes the panel scale as one thing.
 *
 * Only the rungs the record actually draws with are listed. A rung it does not
 * use would be dead weight, and a rung it starts using later will inherit the
 * page's value and stand out immediately -- which is the failure being visible
 * rather than silent.
 */
.aar-record:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){
  /* AND BACK UP A LITTLE. Owner, 2026-09-05: "the ENTIRE record needs to be a
     little bigger." .7 was 30% off a full-size table and it read as a table
     seen from further away; .8 keeps the record smaller than everything around
     it, which is what the original ask was for, without making a sergeant lean
     in. The whole panel still moves as ONE number -- that is the point of the
     paragraph above -- so this line is the entire change to its box. */
  --ui:calc(var(--ui-page) * .8);
  /*
   * THE BOX SHRINKS BY 30%. THE TYPE DOES NOT, and running them together was
   * the mistake. Owner: "the text is WAY too small in the record now."
   *
   * He asked for the RECORD to be smaller, and what makes a table smaller is
   * its rows and its gutters -- the padding, the row height, the track widths,
   * all of which ride --ui above. Type is not a dimension of the box: --fs-1 is
   * already 9px, and 30% off it is 6.3px, which is not small type, it is
   * unreadable type. The ladder is declared against the PAGE's scale with a
   * gentle trim instead, so the panel reads as tighter rather than as further
   * away.
   */
  /* THE TYPE COMES BACK TO FULL SIZE with it. The .92 trim existed to make a
     30%-smaller box read as tighter rather than as further away; at .8 the box
     no longer needs the words apologising for it, and "the ENTIRE record"
     means the words as well as the table. */
  --fs-1:calc(9px * var(--ui-page));  --fs-2:calc(10px * var(--ui-page));
  --fs-3:calc(11px * var(--ui-page)); --fs-4:calc(12px * var(--ui-page));
  --fs-5:calc(13px * var(--ui-page)); --fs-6:calc(14px * var(--ui-page));
  --s1:calc(2px * var(--ui));  --s2:calc(4px * var(--ui));
  --s3:calc(6px * var(--ui));  --s4:calc(8px * var(--ui));
  --s5:calc(10px * var(--ui)); --s6:calc(12px * var(--ui));
  --s7:calc(16px * var(--ui)); --s8:calc(20px * var(--ui))}

/* ---- THE RECORD'S THREE COLUMNS -----------------------------------------
   ONE TRACK LIST, TWO USERS. The column strip and every row's inner grid read
   the same declaration, so a column that widens widens in both and the heads
   stay over the values they name. They are the mockup's own tracks, its own
   12px gutter and its own 34px row. */
.record-hdr:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.record-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;
  grid-template-columns:minmax(calc(165px * var(--ui)),1fr)
                        minmax(calc(165px * var(--ui)),1fr)
                        minmax(calc(150px * var(--ui)),max-content);
  gap:0 var(--s6)}
/* 9px down to the rule, which is .plate-head's own step and not on the --s
   ladder. Sides are --s3, so the strip starts on the same 26px as the rows
   under it -- the panel's 20 plus the row's 6, which is the mockup's 20+6. */
.record-hdr:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){align-items:center;padding:0 var(--s3) calc(9px * var(--ui));
  border-bottom:1px solid var(--line-3)}
/* The middle head takes the brass ash the mockup gives it. --gold-ash IS its
   #8c7a52; nothing new is minted for it. */
.record-hdr-mid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-ash)}
/* THE VARIANCE COUNT, beside the AUSPEX verdict it qualifies. The mockup draws
   the two as one pill; here they are two spans so a clean report can carry the
   verdict alone, and the rule between them is what makes them read as one. */
/* THE VOX SEAM IS GONE. Owner: "lets remove the second brother that seperates
   us when we're on vox since we're going to have the filters itll be nice." It
   was .grid-hdr repeated mid-list -- a second row of column names in the middle
   of the roster, which read as a brother until you looked at it -- and the VOX
   dropdown answers the question it was drawing: press "in a channel" and the
   list IS the men on vox. The on-vox men still ride the top of the list; that
   is a paging guarantee (see boardRows) rather than a heading, and every one of
   them still prints ON VOX in his own row. */

/* THE GROUP RULE. A label, a hairline and a count -- the same furniture the
   panel heads use, one step quieter, so it reads as a division inside a panel
   rather than as another panel. */
.crew-group:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;gap:var(--s5);
  padding:calc(4px * var(--ui)) 0 calc(6px * var(--ui));margin-top:calc(4px * var(--ui))}
/* THE HAIRLINE THE COMMENT ABOVE PROMISES HAS NEVER BEEN DRAWN. Same
   undeclared --rule-2, and an invalid background computes to transparent
   rather than to currentColor -- so unlike the border above, this one was
   simply invisible. --hair-2 shows the division while staying under the gold
   the panel heads rule with, which is the "one step quieter" it asks for. */
.crew-group:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .spacer{flex:1;height:1px;background:var(--hair-2)}
.crew-group:first-child:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-top:0}
/* HIS OWN COMPANY, SET APART FROM THE REST OF THE STRIKE TEAM. Owner,
   2026-09-15: "when you're approving aars as the sgt I want to see your
   company seperated a bit from the rest of the brothers in the strike team."

   The heading was already drawn and carried FOUR PIXELS above it -- less than
   the gap between two brothers of the same squad -- so the first man he cannot
   answer for sat level with the last man he can, and the division he is meant
   to read was the only thing on the card that did not look like one.

   THE GAP IS THE DIVISION, not another rule: the group heading already draws a
   hairline of its own, and two lines four pixels apart is a box, not a break.
   The cards below it are stood down rather than hidden -- he still has to see
   who else was on the run, and the men he is answering for are the ones the
   eye should land on. Hover brings a stood-down card back to full, because
   reading a name is not the same as being asked for a word about it. */
.crew-group-other:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-top:calc(22px * var(--ui))}
.crew-card-other:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){opacity:.66}
.crew-card-other:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.crew-card-other:focus-within:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){opacity:1}
@media (prefers-reduced-motion:no-preference){
  .crew-card-other:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:opacity .14s ease}
}
/* WHICH ONE IS YOU. Set in the gold the page uses for the reader's own name
   everywhere else, so it is recognised before it is read. */
/* ON LEAVE. Owner: "he just gets a label on his name in the roster 'on leave'."
   Quiet on purpose -- it is a fact about a man, not a warning about him, so it
   reads like the rank beside it rather than the ON VOX badge, which is a thing
   happening right now. */
/* THE COMPANY AS ITS POST READS IT. A position is a heading in the chapter's
   own words with its men under it, indented by what it answers to -- the same
   shape a man sees in #legionnaires-rank-and-file, which is the point. */
.org-post-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;gap:var(--s5);align-items:baseline;
  padding:var(--s5) var(--s6) 2px var(--pad);margin-top:var(--s4);cursor:grab;border-radius:3px}
.org-post-row:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(230,223,204,.05)}
.org-post:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif);font-size:var(--fs-6);text-transform:uppercase;
  letter-spacing:.14em;color:var(--gold);flex:1 1 auto;min-width:0}
.org-count:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-3);color:rgb(var(--hue-68,92,106,97))}
.org-man-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;padding:2px var(--s6) 2px var(--pad);border-radius:3px}
.org-man-row:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(230,223,204,.03)}
.org-man:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-4);color:rgb(var(--hue-21,169,188,175));min-width:0}
.org-held:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){opacity:.4;cursor:grabbing;background:rgba(180,143,50,.12)}
.leave-badge:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:.08em;text-transform:uppercase;white-space:nowrap;
  padding:1px 5px;border:1px solid #4A5A6A;border-radius:3px;color:#8FA3B8}
.crew-you:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--gold);
  margin-left:calc(6px * var(--ui));padding:1px 5px;
  border:1px solid var(--rule-head);border-radius:2px;vertical-align:2px}
/* THE GUILD KNOWS HIM. A green disc and a mono word beside the YOU badge, the
   same measure as it so a man who is both does not get two different chips.
   It is drawn only when the last sweep listed him -- see teamOf(). */
.crew-dc:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:inline-flex;align-items:center;gap:calc(5px * var(--ui));
  font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--good);
  margin-left:calc(6px * var(--ui));padding:1px 5px;
  border:1px solid rgba(var(--hue-5,121,196,141),.35);border-radius:2px;vertical-align:2px}
.crew-dc-dot:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:4px;height:4px;border-radius:50%;background:var(--good);flex:none}

/* HIS THREAD, indented under his row so it reads as belonging to him rather
   than to the card's edge, and separated by a rule the way the log upstairs is. */
.crew-thread:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-top:calc(8px * var(--ui));margin-top:calc(2px * var(--ui));
  border-top:1px solid var(--rule)}
.crew-say:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-top:calc(6px * var(--ui))}

/* THE ESCALATE, which is neither an accept nor a refusal and must not read as
   either. The mockup's blue, used nowhere else on the card. */
.c-esc:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--esc)}
.btn-esc:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:rgba(122,146,196,.5);background:rgba(122,146,196,.16);color:var(--esc-lit)}
.h-esc-line:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:rgba(122,146,196,.55);color:var(--esc-lit)}

.pill-variance:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--bad-lit);
  padding:2px 7px;border:1px solid rgba(192,90,60,.34);border-radius:2px;
  background:rgba(192,90,60,.10);white-space:nowrap}
.record-hdr-end:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){text-align:right}
.record-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){align-items:center;min-height:calc(34px * var(--ui))}
/* STRETCHED, AND RULED ON ITS LEADING EDGE. The divider belongs to the COLUMN,
   not to the line of type, so it runs the full height of the row -- which is
   what makes six rows read as a table rather than as six stacked pairs. */
.record-mid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;align-self:stretch;min-width:0;
  padding-left:var(--s6);border-left:1px solid var(--hair)}
/* NO overflow:hidden, and the mockup's own rule would have been a bug here:
   the override menu is an absolutely-positioned .pop hanging off this cell, and
   a clipped cell would cut it off at the row's edge. min-width:0 is what the
   ellipsis beside it actually needs. */
.record-end:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;justify-content:flex-end;align-items:center;
  gap:var(--s4);min-width:0}
/* THE TABLE FOLDS, because the mockup never had to. It is drawn at a hard
   min-width:1280px, where three tracks with a 480px floor always fit; this app
   runs down to a phone, where 480px of tracks inside a 340px panel would push
   the row's own text off the side of the screen. One column, no rule, heads
   dropped -- every row already carries its field name in .decide-k.
   These sit HERE and not in the block at the top of the sheet: a media query
   adds no specificity, so written up there they would lose to the base rules
   above on source order. */
@media (max-width:999px){
.record-hdr:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none}
.record-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){grid-template-columns:1fr;gap:var(--s3);align-items:flex-start}
.record-mid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){align-self:auto;padding-left:0;border-left:0}
.record-end:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){justify-content:flex-start}
}

/* Sized to its own column rather than to the panel, so a long explanation
   makes its field taller instead of stretching the whole row. */
.decide-help:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-3);color:var(--ink-4);line-height:1.4;
  max-width:calc(260px * var(--ui))}

.decide-worth:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-left:auto;font-family:var(--mono);font-size:var(--fs-3);
  color:var(--gold-dim);text-align:right}

/* ==========================================================================
   THE MUSTER -- the companies, and the one standing open beside them.

   Owner, 2026-09-01: "lets workflow the whole companies tab in the roster i
   want that to look and feel fucking awesome", then "i want it to look and
   feel good NOT POPULATE IT", then "yeah look feel functionality framework for
   when the bot is done... it looks like shit right now... and is clunky to
   use."

   WHAT WAS WRONG WAS NEVER THE CONTENT. Seven near-identical cards filled the
   top third of a 1600px screen and abandoned the rest; the grid resolved to
   four columns so the seventh row was three cells short; the widest run on a
   card about a company was its sergeant's name; and every place inside an
   opened company got a full-width iron plate whether it held six men or one.
   Nothing below adds a fact to the screen. One count is repaired and one
   sentence -- "no discord role", printed fourteen times above the men on the
   Legion -- is deleted rather than restyled.

   .co-slot is DELETED, not kept out of caution: declared, used nowhere, and
   named in .rail's own doctrine as the inferior version of itself -- "and
   :focus-within so a keyboard can reach it, which .co-slot's version could
   not". .co-group is deleted too. It is a rank-group divider and it was used,
   once, to wrap the LAST line of a company card, where its :first-child escape
   hatch can never fire -- so every card carried an accidental 13px indent and
   an accidental --hair-3 rule, the outermost division in this sheet, drawn
   inside a 300px card. Nobody chose that. .co-grid goes with the grid;
   .quote-grid keeps the rule it was sharing.

   ORNAMENT AUDIT
     the plate's rivets     BOUNDARY  the tab is one panel, said once
     the roll row's rule    BOUNDARY  where one company ends
     .co-lead               BOUNDARY  where a place's name ends and the figure
                                      that answers it begins. A leader: the
                                      oldest device in typesetting, and the
                                      only thing on this screen that is not a
                                      letter.
     .rail-act hover/press  STATE     you are here, and you are pressing
     .rail-on               STATE     this company is the one standing open
     .rail-you              WHOSE     yours -- a shape, not a second gold
     .mark-read .mark-lamp  STATE     a machine fills this and is watching it
   THREE OF THE FOUR LAMPS ARE UNSPENT HERE. --glow-owed and --glow-spent are
   left alone on purpose: when the bot starts governing a place there will be a
   real machine state to light, and a screen that has already used its lights
   to say "this is a company" will have nothing left to say it with.
   ========================================================================== */

/* Two names for one grid now: the quotes, and any plate laid beside another.
   The companies left it -- seven cards in an auto-fill grid resolve to four
   columns at 1440px, and seven is 4+3 with a hole. */
.quote-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.plate-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;gap:var(--s5);
  grid-template-columns:repeat(auto-fill,minmax(calc(280px * var(--ui)),1fr))}

/* THE WHOLE TAB IS ONE PLATE. A grid of cards de-rivets because twelve riveted
   rectangles say "this is a panel" twelve times; a single panel rivets, which
   is the same rule read the other way.

   overflow is the INITIAL value restated, and it is a guard rather than a
   layout: .plate does not clip today, and this is (0,1,0) declared after it,
   so if a later hand gives .plate an overflow the place menus still escape
   their column. Said plainly because a rule that only restates a default
   reads as load-bearing to the next person, and this one is only a latch. */
.muster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){overflow:visible}

/* THE SPLIT. 1000px is the threshold the interface already calls `wide` and the
   one --ui steps at, and renderVals decides which panes exist, so the layout
   and the script cannot disagree about which shape they are in.
   min-height so a four-brother company is FRAMED rather than a stub under a
   full roll -- the plate reaches, and the room shows below it, which is what
   the fixed arch behind this screen has always been for. */
.muster-split:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:stretch;min-width:0;gap:var(--s6);padding:var(--s6)}
@media (min-width:1000px){ .muster-split:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-height:calc(480px * var(--ui))} }
@media (max-width:999px){ .muster-split:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex-direction:column;padding:var(--s5);gap:var(--s5)} }

/* A PANEL EACH, WITH THE SAME DEPTH EVERY OTHER BLOCK ON THE SITE HAS. Owner,
   2026-09-11: "it needs sepertion the middle and the left both need tiehr own
   depth panels." One hairline between two panes made them one pane with a rule
   down it; these are two objects now -- lit top edge, cast shadow, the plate's
   own face -- sitting in the plate that holds them both. */
.muster-roll:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.muster-one:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;border-radius:var(--r2);
  background:var(--panel-3);background-image:var(--face-1);
  border:1px solid var(--line-3);border-top-color:rgba(var(--hue-8,210,225,210),.14);
  box-shadow:var(--bevel),var(--cast-2)}
.muster-roll:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:0 0 calc(330px * var(--ui));min-width:0;overflow:hidden}
.muster-roll:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .rail:last-child{border-bottom:none}
/* A LITTLE AIR BETWEEN THE COMPANIES. Owner: "the companines tab on the left
   needs a little seperation". Seven companies stacked on one rule each read as
   a single ruled block; a company is a thing you pick, not a line you scan.
   The rules stay -- this roll is a list of choices and they mark where one
   ends -- and the room around them grows, which is the answer that has worked
   every other time on this site. */
.muster-roll:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .rail{padding-top:var(--s5);padding-bottom:var(--s5)}
.muster-one:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1 1 auto;min-width:0}
/* A CHAPTER WITH NO COMPANIES IN IT. shownCo is null, so atCoOne is false and
   the roll is the only pane -- and an sc-if compiles to a React.Fragment with
   no wrapper element (support.js:645), so the roll genuinely IS the only child
   here. Without this, the first screen a new chapter ever sees is NO COMPANIES
   YET centred in a 363px gutter with a 528px vertical rule beside it and a
   thousand pixels of empty plate to its right: the abandoned screen this whole
   rebuild exists to kill, in the one state it cannot be argued with. */
.muster-roll:only-child:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1 1 auto}
@media (max-width:999px){
  .muster-roll:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1 1 auto}
}

/* A COMPANY IS A ROW IN A LIST, and everything else about it is the rail's --
   the brass rule under it, the warm hover, the 3px state edge, the
   :focus-within that reveals its own controls. Only the axis changes: two
   lines rather than one, and --s5 top and bottom against .rail's --s4, because
   seven of these are the whole left-hand side of the screen and are read at
   arm's length rather than scanned at a hundred an evening. */
.co-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s5) var(--s7);gap:var(--s5)}

/* THE FIGURE, IN ITS OWN COLUMN, so the seven form a column down the roll
   instead of floating to wherever each name ran out. text-align:right and
   .num's tabular figures, because a column that twitches is not a column. */
.co-tally:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;display:flex;flex-direction:column;align-items:flex-end;
gap:var(--s1);text-align:right}

/* YOURS AND OPEN AT ONCE, which is the row the owner looks at first.
   .rail-on and .rail-you are both (0,1,0) and .rail-you is declared second, so
   without this the union silently loses .rail-on's lit rail AND its whole
   ember. The sheet already wrote three repairs of exactly this shape for the
   hover pairs at :676; this pair was never written because nothing had ever
   used .rail-on. */
.rail-on.rail-you:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel-3);
box-shadow:inset 3px 0 0 var(--gold-lit),0 0 20px -12px var(--glow-live)}
/* AND THE FOURTH RULE IN THE FAMILY, which the block above reproduced the very
   collision it was written to repair. .rail-on.rail-you is (0,2,0) and lands
   after .rail-act:hover, which is also (0,2,0) -- so on the one row the owner
   reaches for first, YOUR OWN OPEN COMPANY, the warm ground never arrived and
   hovering it produced no visual change whatsoever. The rail and its ember are
   already correct from .rail-on.rail-act:hover at :678; only the ground was
   being taken back, and only here. */
.rail-on.rail-you.rail-act:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--lift);
box-shadow:inset 3px 0 0 var(--gold-lit),0 0 20px -12px var(--glow-live)}

/* A PRESS, which this sheet has never expressed anywhere, and --lift-hard has
   sat in the tokens with no user since it was named. One step of the same warm
   lamp. On a phone it is the only answer a thumb ever gets. */
.muster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .rail-act:active{background:var(--lift-hard)}

/* A KEYBOARD CAN REACH EVERY CONTROL ON THIS TAB NOW, so it has to be able to
   see where it is: the sheet's only focus style was input:focus, and the three
   text boxes were the only focusable things on 8467 lines. Inset by a pixel so
   a focused row does not grow. */
.muster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) :focus-visible{outline:1px solid var(--gold);outline-offset:-1px}

/* A THUMB HAS NO HOVER. .on-hover is opacity-only so a screen reader keeps it
   and :focus-within so a keyboard reaches it -- and a touch device, which has
   neither, lost it completely. Scoped to this tab so no other screen changes. */
@media (hover:none){
  .muster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .rail .on-hover,.muster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .co-place-head .on-hand{opacity:1}
}

/* ---- one company, opened ------------------------------------------------ */

/* TWO PLAIN COLUMNS, and the logic decides how many. A grid with a full-width
   item leaves the remainder of its row empty every time one is reached, which
   the Legion's eleven one-man places interleaved with three squads guarantees;
   an equal-height grid reproduces the hollow stretched card one level down;
   and multicol re-balances every column the instant an inline forge opens, so
   places jump under the hand. Two prefix/suffix columns are newspaper order,
   which is the written order, which IS the chain of command. */
.co-cols:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:flex-start;gap:0 var(--s8);
padding:var(--s5) var(--s7) 0}
/* THE MEASURE OF ONE COLUMN, which the two-column case gave it for free and the
   one-column case never had. Between 1000 and 1399 the script hands the CSS a
   single column and it filled the pane: at 1240 that is a 756px row with a
   man's name at the far left and the two glyphs that act on him at the far
   right, and a place's leader stretched 600px of brass between a name and its
   count. Measured: 917.8px at 1399, 755.9 at 1240. The cap never binds on the
   two-column layout, where each column is about 452px at 1440, and :only-child
   says so rather than leaving it to arithmetic. */
/* THE MEASURE BINDS ON EVERY COLUMN, NOT JUST A LONE ONE. It was written for
   the one-column case and the paragraph above says why it was thought
   unnecessary elsewhere: "The cap never binds on the two-column layout, where
   each column is about 452px at 1440." That is true at 1440 and false at the
   width a man actually has. On a 2454px window the roll takes 330 and the two
   columns take about a thousand EACH, which is the very shape the cap exists
   to prevent -- a place's leader stretching six hundred pixels of brass
   between his name and a count of one -- arriving on the layout nobody
   measured. Owner, seeing it: "i hate this... its strange looking."
   One measure, every column, however many the script hands over. */
.co-col:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1 1 0;min-width:0;max-width:calc(560px * var(--ui))}

.co-place:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:0;padding-bottom:var(--s6)}

/* A BIG PLACE FLOWS INTO COLUMNS RATHER THAN RUNNING OFF THE SCREEN. Owner:
   "this just has so much ampty space and scrolls down".

   The split above says plainly why it cannot fix this by itself: "the order of
   places IS the chain of command, and re-ordering to square the columns would
   throw away the one fact the order carries... the room under RESERVES is the
   shape of the 10th, not a fault." That is true of the PLACES. It is not true
   of the MEN inside one: seventy-three reserves have no order worth keeping
   beyond their run headings, and a tower of them is what makes the 10th scroll
   while a third of the window sits empty.

   So the chain of command keeps its order and a crowd stops pretending to be a
   queue. Only a place big enough to be a wall gets this -- below that a second
   column would put two names beside each other and call it a layout. */
.co-men-split:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){column-width:calc(230px * var(--ui));column-gap:var(--s8)}
/* A man, and the run heading above him, must not be torn across the break. */
.co-men-split:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > *{break-inside:avoid}
.co-men-split:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > .rail-group{break-after:avoid}

/* A PLACE IS NOT A PLATE. Fourteen bolted iron boxes, one of them 130px tall to
   hold a single name, is what the Legion screen was -- and .plate-bare was on
   for all fourteen, which is a plate with the one ornament that makes it a
   plate switched off, and the clearest possible tell that they were never
   plates. .plate-head-sm was written for exactly this and has never been used
   once: its own comment says so, "a tier in the Strategium, A PLACE INSIDE A
   COMPANY". */
/* ONE LINE, AND IT DOES NOT BREAK. .plate-head wraps, which is right for a
   screen head and wrong here: flexbox breaks a line BEFORE it shrinks
   anything, using each item's hypothetical size, so min-width:0 on the name
   cannot save the tail. Measured at 1440: CHIEF APOTHECARY leaves 6px of
   headroom and MASTER OF SANCTITY carrying a role mark needs 31px more than
   the column has -- at which point the count and the ⋯ drop to a second flex
   line, LEFT-aligned, under the name, at the opposite end of the head from
   where the reader learned the control lives. It is worse on a phone, where
   any two-digit count does it. Held to one line, a long name shrinks and
   wraps inside its own box instead and the tail stays where it was put. */
/* NAME, LEADER, COUNT -- AND NOTHING UNDER IT. Owner: "we dont need that line
   under grand master or leutenants or reclusium just a single name and line
   name -------------- n brother. would be nice."

   .plate-head-sm carries a --hair rule below, which is right for a tier in the
   strategium and wrong for a place: the head already draws a line, the brass
   one running from the end of the name to the figure answering it. Two lines
   thirty pixels apart on a place holding one man is why a company of small
   places read as ruled paper. The one that carries meaning stays; the one that
   only says "a heading was here" goes, and the space above the next place says
   it instead. */
.co-place-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex-wrap:nowrap;border-bottom:none;padding-bottom:0}

/* A PLACE'S NAME IS A HEADING AND KEEPS THE FAMILY, BUT NOT THE STAMP. The
   component's ::after is a 2px gold rule under the name; with .co-lead's brass
   hairline at cap-centre and .plate-head-sm's own --hair rule below, a 30px
   head drew THREE horizontal lines in three colours, and at a place's measure
   they read as one broken stepped line rather than as three ornaments. The
   leader is the one that carries meaning -- where the name ends and the figure
   answering it begins -- so it is the one that stays, which is also what
   .co-lead's own comment claims and could not have been true beside a stamp. */
.co-place-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .heading::after{display:none}

/* THE LEADER. --rule brass, the same hairline that divides one company from the
   next in the roll, so the screen has one dividing colour and not two. Centred
   rather than on the baseline the two ends share: at the baseline it strikes
   through the descenders of the name it is leaving. */
.co-lead:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1 1 var(--s7);min-width:var(--s7);height:0;align-self:center;
border-bottom:1px solid var(--rule)}

/* THE ROLES A PLACE STANDS FOR, when there is more than one and the mark cannot
   name them all. Its own line UNDER the head rather than a wrapped flex item
   inside it, because the head no longer wraps -- and indented to --s4 so it
   starts on the same margin as the run headings and the men, which is where
   everything else that belongs to the place begins. */
.co-roleline:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:block;padding:var(--s3) 0 0 var(--s4)}

/* THE ONE GLYPH A PLACE REVEALS. Deliberately NOT named .on-hover: a place
   contains rails, `.rail .on-hover` is already scoped to the rail, and reusing
   the name here would light every man's x the moment a hand entered the place
   -- which is the ninety-glyph screen this replaces, arrived at from the other
   direction. Two reveals at two scales need two names. */
.co-place-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .on-hand{opacity:0;transition:opacity var(--t)}
.co-place-head:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .on-hand,
.co-place-head:focus-within:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .on-hand{opacity:1}

/* The confirmation is the menu's own box with the question in it: same
   component, no new one, and nothing floats over the page that the reader did
   not open.

   .pop.co-ask, not .co-ask, and the doubled class is load-bearing: the sheet
   sizes the floating menu a second time in the UTILITIES region at :2162, and
   the utilities land after the components. At (0,1,0) this rule would set a
   measure that a later rule of equal weight silently took back -- the exact
   class of defect the ordering comment on this block exists to prevent. A
   sentence needs more measure than a list of four words does. */
.pop.co-ask:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s4);padding:var(--s5);min-width:calc(268px * var(--ui))}

/* A man's rank, on a place holding one man, where no run heading says it. */
.co-office:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){max-width:calc(160px * var(--ui))}

/* AN EMPTY PLACE at a place's measure. The Risen's one place is bound to four
   Discord roles and holds nobody -- the one spot on this site where the ledger
   says a machine is watching this and has found nobody yet -- and it was a grey
   mono line in --ink-4. Same component as the screen-scale empty, two thirds
   the size, and still UNLIT: an empty state must never be the brightest thing
   on a screen, or absence reads as an event.
   Indented to --s4 with the run headings and the men: without it the sentence
   about who is missing began a whole indent to the LEFT of every man who is
   present, which is the one place on the screen it must not do. */
.co-empty:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s6) 0 var(--s6) var(--s4);gap:var(--s4);
align-items:flex-start;text-align:left}
.co-empty:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .empty-mark{width:calc(30px * var(--ui));height:calc(30px * var(--ui));
font-size:var(--fs-6)}
.co-empty:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .empty-line{font-size:var(--fs-5)}

/* THE BAR WRAPS ON A PHONE, HERE, because this one carries a decision and not
   only a filter. Below 1000px the shared bar goes flex-wrap:nowrap with
   overflow-x:auto and no scroll affordance of any kind: measured at 390px its
   content was 480px in a 332px box, so a button could sit with its centre off
   the screen and be reached only by guessing that a 27px strip scrolls
   sideways. A control you cannot see is not a control. THE SECOND BUTTON HAS
   SINCE GONE -- `build from discord` was the one measured off the edge, and
   the seed it triggered runs on its own after every worker push -- but the
   rule stays: the search placeholder is the longest on the site at 164px and
   wrapping is still the only way it fits the box it is in. Scoped to .muster
   so no other list screen changes. */
@media (max-width:999px){
  .muster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>.bar{flex-wrap:wrap;overflow-x:visible}
  .muster:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>.bar>.search{flex:1 1 100%;min-width:0}
}

/* THE SIDES OF A HEAD WITH NO PLATE UNDER IT. The phone override at :1612 sets
   .plate-head's padding back to --s7 and is declared AFTER .plate-head-sm at
   equal specificity, so on a phone the small head silently gets its side
   padding back and steps 16px outboard of the .rail-sm rows beneath it -- the
   same defect the sheet logged and fixed for the full head. It has never bitten
   because .plate-head-sm has never been used. It is used now. */
@media (max-width:999px){
  .plate-head-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s4) 0 var(--s2)}
}

@media (prefers-reduced-motion:reduce){
  /* A guttering candle is still a lit candle: the movement goes, the light
     stays. .anim-enter, .danim and .forge are already cut by the sheet's own
     three hooks; these two are the transitions it does not reach. */
  .co-place-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .on-hand,.rail:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .on-hover{transition:none}
}

/* ==========================================================================
   REDUCED MOTION -- one consolidated block, the same pattern the sheet
   already uses for .act/.btn/.chip and for .boot-leaving. The quote
   crossfade is disabled outright rather than slowed: the outgoing instance
   is pinned to opacity:0 and the incoming to opaque and sharp, so the swap
   still happens, it just happens as a cut instead of a slide-and-blur.
   ========================================================================== */
@media (prefers-reduced-motion:reduce){
  .quote-in:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.quote-out:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none}
  .quote-in:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){opacity:1;filter:none;transform:none}
  .quote-out:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){opacity:0;filter:none;transform:none}
  .subtab:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.decide-v:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:none}
}

@keyframes quotein{
  0%{opacity:0;filter:blur(6px);transform:translateY(10px)}
  100%{opacity:1;filter:blur(0);transform:translateY(0)}
}
@keyframes quoteout{
  0%{opacity:1;filter:blur(0);transform:translateY(0)}
  100%{opacity:0;filter:blur(6px);transform:translateY(-14px)}
}

/* A brother's own Discord picture, filling the same circle the initials use.
   The URL lives in data-lazy-bg until IntersectionObserver sees the circle. A
   CSS background in the rendered row is fetched even when that row is hundreds
   of places below the viewport; the roster used to download every full-sized
   Discord avatar on first open. Initials remain visible until the image loads.

   THE SELECTOR IS DOUBLED and that is load-bearing. `color:transparent` is
   what hides the initials once the picture is behind them, and at (0,1,0) it
   was silently taken back by .c-2 -- also (0,1,0), declared 96 lines later in
   the utilities -- on every face whose class string carries a colour. Measured
   on the companies tab: "avatar avatar-sm c-2 lazy-avatar avatar-face" resolved
   to rgb(var(--hue-21,169,188,175)), so two-letter initials were drawn ON TOP of fifty-three
   brothers' photographs. .avatar.avatar-face is (0,2,0) and cannot lose to a
   utility whatever order the sheet is read in. */
.avatar.avatar-face:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:transparent;background-size:cover;background-position:center;
background-repeat:no-repeat;text-shadow:none}

/* Owner: "put notes in its own section in a bigger text box under overrides",
   and this was 132px tall with 96px of padding under the caret to answer it.
   THE ELEMENT IS AN <input>, not a textarea -- it has always been one line of
   text -- so the height bought no room to write in, only 96px of dead box
   below a caret pinned to the top of it. The mockup draws a single-line
   composer, which is what the control actually is. If he wants the room back
   it is a textarea, not a min-height. */
.notes-box:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:100%;line-height:1.5;
  padding:var(--s5) var(--s6);
  font-family:var(--sans);font-size:var(--fs-5)}

/* ==========================================================================
UTILITIES — last, so they win.
========================================================================== */

/* ---- layout ---- */
.col:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;flex-direction:column}
.row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center}
.row-base:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:baseline}
.row-top:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:flex-start}
.row-stretch:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:stretch}
.wrap:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex-wrap:wrap}
.nowrap:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){white-space:nowrap}
.grow:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1}
.grow-0:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1;min-width:0}
.spacer:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:1}
.fn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none}
.min0:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:0}
.mh0:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-height:0}
.h100:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){height:100%}
.ac:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){align-items:center}
.ab:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){align-items:baseline}
.ae:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){align-items:flex-end}
.at:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){align-items:flex-start}
.as:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){align-self:flex-start}
.jc:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){justify-content:center}
.jb:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){justify-content:space-between}
.je:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){justify-content:flex-end}
.fill:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute;inset:0}
.abs:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:absolute}
.fixed-fill:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;inset:0}
.rel:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative}
.clip:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){overflow:hidden}
.scroll:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){overflow:auto}
.scroll-x:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){overflow-x:auto}
.scroll-y:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){overflow-y:auto}
.none:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){pointer-events:none}

.g1:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s1)} .g2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s2)} .g3:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s3)} .g4:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s4)}
.g5:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s5)} .g6:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s6)} .g7:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s7)} .g8:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s8)}

.p0:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:0}
.p4:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s4)} .p5:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s5)} .p6:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s6)}
.p7:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s7)} .p8:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s8)} .p9:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s9)}
.px5:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s3) var(--s5)} .px6:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s4) var(--s6)}
.px7:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s5) var(--s7)} .pad-cell:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s4) var(--s8)}
.pad-card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s6) var(--s8)} .pad-panel:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s7) var(--s8)}
/* A section heading sits closer to what it introduces than to what is above
it, which is why the three-value paddings existed in eleven places. */
.pad-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s7) var(--s8) var(--s3)}
.pt3:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-top:var(--s3)} .pt5:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-top:var(--s5)}
.pb5:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-bottom:var(--s5)} .pb7:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-bottom:var(--s7)}
.py5:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s5) 0} .py3:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s3) 0}

.m0:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin:0} .m-auto:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin:auto}
.mt1:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-top:var(--s1)} .mt2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-top:var(--s2)}
.mt4:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-top:var(--s4)} .mt6:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-top:var(--s6)} .mt7:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-top:var(--s7)}
.mb4:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-bottom:var(--s4)} .mb6:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-bottom:var(--s6)} .mb7:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-bottom:var(--s7)}
.mx7:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin-left:var(--s7);margin-right:var(--s7)}
/* THE GUTTER, and it is one number so nothing can drift out of line with
   anything else. Every screen used to run to the glass: the roster's first
   column, the reports table and the report's own sticky header all began at
   pixel zero, which on a wide monitor reads as an unfinished window rather
   than a full one. The nav bar above takes the same value, so the sigil, the
   tabs and the content below them share one left edge.

   It scales with the window rather than sitting at a fixed width -- a laptop
   cannot spare 40px and a 32-inch monitor wants more than 12 -- and it is
   applied to the SCROLL CONTAINER, so a sticky header inside a screen is
   inset with everything else instead of sliding out from under it. */
.gutter:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-left:var(--gut);padding-right:var(--gut)}

/* ---- type ---- */
.mono:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono)}
.serif:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--serif)}
.num:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-variant-numeric:tabular-nums}
.t1:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-1)} .t2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-2)} .t3:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-3)}
.t4:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-4)} .t5:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-5)} .t6:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-6)}
.t7:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-7)} .t8:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-8)} .t9:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-9)}
.t10:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--fs-10)}
.w5:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-weight:500} .w6:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-weight:600} .w7:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-weight:700}
.ls1:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){letter-spacing:var(--ls-1)} .ls2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){letter-spacing:var(--ls-2)} .ls3:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){letter-spacing:var(--ls-3)}
.ls-key:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){letter-spacing:var(--ls-key)} .ls-ctl:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){letter-spacing:var(--ls-ctl)}
.up:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){text-transform:uppercase}
.right:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){text-align:right} .center:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){text-align:center}
.lead:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){line-height:1.45} .tight:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){line-height:1.1} .snug:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){line-height:1.3}
.ellipsis:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.c-ink:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink)} .c-cool:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-cool)} .c-2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-2)}
.c-3:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-3)} .c-4:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-4)} .c-5:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-5)}
.c-6:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-6)} .c-gold:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold)} .c-gold-lit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-lit)}
.c-gold-warm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-warm)} .c-gold-dim:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-dim)}
.c-ash:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-ash)} .c-good:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--good)} .c-warn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--warn)}
.c-bad:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--bad)} .c-bad-lit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--bad-lit)} .c-cyan:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--cyan)}
.c-on-gold:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-on-gold)}

/* ---- fills ---- */
.bg-base:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--bg)} .bg-deep:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--bg-deep)}
.bg-panel:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel)} .bg-panel2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel-2)}
.bg-panel3:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel-3)} .bg-panel4:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--panel-4)}
.bg-pop:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--pop)} .bg-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--row-alt)}
.bg-tile:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--tile)} .bg-track:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--track)}
.bg-line:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--line)} .bg-gold:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--gold)}
.bg-gold-lit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--gold-lit)} .bg-good:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--good)}
.bg-bad:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--bad)} .bg-cyan:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--cyan)}
.bg-none:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:transparent}

/* ---- edges ---- */
.bd:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--gold-line)}
.bd-hair:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--hair-2)}
.bd-line:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--line-3)}
.bd-gold:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--gold)}
.bd-bad:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--bad)}
.bd-tile:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--tile-edge)}
.bd-none:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:none}
.r1:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-radius:var(--r1)} .r2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-radius:var(--r2)} .rc:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-radius:50%}

.anim-enter:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:enter var(--dur-screen) var(--ease-settle)}
.sticky:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:sticky}
.grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid}
.pre:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){white-space:pre-wrap}
.block:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:block}
.fit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){object-fit:contain}
.cover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){object-fit:cover}
.bd-warn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--warn)}
.bd-good:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:1px solid var(--good)}
.pl4:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-left:var(--s4)}
.p2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s2)} .p3:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s3)}
/* A .tile with no size is a square that drifts to 22px on one tab and 26 on
another, which is exactly the class of bug this stylesheet exists to stop.
Same for the floating menu's width. */
.tile-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:calc(24px * var(--ui));height:calc(24px * var(--ui))}
.tile-lg:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:calc(34px * var(--ui));height:calc(34px * var(--ui))}
.pop:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:calc(230px * var(--ui))}

/* ==========================================================================
   DEPTH — the other half of the lighting model.

   The sheet already had ONE lighting rule and only half of it: --bevel, a lit
   top lip and a dark bottom, light from directly above. What it never had was
   the second half of that sentence — the shadow that light casts, and the face
   it falls across. Every panel in the app was a 1px outline with a 1px lip and
   nothing underneath, which is why a screen of them read as a drawing rather
   than as plate bolted to a hull. Owner: "which is mostly just adding depth."

   THE LADDER. Four steps, and there is no fifth:
     .raise-2 / --cast-2   a panel standing on the page
     .raise-1 / --cast-1   a thing resting on a panel
     (nothing)             a thing that IS part of the panel
     .sink-1  / --sink-1   a groove: what you type into, a track
     .sink-2  / --sink-2   a well: a body of text, a list you scroll into

   THE TOP EDGE IS LIT AFTER ALL, and the refusal that stood here was an
   arithmetic mistake. It read: "rgba(var(--hue-8,210,225,210),.16) over a plate face
   composites to rgb(var(--hue-59,46,59,52)), DARKER than the --line-3 border already painted
   there". Over the plate's actual top ground rgb(var(--hue-60,21,36,28)) the border comes to
   rgb(var(--hue-61,41,58,47)) and the light to rgb(var(--hue-62,53,68,59)) -- brighter on every channel.
   The mockup draws it on every panel, so .plate carries it now, and the plate
   family's inner lip is --lip (one faint cool line, the mockup's) rather than
   --bevel (a warm gold line plus two black edges nothing in the mockup has).
   --bevel is untouched and still lights every other surface.
   ========================================================================== */
:root{
/* EACH CAST IS TWO LAYERS. The wide ambient carries a NEGATIVE SPREAD equal to
   its own y-offset (0 16px 34px -16px), so the blur can never escape sideways
   past the element's own footprint and the shadow pools strictly underneath —
   which is what a lamp directly overhead actually does. A shadow at spread 0
   halos on four sides and reads as a glow, and this sheet already owns four
   --glow-* lamps that mean STATE; depth must never be mistakable for one. The
   tight contact layer (y 2-3px, blur 5-8px) is what makes a thing read as
   TOUCHING the surface rather than floating over it — without it a big soft
   shadow is fog. Retuning the app's depth is now these six lines. */
--cast-1:0 10px 22px -12px rgba(0,0,0,.85),0 2px 5px -2px rgba(0,0,0,.5);
--cast-2:0 16px 34px -16px rgba(0,0,0,.9),0 3px 8px -3px rgba(0,0,0,.55);
/* The inverse, and it inverts every part: inner not outer, from the TOP edge
   because that is the edge facing away from the light, and no cast at all. */
--sink-1:inset 0 1px 3px rgba(0,0,0,.5);
--sink-2:inset 0 2px 8px rgba(0,0,0,.6);
/* THE FACE. background-IMAGE, never a shorthand, so it layers over whatever
   ground a thing already has and any later `background:` (.dbg, the per-man
   tint) still wins outright. 180deg — the same direction as the lip and the
   cast, so face shading, edge light and shadow all agree. Green at the top
   because the lamp in this room is green-lit; black at the bottom because
   shade has no colour of its own. Both bottom out TRANSLUCENT, so the body's
   radials keep coming up through the lower half of every panel: an opaque
   panel is a hole punched in the page and the scene goes flat.
   --face-2 is the LIGHTER of the two, and that is not a typo — it is for what
   sits NEARER the lamp, so a card nested in a panel is brighter than the panel
   and elevation stays legible with shadows switched off. */
--face-1:linear-gradient(180deg,rgba(var(--hue-32,60,92,72),.16),rgba(0,0,0,.20));
--face-2:linear-gradient(180deg,rgba(var(--hue-32,60,92,72),.24),rgba(0,0,0,.16));
}

/* ---- the components take the ladder, every page at once --------------------
   .plate is 37 elements across every screen and .plate-bare is already the
   sheet's word for "a plate inside something", so the whole app steps up in
   two rules instead of 37 class attributes — and a nested card steps DOWN one
   automatically, with no markup to keep in sync. Deleting this block is the
   entire undo. */
.plate:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background-image:var(--face-1);box-shadow:var(--lip),var(--cast-2)}
.plate-bare:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background-image:var(--face-2);box-shadow:var(--lip),var(--cast-1)}

/* THE STATE PANELS KEEP THEIR LAMP. .plate-lit and .plate-owed are declared
   above at the same specificity, so without these four repairs the rule above
   would drop the glow from exactly the panels that have something to say — a
   panel going flatter at the moment it became important, which is the bug the
   --bevel comment records having already fixed once. */
.plate-lit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--lip),var(--cast-2),0 0 26px -12px var(--glow-live)}
.plate-owed:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--lip),var(--cast-2),0 0 26px -12px var(--glow-owed)}
.plate-bare.plate-lit:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--lip),var(--cast-1),0 0 26px -12px var(--glow-live)}
.plate-bare.plate-owed:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--lip),var(--cast-1),0 0 26px -12px var(--glow-owed)}

/* A card and a banner already took the plate's lip; they take its cast too, at
   the NESTED step, because they sit inside sections rather than on the page.
   The forge keeps its ember and gains the ground under it. */
.card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.banner:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background-image:var(--face-1);box-shadow:var(--bevel),var(--cast-1)}
.forge:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--bevel),var(--cast-1),0 0 24px -14px var(--glow-live)}

/* WHAT YOU TYPE INTO IS CUT INTO THE PANEL, NOT SITTING ON IT. One groove, on
   both text surfaces the app has, so a field and a search box are the same
   depth on every screen. */
.field:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.search:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--sink-1)}

/* ---- the four steps as classes, for everything that is not a component ----
   A tile, a bar, a drawer, a reading well. Composable: they set one property
   and expect a border and a ground from the classes already on the element. */
.raise-1:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--bevel),var(--cast-1)}
.raise-2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--bevel),var(--cast-2)}
.sink-1:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--sink-1)}
.sink-2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--sink-2)}
.face-1:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background-image:var(--face-1)}
.face-2:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background-image:var(--face-2)}

/* THE PINNED SURFACE, with a name. The nav, the mobile nav and .bar all solve a
   sticky translucent surface with blur(7px) and .bar's own comment calls that
   "one material"; the AAR header was a fourth pinned surface solving it with a
   hand-written gradient and no blur. background-COLOR rather than the
   shorthand, so a face can be laid over it. */
.glass:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background-color:var(--panel);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px)}

/* position:sticky;top:0;z-index:3 was written inline on three pages. .sticky
   above sets position and nothing else, which is exactly why nobody used it. */
.stick-top:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:sticky;top:0;z-index:3}

/* A THING THAT RISES UNDER THE HAND — the tile going from resting on the panel
   to held above it. THE FULL TRANSITION LIST IS RESTATED ON PURPOSE: a bare
   `transition:box-shadow` here lands after .shot at equal specificity and would
   REPLACE its list, and the tile would stop lifting at the moment it was given
   a shadow. If .shot's transition ever changes, change this with it. */
.h-raise:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:box-shadow var(--t),transform var(--t),border-color var(--t),background var(--t)}
.h-raise:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--bevel),var(--cast-2)}

/* THE ACCENT RAIL, TAKING ITS COLOUR FROM THE d* HANDOFF. The AAR banner drew
   this a second way, as border-width:0 0 0 3px — which consumes the element's
   border box, so it could never also carry an outline. `inset 3px 0 0` is the
   sheet's one spelling of an accent rail (.rail); this is that spelling with a
   computed colour, so the two idioms become one. */
.drail:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:inset 3px 0 0 var(--dbd)}

/* ---- and the literals that were inline, given names ------------------------
   THE STATE DOT, one component instead of three spellings of it at 8, 9 and
   11px on a single screen. .tick is the 11px one and stays; these are the two
   that were written inline, one of them with a hardcoded #D8B45A that is
   verbatim the value of --gold-warm. */
.pip:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:9px;height:9px;flex:none}
.pip-sm:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:8px;height:8px;flex:none}
.pip-gold:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--gold-warm);border-color:var(--gold-warm)}
/* A reason that actually cost him the points, against one that only notes something. */
.pip-bad:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--bad);border-color:var(--bad)}

/* A BUTTON THAT IS OFF. It was two colour utilities and an inline cursor, so
   the shared layer had a state it could not say. */
.btn-off:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){cursor:not-allowed;color:var(--ink-6);border-color:var(--line-3)}

/* THE DRAWER THAT OPENS UNDER A ROW: an editor cut into the card it belongs to,
   which is why it sinks rather than rises. Its entrance was a CONSTANT passed
   through --danim on three byte-identical elements — the dynamic hook being
   used as a literal. It reuses the sheet's `enter` keyframe, the same one the
   forge opens with. */
.drawer:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--sink-2);animation:enter var(--dur-in) var(--ease-settle)}

/* Sizes that were inline literals. .pop-wide is after .pop's min-width above,
   so it wins -- and that is exactly how it went wrong: .pop is
   calc(230px * var(--ui)) and this was a raw 250px, so on the desktop the WIDE
   popup came out 250px and the plain one it is meant to widen came out 253px.
   Anything that has to beat a scaled value has to scale too. */
.read-min:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-height:calc(168px * var(--ui))}
.list-cap:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){max-height:calc(230px * var(--ui))}
.pop-wide:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:calc(250px * var(--ui));max-height:calc(320px * var(--ui))}

@media (prefers-reduced-motion:reduce){
.h-raise:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:none}
/* The drawer's entrance plays once and is the same one the forge's is. A
   reader who asked for no motion asked for none. */
.drawer:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none}
}

/* ==========================================================================
DYNAMIC HOOKS — the only thing a style= attribute may carry.
A value the server computes cannot live in a stylesheet, so it arrives as a
custom property and one of these reads it. See DESIGN.md for the form.
========================================================================== */
.dc:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--c)}
.dbg:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--dbg)}
.dbd:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--dbd)}
.dw:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:var(--dw)}
.dh:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){height:var(--dh)}
.dpad:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--dpad)}
.dgap:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--dgap)}
.dfs:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:var(--dfs)}
.dcols:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){grid-template-columns:var(--dcols)}
.danim:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:var(--danim)}
.dflex:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:var(--dflex)}
.dmin:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:var(--dmin)}
.dta:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){text-align:var(--dta)}
.dcur:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){cursor:var(--dcur)}
.dshadow:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:var(--dshadow)}
.dstyle:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-style:var(--dstyle)}

/* ==========================================================================
   THE MUSTER — a brother, marked, on every list there is.

   Owner: "id like the users everywhere in the roster to be more awesome
   looking", then "I didnt want the whoele row to be a glass tube just the
   brother...", then "yeah not a fan of the glass bubbles how can we make them
   look badass?" -- and then a picture of the thing he actually wanted, with
   "lets actually make it look more liks this."

   TWO WRONG ANSWERS BEFORE THIS ONE, and they were wrong the same way: both
   put the brother in a CONTAINER -- a glass capsule, then a stamped plate --
   and his mockup has none. A man on a muster roll is not an object in a box.
   He is an index, the numeral of his company, the stamp of his rank and his
   name, laid out on a ruled table, and the only thing drawn around anything is
   the small dark chip his rank sits in.

   WHAT MAKES IT READ is the ruling, not the ornament: full-height hairlines
   between the columns, the brother's own column on a ground a shade lighter
   than the rest, and a legend under the foot of the list. That is a muster
   roll, and it is what the picture is.
   ========================================================================== */

/* THE MARK ITSELF: no ground, no border, no shadow. A row of them IS the list. */
.ident:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:inline-flex;align-items:center;gap:var(--s4);
  min-width:0;max-width:100%}
/* The name is the only part that gives -- and on the two-line form it is the
   COLUMN holding the name that has to. .ellipsis and .col both set a floor of
   their own content, and a flex item's floor is its content, so without this a
   long name pushes the marks off the end instead of truncating. */
.ident:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>.ellipsis,.ident:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>.col{min-width:0}

/* HIS PLACE IN THE LIST. Tabular, so 09 and 10 sit under one another rather
   than shuffling the column as it pages. */
.ident-n:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;font-family:var(--mono);font-size:var(--fs-1);
  color:var(--ink-6);font-variant-numeric:tabular-nums;
  min-width:calc(15px * var(--ui))}

/* THE COMPANY'S NUMERAL. Roman, and never the word: X beside a name is a
   MARKING, where "10th company" is the column two along said a second time. */
.ident-num:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:.05em;color:var(--gold-dim);
  min-width:calc(17px * var(--ui))}

/* HIS RANK, STAMPED. The one thing on the roll with anything drawn round it,
   and the whole of what survived two rejected containers -- because a rank is
   an abbreviation and an abbreviation needs an edge to be read as one. */
.ident-rank:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;font-family:var(--mono);font-size:var(--fs-1);
  letter-spacing:var(--ls-1);color:var(--ink-3);white-space:nowrap;
  padding:2px 5px;border-radius:var(--r2);
  background:linear-gradient(180deg,rgb(var(--hue-93,30,42,36)),rgb(var(--hue-94,21,30,25)));
  box-shadow:inset 0 1px 0 rgba(var(--hue-63,226,236,224),.07),0 1px 0 rgba(0,0,0,.5)}

/* A BROTHER ON A REPORT keeps an edge, because a dozen of them wrap inside one
   cell and have to be told apart -- and the edge is the colour of what his
   sergeant did with him, which is the one piece of state a crew mark carries. */
.ident-tint:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:2px var(--s4) 2px var(--s4);border-radius:var(--r2);
  background:rgba(0,0,0,.22);box-shadow:inset 0 0 0 1px var(--dbd)}

/* THE SAME MARK WITH NOTHING TO SAY. THE RECORD's AS WRITTEN column is the
   brother's own words, so the men he named are drawn without the credit
   colour: a sergeant's decision inside the brother's quoted squad is the
   conflation this record's three columns exist to undo. */
.ident-quiet:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:2px var(--s4);border-radius:var(--r2);
  background:rgba(0,0,0,.22);box-shadow:inset 0 0 0 1px var(--hair)}

/* ---- THE RULED TABLE ----
   .rail-grid centres its cells and spaces them with a gap, which leaves a
   divider nothing to run down: a border on a centred cell is only as tall as
   its own text. So the muster stretches its cells, drops the gap, and pays for
   the spacing with padding -- and the hairline then runs the full height of
   the row, which is what a ruled table is. The header takes the same class, or
   the ruling and the labels part company at the first long name. */
.muster-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){align-items:stretch;gap:0}
/* THE HEADER KEEPS ITS OWN VERTICAL PADDING AND GIVES UP ITS HORIZONTAL. The
   cells below supply that now, and a header that kept both would sit its
   labels 16px to the right of the columns they name. */
.grid-hdr.muster-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-left:0;padding-right:0}
.muster-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>*{display:flex;align-items:center;min-width:0;padding:0 var(--s6)}
.muster-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>*:first-child{padding-left:var(--s7)}
.muster-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>*+*{border-left:1px solid var(--hair)}
.muster-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>*:last-child{justify-content:flex-end;padding-right:var(--s7)}
/* The men are lit a shade above the facts beside them. It is the difference
   between a list of brothers and a spreadsheet about them. */
.muster-grid:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>*:first-child{background:rgba(var(--hue-1,178,201,177),.025)}

/* ON VOX IS THE ONLY THING WORTH A BADGE. The column used to print OFF VOX six
   hundred times, which is six hundred rows saying nothing -- the fact anybody
   opens this screen for is who is in a channel NOW. Off and unknown are blank,
   and the cell's title still says which of the two it is. */
.vox-badge:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-1);font-weight:500;
  letter-spacing:var(--ls-2);white-space:nowrap;
  padding:2px 6px;border-radius:var(--r2);
  color:var(--good);border:1px solid rgba(var(--hue-2,127,180,137),.45);
  background:rgba(var(--hue-2,127,180,137),.08)}

/* THE FOOT OF THE ROLL: what the dashes meant, and how many men were counted. */
/* ROOM FOR A MAN WITH TWO NAMES. Owner, on the roll: "we need some padding
   here." Every other grid on this site puts ONE line in a row, and the 10px of
   --dpad that is right for those wraps a thirty-pixel block here: the muster's
   first cell carries a man's name and his handle stacked, so the roll reads as
   one paragraph of men rather than as a list of them.

   A FLOOR RATHER THAN MORE PADDING, for the reason the record gave when the
   strategium had the same complaint: a row told how short it may be keeps its
   rhythm whether a man has a handle, a vox flag, both or neither. Reports and
   the honours are untouched -- their rows are one line and already sit right. */
/* A BADGE IS THE ROLE'S OWN PICTURE, at the size a name can carry. Discord
   draws these at 32 and up. It went out at 16px, half-lit, on the reasoning
   that a badge must not compete with a man's face -- and the owner read it the
   way it looked: "make the icons a little bigger they are way too small". An
   honour that has to be squinted at is not being shown. 22px is a little over
   a line of type, which is what a thing worn ON a name should be, and the
   dimming is gone: "that is a distinguised icon its important to always show."
   The bytes are asked for at 64 so the picture is still sharp on a retina
   screen at this size. Never allowed to shrink -- a row running out of width must
   take it from the name, which ellipsises, and not from the badge, which would
   simply become a smudge. */
/* AND ROOM AT THE EDGES OF THE ROLL. Owner: "please give the brother column
   some padding now since you removed the old badges." The stencils that used
   to sit in front of a man's face were also, accidentally, his left margin --
   with them gone his portrait starts hard against the plate's own edge, and
   the "brother" heading above him is indented 16px by .muster-grid's first
   cell while he is not.

   PADDING INSIDE THE CELL, never on the row. --dpad is the row's box and
   widening it there would shrink every track by 32px while the header's stay
   full width, walking all six columns to the left of the labels that name
   them. Padding inside the first and last cells moves the ink and leaves the
   tracks exactly where the header put them. The figures at the far end get
   the same treatment for the same reason -- .muster-grid ends on --s7 too, and
   a points column flush to the edge reads as an overflow. */
.muster-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>*:first-child{padding-left:var(--s7)}
.muster-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>*:last-child{padding-right:var(--s7)}

/* THE TICK ON A PICKED REPORT. A box rather than a native checkbox: the rows
   are a grid of hairlines and mono figures, and a platform control would be the
   one thing on the screen drawn by somebody else's designer. Fixed size so a
   ticked row and an unticked one are the same height -- a list that shifts as
   you tick down it is a list you lose your place in. */
.pick-box:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:calc(15px * var(--ui));height:calc(15px * var(--ui));
  border:1px solid var(--line-3);border-radius:var(--r1);
  font-family:var(--mono);font-size:var(--fs-2);line-height:1;color:transparent}
.pick-on:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--gold);color:var(--gold);background:rgba(196,157,74,.12)}

/* HONOURS ARE A DESK VIEW. Owner: "lets remove them from the momile view only
   keep them on the pc view." Uncapped, they are the widest thing on a roster
   row, and a phone row has a name and a handle to fit before it has room for
   ornament. Hidden rather than capped, so the desk view stays complete. */
@media (max-width:999px){ .role-badge:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:none} }
.role-badge:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;width:calc(22px * var(--ui));height:calc(22px * var(--ui));
  object-fit:contain;vertical-align:middle}
.role-badge-lg:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:calc(30px * var(--ui));height:calc(30px * var(--ui))}

.muster-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-height:calc(52px * var(--ui))}
.muster-card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-top:var(--s7);padding-bottom:var(--s7)}

.muster-foot:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;gap:var(--s5);
  padding:var(--s5) var(--s7);border-top:1px solid var(--hair)}

/* THE ROW A HANDLE NOBODY HOLDS SITS ON, on the honours. */
.row-orphan:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){box-shadow:inset 2px 0 0 var(--ink-6)}

/* ==========================================================================
   THE AAR JUDGING PAGE — the places the owner's mockup and the shared
   components disagree, and the mockup wins for this page alone.

   EVERY RULE HERE IS SCOPED. .mark is the app's one state pill, .btn-gold its
   one primary action, .field its one form control and .pad-card its one body
   inset — the mockup contradicts all four, but only on this screen, and this
   sheet has been burned before by a page's opinion escaping into a component.
   Scoped here means: it lands on the judging page and nowhere else, without
   touching the component or reordering anything.

   Placed after the d* hooks on purpose. .dbg, .dbd and .dcur are `background`,
   `border-color` and `cursor` at (0,1,0) declared last, so a component's own
   declaration always loses to them; a rule that has to survive one of those
   has to sit here AND carry two classes.
   ========================================================================== */

/* ---- the page title block ------------------------------------------------
   Flat on the page ground: the mockup gives it padding and a 1px bottom rule
   and nothing else — no ground, no blur, no shadow and no position. It is the
   DECIDING column that is pinned now (see .aar-side), which is what lets the
   filing scroll under a fixed fireteam rather than under a bar repeating a
   word the fireteam already says. Sides come from .gutter, so the block starts
   on the same left edge as the panels beneath it. */
/* THE DECIDING COLUMN IS A TRACK, NOT A SHARE, and the track is declared on
   .aar-cols now rather than here. This used to be flex:0 1 560px, overriding
   the `dflex` binding (--dflex:1 1 {{ dSideW }}, flex-GROW enabled, which made
   the column a function of the viewport: measured 565px at 1600 and 491px at
   1280 against a drawn 560 at both). Both are inert under a grid, which is
   what the mockup actually declares, so the width is the 560px track and the
   binding stays in the markup untouched and unused. It is left there because
   removing it is a markup change for no gain, and the script still sets it. */

/* IT RUNS THE FULL WIDTH BECAUSE THE COLUMNS DO, and that is the whole rule:
   the way back has to sit on THE RECORD's left edge and WORTH on the
   fireteam's right, so the header can only ever be as wide as the page under
   it. Both were capped at 1636px and centred for an hour; the owner asked for
   full width, .aar-cols lost its cap and THIS ONE WAS LEFT BEHIND -- which is
   how the header came to sit centred and narrow over columns running edge to
   edge. Same two declarations on both, or neither. */
.aar-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:calc(22px * var(--ui)) 0 calc(18px * var(--ui))}
.aar-head-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s9)}
.aar-ref:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border:0;background:none;padding:0;text-align:left}
.aar-ref:focus-visible:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){outline:1px solid var(--gold);outline-offset:3px}
/* Owner, on the header this block replaces: "we see awaiting judgment back and
   worth its too tall and takes up too much screen." The WORTH figure is the
   tall thing, so it is what comes down -- 26px against the desktop's 40 keeps
   the whole block to one row's height on a phone without losing the figure. */
@media (max-width:999px){
.aar-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-top:var(--s7);padding-bottom:var(--s6)}
.aar-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .worth-n{font-size:var(--fs-9)}
}
/* BACK IS A BUTTON. Owner: "make it an actuall button and give it a little
   space". .btn is already the mockup's declaration character for character —
   mono 500, --fs-2 (its 10px), --ls-ctl (its .12em), uppercase, --r1, 9px
   vertical, --ink-2 — so this adds only the three it differs on: the flex the
   arrow needs a gap in, its 13px sides, and its slightly lifted hairline. */
.btn-back:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:inline-flex;align-items:center;justify-content:center;
gap:var(--s4);padding:9px calc(13px * var(--ui));border-color:var(--hair-2)}
/* WHAT THE REPORT IS WORTH, AS THE PAGE'S HEADLINE FIGURE. It was a 22px mono
   chip in a bordered box down in the fireteam's action row; the mockup sets it
   at 40px Cormorant, unboxed, at the top right of the title block. There is no
   type token at 40 (--fs-9 is 26, --fs-10 is 44), so it is a literal scaled by
   --ui. The colour stays on .dc: {{ dScoreCo }} is a live binding. */
.aar-worth:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:0 var(--s3)}
.worth-n:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-size:calc(40px * var(--ui));line-height:1;margin-top:var(--s2)}
/* THE STATE, AS A PILL RATHER THAN A LOOSE WORD. The mockup has no state word
   in its title block at all — it has a headline, and the app has no headline
   binding to draw. So the word stays and takes the shape the mockup DOES use
   for a state, its verdict chip in THE RECORD's head; that also gives
   {{ dBannerBd }} an edge to colour, which is what it was computed for. */
.aar-banner:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-radius:var(--r1);padding:5px var(--s5)}

/* ---- the panel's body ----------------------------------------------------
   THE MOCKUP PADS THE PLATE, NOT THE BLOCKS INSIDE IT: 18px 20px 20px on the
   panel, with the head adding 9px down to its rule and 14px of margin under
   it. The app splits that between .plate-head and .pad-card, which is fine
   until a panel stacks two bodies — then the plate's single 20px foot gets
   paid twice and the 14px under the rule gets paid to a block that is not
   under the rule. These two say it once each: the first body after the head
   takes the 14, the last body in the panel takes the 20, and a body in the
   middle keeps --s6. */
.plate:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > .plate-head + .pad-card{padding-top:calc(14px * var(--ui))}
.plate:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) > .pad-card:last-child{padding-bottom:var(--s8)}

/* THE ONE PANEL THE MOCKUP DOES NOT LIGHT. Every other plate it draws has the
   lifted top edge .plate now carries; THE FIRETEAM is a uniform hairline,
   because it is the one panel whose contents are themselves cards. */
.aar-team:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-top-color:var(--line-3)}
/* THE HEAD BLOCK: title, bar and gate line, closed by ONE hairline.
   The mockup pads this whole block 17/20/14 and rules it off; .plate-head
   already pays the 18 above and the 9 down to its brass rule, so the block
   adds only the 14 underneath and the cool hairline that ends it. The bar and
   the sentence take the side padding the head has, so all three start on the
   same 20px line. 11 and 14 are off the --s ladder and are literals scaled by
   --ui, the way .plate-head's own 18 and 9 already are. */
.aar-team-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-bottom:calc(14px * var(--ui));
border-bottom:1px solid var(--hair)}
.aar-team-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .meter{margin:calc(11px * var(--ui)) var(--s8) 0}
.aar-team-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .gate-line{margin:var(--s5) var(--s8) 0}

/* Keep the compact record legible and its history available on demand.
   The .plate-head-sub/-meta pair that sat here is now the component's own
   default -- every head on the site reads the way this page's did. */
.aar-record:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .decide-k,.aar-record:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .meta-ash{font-size:max(10px,var(--fs-2));color:var(--ink-3)}
.aar-record:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .record-hdr-mid{color:var(--gold)}
.aar-chronicle:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) summary{cursor:pointer;line-height:1.6}
.aar-chronicle:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) summary:hover{color:var(--ink)}
.chronicle-hint:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink-3);font-size:max(10px,var(--fs-3));margin-left:var(--s4)}
.chronicle-body:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding-top:var(--s6)}
.chronicle-value:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){white-space:pre-line;overflow-wrap:anywhere}
.record-dispatch:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){margin:0;white-space:pre-wrap;overflow-wrap:anywhere}
.crew-name:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.crew-meta:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.crew-owner:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){overflow-wrap:anywhere;line-height:1.5}
.crew-card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .crew-meta,.crew-card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .crew-owner{color:var(--ink-3);font-size:max(11px,var(--fs-3))}
.crew-card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .crew-state{align-self:flex-start}
.crew-card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .crew-state .t1{font-size:max(10px,var(--fs-1))}
@media(max-width:600px){
  .aar-head-row:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){gap:var(--s4);align-items:center}
  .aar-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .aar-banner{flex:1;min-width:0;padding:var(--s3);gap:var(--s3)}
  .aar-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .aar-banner>span{white-space:normal;font-size:11px;letter-spacing:.05em}
  .aar-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .aar-worth{padding:0}
  .aar-team:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .plate-head,.aar-evidence:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .plate-head{flex-wrap:wrap;gap:var(--s3) var(--s5)}
  .aar-team:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .plate-head > .plate-head-meta{flex-basis:100%;margin-left:0}
  .crew-identity:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:auto minmax(0,1fr);align-items:start}
  .crew-identity:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .crew-state{grid-column:2;justify-self:start;padding:var(--s2) var(--s4)}
}

/* ---- the evidence tile ---------------------------------------------------
   It steps off the --bevel/--cast ladder deliberately, and only here. The
   mockup draws a hatched empty tile with a heavy INNER BOTTOM VIGNETTE —
   inset 0 -18px 30px -18px — which darkens the lower third and is what stops
   five identical hatched rectangles reading as wallpaper. The ladder has no
   step for that because no other surface in the app is a picture. Two classes,
   because .raise-1 and .h-raise:hover both land after .shot at equal weight. */
.gallery:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .shot{cursor:zoom-in;
box-shadow:inset 0 1px 0 rgba(var(--hue-23,228,240,226),.08),
           inset 0 -18px 30px -18px rgba(0,0,0,.8),
           0 12px 24px -12px rgba(0,0,0,.9),
           0 2px 6px -2px rgba(0,0,0,.55)}
/* zoom-in is a promise, and a tile whose bytes were never fetched cannot keep
   it. .shot-slot is what a tile with no picture draws, so it is the test. */
.gallery:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .shot:has(.shot-slot){cursor:default}
.gallery:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .shot:hover{box-shadow:inset 0 1px 0 rgba(var(--hue-23,228,240,226),.1),
           0 18px 30px -12px rgba(0,0,0,.95),
           0 3px 8px -2px rgba(0,0,0,.6)}
/* TWO CHIPS IN ONE CORNER HAVE TO BE ONE HEIGHT. .mark grew to 6px/10px for
   the auspex verdict, which put a 28px pill beside the 19px number badge on
   every tile. Inside a tile it keeps the badge's own 2px/6px. */
.shot-tags:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .shot-mark{gap:var(--s2);padding:var(--s1) var(--s3)}

/* ---- the auspex verdict --------------------------------------------------
   5px/10px is the mockup's pill, and it draws NO halo: the lamp on .mark-lamp
   marks a machine at work, and a verdict that has already been reached is not
   one. The dot stays 6px, which is the mockup's exactly. */
.aar-auspex:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .shot-mark{padding:5px var(--s5);box-shadow:none}
.aar-auspex:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .shot-mark[data-verdict]::before{box-shadow:none}

/* ---- the ledger notes well -----------------------------------------------
   A fixed well the log sits on the FLOOR of and grows upward out of, so a
   report with two notes and a report with twenty are the same height and the
   newest line is always in the same place. --sink-2 is inset 0 2px 8px
   rgba(0,0,0,.6) against the mockup's .65, near enough not to mint a seventh
   shadow for. */
.notes-well:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){height:calc(188px * var(--ui));
border:1px solid rgba(var(--hue-1,178,201,177),.12);border-top-color:rgba(0,0,0,.5);
border-radius:var(--r1);background:rgba(var(--hue-113,6,11,9),.62);box-shadow:var(--sink-2);
padding:calc(13px * var(--ui)) calc(14px * var(--ui)) calc(14px * var(--ui))}
.log-line:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){line-height:1.65}
/* The composer's edge is a hairline here, not the app's gold field edge: the
   mockup keeps gold for the button beside it, and two golds in one row is the
   emphasis spent twice. */
.aar-notes:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .field{border-color:var(--hair);background:rgba(var(--hue-114,8,14,11),.6)}
.aar-notes:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .btn-outline{background:rgba(201,164,92,.12);color:var(--gold-warm);
letter-spacing:var(--ls-ctl)}

/* The held / barred sentences under the action row. Mono at 1.7, which is the
   mockup's gate line; the sheet's .lead is 1.45 and has no looser step. */
.gate-line:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){line-height:1.7;text-wrap:pretty}

/* ---- the brother card ----------------------------------------------------
   TWO UTILITIES WERE EATING THE COMPONENT. .dbg is the `background` SHORTHAND,
   so on `plate plate-bare crew-card dbg dbd` it reset background-image to none
   and background-color to {{ m.dbg }} — the literal string "transparent" for
   every man not yet credited. The cards had no fill at all. .dbd did the same
   to the border, painting the saturated status colour on all four sides where
   the mockup wants a hairline and a 3px inset bar down the left. Both are
   repaired at (0,2,0), which wins without reordering and without touching the
   markup, so {{ m.dbg }} and {{ m.bd }} keep their jobs — the tint becomes the
   underlayer the mockup actually draws it as, and the status colour becomes
   the accent rail it actually draws it as. */
.crew-card.dbg:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:linear-gradient(180deg,rgba(var(--hue-29,32,49,39),.85),rgba(var(--hue-80,15,25,20),.55)),var(--dbg)}
.crew-card.dbd:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:var(--line-3);border-top-color:rgba(var(--hue-8,210,225,210),.16);
box-shadow:inset 3px 0 0 var(--dbd),var(--lip),var(--cast-1)}
/* 34px round with 10px initials. The face class is a whole-attribute binding
   ({{ m.faceCls }}, and faceCls() appends .lazy-avatar to it), so this is a
   descendant selector rather than a class swap. */
.crew-card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .avatar{width:calc(34px * var(--ui));height:calc(34px * var(--ui));
font-size:var(--fs-2)}
.crew-list:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s6) var(--s7)}
.crew-state:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:var(--s3) var(--s5);border-radius:var(--r1);
background:rgba(var(--hue-1,178,201,177),.05)}
/* THE TWO BUTTONS SPAN THE CARD. They were the tail of a wrapping chip row
   with a spacer shoving them right, so the two controls the whole panel exists
   for were the smallest things on it. */
/* ============================================================================
   THE DESKS WORKBENCH
   ----------------------------------------------------------------------------
   Three panels in one plate: the companies, the company's layout, and every
   job on the site. Each pane has the same depth every other block here has, so
   they read as three objects rather than one pane with two rules down it.
   ========================================================================== */
.desk-work:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:grid;grid-template-columns:minmax(0,220px) minmax(0,1fr) minmax(0,340px);
  gap:var(--s5);padding:var(--s5);align-items:start}
@media (max-width:1199px){.desk-work:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){grid-template-columns:minmax(0,200px) minmax(0,1fr)}
  .desk-cards:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){grid-column:1 / -1}}
@media (max-width:799px){.desk-work:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){grid-template-columns:minmax(0,1fr)}
  .desk-cos:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.desk-tree:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.desk-cards:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){grid-column:1}}
.desk-pane:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;min-width:0;border-radius:var(--r2);
  background:var(--panel-3);background-image:var(--face-1);
  border:1px solid var(--line-3);border-top-color:rgba(var(--hue-8,210,225,210),.14);
  box-shadow:var(--bevel),var(--cast-2)}
.desk-pane-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;gap:var(--s4);
  padding:var(--s5) var(--s5) var(--s3);border-bottom:1px solid var(--rule-head);
  font-family:var(--mono);font-size:var(--fs-1);letter-spacing:var(--ls-meta);
  text-transform:uppercase;color:var(--ink-5)}
.desk-cos:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .rail{padding:var(--s4) var(--s5)}
.desk-tree:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:0 var(--s4) var(--s4)}
.desk-cards:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;flex-direction:column}

/* A CARD PER JOB, SHUT UNTIL YOU OPEN IT. Owner: "each one of those things
   would be a card that can be expanded / opened." */
.desk-card:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-bottom:1px solid var(--hair)}
.desk-card:last-child:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-bottom:none}
.desk-card-head:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;align-items:center;gap:var(--s4);cursor:pointer;
  padding:var(--s4) var(--s5);transition:background var(--t)}
.desk-card-head:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(var(--hue-1,178,201,177),.05)}
.desk-fold:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex:none;width:12px;text-align:center;font-family:var(--mono);
  font-size:var(--fs-4);color:var(--ink-5)}
.desk-card-open:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(201,164,92,.05)}
.desk-card-open:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .desk-fold{color:var(--gold)}
.desk-card-body:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex;flex-direction:column;gap:var(--s4);
  padding:0 var(--s5) var(--s5) calc(var(--s5) + 12px)}

/* HIS POSITION, BESIDE HIS COMPANY. Gold and boxed when it is the reader's own
   squad, so "who is in my squad" is one glance rather than three lines of
   "awaiting". Owner, 2026-09-11. */
.crew-place:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){font-family:var(--mono);font-size:var(--fs-2);letter-spacing:var(--ls-1);
  color:var(--ink-4);white-space:nowrap}
.crew-place-mine:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-warm);border:1px solid var(--gold-line);border-radius:2px;
  padding:0 var(--s3);background:rgba(201,164,92,.10)}
.crew-group-mine:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .decide-k{color:var(--gold-warm)}
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){display:flex}
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .btn{flex:1;font-size:var(--fs-2);letter-spacing:var(--ls-ctl);
padding:var(--s5) 0}
/* A CREDITED BROTHER IS GREEN, NOT BRASS. .btn-gold is the app's "this is the
   answer" everywhere else and it stays that everywhere else; on a card the
   answer has a colour of its own already, and the notch it clips out of its
   own corner appears on no control in the mockup. */
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .btn-gold{border:1px solid var(--good);background:rgba(var(--hue-2,127,180,137),.28);
color:var(--ink);box-shadow:none;clip-path:none}
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .bg-bad{border-color:var(--warn);background:rgba(181,112,90,.3)}

/*
 * THE THREE BUTTONS, AND WHICH ONE IS THE ANSWER.
 *
 * Owner, 2026-09-04: "the buttons could use a little more contrast. auspex is
 * clean so highlight accpet. or if its refused highlight refuse just make this
 * really pop please. then this will be the gold standard for the whole site."
 *
 * The base .btn is deliberately quiet -- a --line-3 hairline on nothing -- which
 * is right for "never mind" and wrong for the row a sergeant is here to press.
 * Three identical grey outlines said the three acts weigh the same, and they do
 * not: on a clean report ACCEPT is what happens next, and the other two are
 * exceptions to it.
 *
 * SO THE ROW HAS A LEAD AND A CHORUS. The lead is filled, lit along its top
 * edge, and carries a ring; the chorus keeps a readable edge and its own hue so
 * REFUSE still reads as refusal and ESCALATE as neither. Which button leads is
 * decided in one place, on the payload -- see crew-lead in the bindings -- so
 * the highlight cannot disagree with the state of the card it is on.
 */
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .btn{border-color:var(--line-2);color:var(--ink-2);
  background-image:linear-gradient(180deg,rgba(var(--hue-64,225,240,226),.09),transparent 50%,rgba(0,0,0,.15));
  box-shadow:inset 0 1px 0 rgba(var(--hue-64,225,240,226),.18),inset 0 -1px 0 rgba(0,0,0,.4),0 3px 0 rgb(var(--hue-84,7,16,11)),0 5px 7px rgba(0,0,0,.3);
  transform:translateY(-1px);transition:background var(--t),border-color var(--t),color var(--t),box-shadow .12s,transform .12s}
.crew-lead:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;font-weight:600}
/* The lit top edge and the ring are what carry it at a glance; the fill alone
   reads as a hover on a dark ground. */
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .crew-lead{box-shadow:inset 0 1px 0 rgba(255,255,255,.3),inset 0 -1px 0 rgba(0,0,0,.35),
  0 3px 0 rgb(var(--hue-84,7,16,11)),0 5px 7px rgba(0,0,0,.3),0 0 12px -5px var(--lead-ring);
  background-color:var(--lead-bg);border-color:var(--lead-bd);color:var(--lead-ink)}
/* Raised keys become recessed only after a recorded decision. */
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .btn.crew-pressed,.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .btn:active{
  transform:translateY(2px);background-image:linear-gradient(180deg,rgba(0,0,0,.35),rgba(0,0,0,.06) 60%,rgba(var(--hue-49,215,240,220),.04));
  box-shadow:inset 0 3px 6px rgba(0,0,0,.7),inset 1px 0 3px rgba(0,0,0,.3),0 1px 0 rgba(var(--hue-47,205,230,210),.18);
  border-top-color:rgba(0,0,0,.6)}
/* ACCEPT LEADING: the report is clean and he is owed his points. */
.crew-lead-good:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){--lead-bg:rgba(var(--hue-2,127,180,137),.32);--lead-bd:var(--good);
  --lead-ring:rgba(var(--hue-2,127,180,137),.45);--lead-ink:rgb(var(--hue-24,234,245,234))}
/* REFUSE LEADING: somebody has already said no, and the card should say so
   louder than the button that would undo it. */
.crew-lead-bad:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){--lead-bg:rgba(155,44,36,.42);--lead-bd:var(--bad-lit);
  --lead-ring:rgba(196,104,95,.45);--lead-ink:#F6E6E4}
/* ESCALATE LEADING: it is with the CO, and neither of the other two applies. */
.crew-lead-esc:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){--lead-bg:rgba(122,146,196,.30);--lead-bd:var(--esc-lit);
  --lead-ring:rgba(122,146,196,.45);--lead-ink:#E4EBF8}
/*
 * AND THE RECOMMENDED ONE BREATHES. Owner, 2026-09-04: "the buttons that
 * recomend the sgt judgment needs to shimmer green on the accepts or red on the
 * refuse."
 *
 * ONLY WHEN IT IS A RECOMMENDATION, which is the distinction the animation has
 * to carry: a card that is already settled lights its button to say SO, and a
 * settled state that pulsed would read as asking to be pressed again. So the
 * shimmer rides .crew-lead-live, set only where nothing has been decided yet
 * and the auspex has an opinion -- see the bindings.
 *
 * Brightness carries the pulse so the raised key keeps its bevel and shadow.
 * Settled controls keep their colour and sit recessed without pulsing.
 */
@keyframes crew-breathe{
  0%,100%{filter:brightness(1)}
  50%{filter:brightness(1.17)}
}
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .crew-lead-live{animation:crew-breathe 2.4s ease-in-out infinite}
.aar-accept-fx:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:relative;isolation:isolate}
.aar-accept-locked:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){cursor:default;pointer-events:none}
/* The original green plate stays. A contained power sweep charges its edges. */
.aar-accept-ready:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::before,.aar-accept-ready:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{content:"";position:absolute;pointer-events:none}
.aar-accept-ready:not(.crew-lead):where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(var(--hue-2,127,180,137),.32);border-color:var(--good);color:rgb(var(--hue-24,234,245,234))}
.aar-accept-ready:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::before{inset:-1px;border:1px solid transparent;
  background:linear-gradient(110deg,transparent 28%,rgba(var(--hue-18,124,239,169),.65) 44%,rgb(var(--hue-51,237,255,241)) 49%,rgba(var(--hue-18,124,239,169),.65) 54%,transparent 70%) border-box;
  background-size:320% 100%;padding:1px;
  mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:exclude;
  animation:seal-charge 3.6s cubic-bezier(.4,0,.2,1) infinite}
.aar-accept-ready:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{inset:1px;overflow:hidden;
  background:linear-gradient(110deg,transparent 34%,rgba(var(--hue-16,111,229,160),.07) 43%,rgba(var(--hue-46,201,255,220),.25) 49%,rgba(var(--hue-16,111,229,160),.07) 55%,transparent 64%);
  background-size:320% 100%;animation:seal-charge 3.6s cubic-bezier(.4,0,.2,1) infinite}
@keyframes seal-charge{0%,18%{background-position:110% 0;opacity:0}24%{opacity:1}
  70%{background-position:-10% 0;opacity:1}82%,100%{background-position:-10% 0;opacity:0}}
.approval-veil:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){position:fixed;inset:0;z-index:65;display:grid;place-items:center;padding:24px;background:rgba(var(--hue-81,4,12,8),.84)}
.approval-dialog:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){width:min(100%,480px);padding:24px;border:1px solid var(--gold);background:rgb(var(--hue-53,19,35,27));box-shadow:0 20px 80px #000}
@media (prefers-reduced-motion:reduce){
  .aar-accept-ready:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::before,.aar-accept-ready:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::after{display:none}
}
/* A guttering candle is still a lit candle: the movement goes, the light stays
   -- the same rule the sheet applies to every other animation it carries. */
@media (prefers-reduced-motion:reduce){
  .crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .crew-lead-live{animation:none}
}

/* THE TWO THAT ARE NOT LEADING still have to be legible: the quiet base is a
   hairline on nothing, which beside a lit button reads as disabled. */
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .btn:not(.crew-lead){background-color:rgba(var(--hue-1,178,201,177),.05)}
.crew-acts:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .btn:not(.crew-lead):hover{background-color:rgba(var(--hue-1,178,201,177),.12);
  border-color:var(--tile-edge)}

/* An unclaimed handle's mark. It was an EMPTY .mark — a 24x15 orange-bordered
   rectangle with a halo and nothing in it, because .mark sizes itself off a
   label and this one has none. The mockup's state markers are all small round
   dots, so it becomes one. */
.pip-warn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--warn-2);border-color:var(--warn-2)}

/* ==========================================================================
HOVER — was 16 distinct style-hover attributes on 42 elements.
========================================================================== */
.h-ink:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--ink)}
.h-warm:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--gold-warm)}
.h-bad:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--bad-lit)}
.h-bad-line:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){color:var(--bad-lit);border-color:var(--bad)}
.h-gold-line:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-color:rgba(201,164,92,.5);color:var(--gold-warm)}
/* FIVE HOVERS THAT DIFFERED ONLY IN THE ALPHA OF ONE GREEN -- .7, .6, .5, .8,
   .6 -- across eighteen elements. Nobody chose that; it accreted, one element
   at a time, each new one guessing at a value near the last.
   
   There is one lamp in this room now and it is WARM, because the reader's own
   attention is the light and light has a source. Green-on-green said "the
   surface changed"; brass says "you are here". Two shapes, not five values:
   a lift, and a lift with a rail down the edge for a row in a list.
   
   The names are kept as they are so no markup moves; they are aliases of one
   behaviour rather than five behaviours with one look. --lift and --lift-hard
   are the only two values, and .rail-act:hover uses the same --lift, so a row
   in a list and a tile in a grid answer the hand identically. */
.h-lift:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.h-lift-soft:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.h-lift-faint:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--lift)}
.h-lift-strong:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--lift-hard)}


.h-bad-fill:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--bad);color:var(--ink)}
.h-gold-fill:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--gold);color:var(--ink-on-gold)}
.h-good-fill:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:rgba(var(--hue-5,121,196,141),.16)}
.h-ink-fill:hover:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){background:var(--ink)}

@media (prefers-reduced-motion:reduce){
.act:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.btn:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.chip:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){transition:none}
.anim-enter:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none}
}

/* ==========================================================================
THE GATE CLOSING

Each piece of the boot panel replays ITS OWN ENTRANCE BACKWARDS -- the same
four keyframes with direction:reverse, not four new ones written to look
like the opposite. Two sets would drift the first time one was tweaked; one
set cannot.

The !important is doing real work rather than papering over specificity:
every one of these elements carries its entrance in an inline style
attribute (they are one-off, screen-specific motion), and an inline
animation is otherwise unbeatable by a stylesheet.

Delays run outward from the middle: the mark goes first, the rule and title
follow, the lines last, and the panel's own fade starts at 280ms over the
top of all of it.
========================================================================== */
.boot-leaving:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .boot-mark{animation:bootmark .34s cubic-bezier(.5,0,.75,0) reverse both!important}
.boot-leaving:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .boot-line{animation:bootline .3s .06s cubic-bezier(.5,0,.75,0) reverse both!important}
.boot-leaving:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .boot-title{animation:track .34s .04s cubic-bezier(.5,0,.75,0) reverse both!important}
.boot-leaving:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .boot-row{animation:tickin .26s .1s ease-in reverse both!important}

@media (prefers-reduced-motion:reduce){
.boot-leaving:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .boot-mark,.boot-leaving:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .boot-line,
.boot-leaving:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .boot-title,.boot-leaving:where(:not(.ledger,.ledger *),.oldskool,.oldskool *) .boot-row{animation:none!important}
}

/*
 * AND EVERY ANIMATION WRITTEN IN THE MARKUP, which is most of them.
 *
 * An inline `style="animation:…"` beats a stylesheet rule unless the rule
 * carries !important -- the block above already knows this, and says so, and
 * then the lesson is applied nowhere else in the file. So a reader who asked
 * for no motion still got the boot mark scaling in, the reticle pulsing, the
 * rule drawing itself, the title's tracking collapsing, the rows ticking in,
 * the loading hairline, the sign-in scanline, the button's glow, the nav badge,
 * the spinner and the reader panel's shimmer. FORTY-FOUR inline animations, and
 * the one rule that existed killed only the boot gate's EXIT -- so they saw the
 * whole entrance and then a hard cut. The asymmetry, backwards.
 *
 * `--danim` is not the string "animation:", so the two hooks have to be named
 * separately. Nothing in the app waits on animationend, so cancelling them
 * cannot strand a state: leaveBoot() runs on a plain 720ms timer and says so.
 */
@media (prefers-reduced-motion:reduce){
[style*="animation:"]:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none!important}
.danim:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none!important}
/* The title's tracking is the END STATE of `track`, not a declaration --
   nothing in the sheet gives .boot-title a letter-spacing -- so cancelling the
   animation would leave CALIBAN LEDGER at default. Declared here it also fixes
   the snap-back the .boot-leaving rule above already causes. */
.boot-title:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){letter-spacing:.22em}
/* Stops the movement and keeps the light, which is the candles' own rule. */
.scanning:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.scan-glyph-reading:where(:not(.ledger,.ledger *),.oldskool,.oldskool *),.tally-scan:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none}
}

/* ==========================================================================
THE PHONE

Owner: "the mobile view is a little fucked... its missing the bottom nav it
used to have. and the headers and such are ugly."

Measured at 390px before touching anything, because two of the three were
worth checking rather than assuming:

- THE SIZE IS NOT THE PROBLEM. --ui is 1 below 1000px, so the desktop 10%
never reached a phone. Left alone.
- THE BOTTOM NAV IS THERE and correctly pinned: six items, 62px tall,
sitting at 782-844 in an 844 viewport. Nothing to restore.
- THE HEADERS REALLY ARE UGLY, and the measurement says why. On the
reports screen the tool bar is 135px TALL and STILL 555px wide inside a
381px column: a search box, a five-chip group 370px wide and another
539px wide. flex-wrap cannot fix that -- one group is wider than the
phone on its own -- so wrapping only built a three-storey tower that
overflowed anyway.

A tool bar on a phone is therefore ONE SCROLLING LINE, which is what every
other application does with a filter strip and what the wrap was failing to
be. The spacer goes: it exists to push the count to the far right of a wide
bar, and inside a scrolling line it is 255px of nothing.
========================================================================== */
@media (max-width:999px){
.bar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
gap:var(--s3);padding:var(--s3) var(--s5)}
.bar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)::-webkit-scrollbar{height:0;width:0}
.bar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>*{flex:none}
.bar:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>.spacer{display:none}

/* THE ONE BAR THAT MUST NOT SCROLL. A .pop is positioned against its control
   and an overflow:auto parent clips it, so a filter menu opened inside a
   scrolling line would be cut off at the bar's own edge. It no longer needs
   the scroll anyway -- that rule exists because seventeen chips could not fit
   on a phone, and four controls wrap onto two comfortable rows. */
.bar-filt:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){flex-wrap:wrap;overflow:visible}
.bar-filt:where(:not(.ledger,.ledger *),.oldskool,.oldskool *)>.spacer{display:none}
.search:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:150px}
.chip:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){padding:5px var(--s4);letter-spacing:var(--ls-1)}

/* A column that fills the screen has nothing to its right to be divided
from, and its 1px edge was the whole of a 5px overflow on statistics. */
.hair-r:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){border-right:none}

/* The band of big numbers is two-up on a phone rather than four-across at
130px each, which is narrower than the numbers it carries. */
.total:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){min-width:44%}
}

@keyframes led{0%,100%{opacity:.28}50%{opacity:1}}@keyframes shimmer{0%{transform:translateX(-140%)}100%{transform:translateX(340%)}}@keyframes scanv{0%{transform:translateY(-12%)}100%{transform:translateY(1100%)}}@keyframes rise{0%{opacity:0;transform:translateY(6px)}100%{opacity:1;transform:translateY(0)}}@keyframes glow{
  0%,100%{box-shadow:inset 0 1px 0 rgba(255,236,180,.55),inset 0 0 10px -3px rgba(255,236,180,.28)}
  50%    {box-shadow:inset 0 1px 0 rgba(255,236,180,.55),inset 0 0 18px 0 rgba(255,236,180,.66)}}@keyframes fadein{0%{opacity:0}100%{opacity:1}}@keyframes spin{100%{transform:rotate(360deg)}}@keyframes enter{0%{opacity:0;transform:translateY(9px)}100%{opacity:1;transform:translateY(0)}}@keyframes bootmark{0%{opacity:0;transform:scale(.55) rotate(-10deg)}70%{opacity:1;transform:scale(1.04) rotate(0)}100%{opacity:1;transform:scale(1)}}@keyframes bootline{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}@keyframes bootout{0%{opacity:1;transform:scale(1);filter:blur(0)}100%{opacity:0;transform:scale(1.045);filter:blur(3px);visibility:hidden}}@keyframes track{0%{letter-spacing:.6em;opacity:0}100%{letter-spacing:var(--ls-brand);opacity:1}}@keyframes tickin{0%{opacity:0;transform:translateY(-7px)}100%{opacity:1;transform:translateY(0)}}@keyframes glowtext{0%,100%{text-shadow:none}50%{text-shadow:0 0 16px rgba(var(--hue-4,59,200,178),.55)}}@keyframes hairline{0%{transform:translateX(-100%)}100%{transform:translateX(400%)}}@keyframes reticle{0%,100%{opacity:.35}50%{opacity:1}}@media (prefers-reduced-motion:reduce){.ember:where(:not(.ledger,.ledger *),.oldskool,.oldskool *){animation:none!important;opacity:0;filter:none}}@keyframes emberrise{0%{transform:translate3d(0,0,0);opacity:0}4%{opacity:var(--peak,.6)}50%{transform:translate3d(var(--dx,12px),-47vh,0)}66%{opacity:var(--peak,.6)}86%{opacity:0}100%{transform:translate3d(0,-94vh,0);opacity:0}}@keyframes emberglow{0%,100%{filter:brightness(.74)}50%{filter:brightness(1.16)}}@keyframes flame{0%,100%{transform:scaleY(1) scaleX(1);opacity:.92}30%{transform:scaleY(1.14) scaleX(.92);opacity:1}55%{transform:scaleY(.9) scaleX(1.06);opacity:.8}78%{transform:scaleY(1.06) scaleX(.97);opacity:.97}}
