/* ==========================================================================
   TELL — css/codex.css · THE COMMONPLACE BOOK
   ==========================================================================
   The codex drawer's stylesheet. index.html links it; js/codex.js owns the
   markup and the behaviour and never styles from script except as a fallback
   (see FALLBACK below).

   THE LOOK — "the fore-edge pull", elected 2026-08-31 from the two candidates
   in css/codex-candidates. Closed, the book is a 22px slice of its own PAGE
   BLOCK peeking past the frame's right edge — cream on the dark stage, almost
   nothing. Pull it and the ledger's right-hand page comes out of the book: a
   binding head board, cloth index tabs tucked under it, the spine slice at the
   inner edge, cue cards clipped on, ruled person rows, torn note-slips for the
   pings, and the plate lifted closer in a lamp-pool rather than a black box.

   This file IS the elected candidate, folded down: its html prefix is gone and
   the base rules it used to override have been rewritten to its values, so
   there is one rule per thing instead of a rule and a counter-rule. The
   --cxb-* token names are kept verbatim so this sheet can still be diffed
   against the candidate it came from.

   ------------------------------------------------------------- FALLBACK
   js/codex.js carries a copy of the PRE-ELECTION sheet in a `CSS` constant and
   injects it as <style id="codex-css"> ONLY when neither this file's <link> nor
   that <style> is already in the document. That copy is deliberately NOT this
   file: a <style> element resolves relative url() against the DOCUMENT, so the
   `../assets/ui/…` paths below would 404 from index.html and leave dark ink on
   a bare near-black panel. The fallback is the plain drawer — no plates, fully
   legible, self-consistent — which is the right thing to see when the
   stylesheet is missing. See the comment on that constant.

   -------------------------------------------------------------- URL LAW
   Every url() here is written against THIS file's location: css/codex.css, so
   `../assets/ui/…`. ui-paper.css's --plate-* tokens are not consumed even
   though they now resolve identically (same directory) — the candidate wrote
   its own, and keeping them local means this sheet survives being moved
   without a silent 404 into an empty image layer.

   ------------------------------------------------------- ORDER CONTRACT
   With the prefix gone, several state rules TIE their resting rule on
   specificity and are decided by SOURCE ORDER. Do not reshuffle these:

     .cx-tab:hover / :focus-visible   BEFORE  .cx-tab.cx-active     (both 1,2,0)
     .cx-tab:hover::before            BEFORE  .cx-tab.cx-active::before
     .cx-entry:nth-child(even)        BEFORE  .cx-entry:has(.cx-tell-name)
     everything                       BEFORE  the reduced-motion block

   The rest of the state rules outrank their resting rule outright and are safe
   wherever they sit: `#codex-drawer.cx-open .cx-panel` / `.cx-handle`,
   `.cx-ping.cx-in`, `.cx-ping .cx-line`, `.cx-person:hover`, `.cx-file
   .cx-chip`, `.cx-chip.cx-brass` are all (1,2,0) over (1,1,0). Nothing here
   uses !important. Adding a property to a resting rule means checking its state
   partner.

   ------------------------------------------------------------------ LAWS
   1  BLANK plates only; every word on screen is engine-drawn runtime text.
   2  ZERO saturated elements. Nothing here is red or green — the elected handle
      is the pale fore-edge, not a ribbon. The only red on the drawer is the
      dried-red margin rule PAINTED INTO ledger-spread-blank.webp, part of the
      asset — the erasure took the ruling and left both margin rules standing.
   3  Hovers are paper: the block slides out, a tab slides out from under the
      board, a ruled row takes a graphite underline. No glows.
   5  The handle's word is hidden by the `.quiet-live` clip-path idiom
      (ui.css:888-896), never display:none; codex.js carries the accessible
      name on the button's aria-label.
   6  Every focusable thing restyled here ships a visible :focus-visible — a 2px
      graphite keyline (rgba(58,63,69,.85), 8.6:1 on the plate's cream).
   7  Transitions run on --pp-t-* tokens (0ms under reduce) AND this file ships
      its own reduced-motion block. No keyframes; no state gated on a transition.
   8  `body.bare #codex-drawer .cx-handle { display:none !important }`
      (stage.css:68) is never fought — no rule here touches `display` on the
      handle.
   10 Labels/stamps --font-type; sightings and empty-page notes --font-hand;
      descriptive lines stay --font-prose.

   ------------------------------------------------------------ COMPOSITING
   NO mix-blend-mode anywhere. `.cx-panel` carries a translateX in both states,
   `.cx-tab` and `.cx-ping` carry transforms — each isolates a stacking context,
   which is what makes their `z-index:-1` plate pseudo safe, and which is also
   why a blended opaque plate inside one would land as an opaque rectangle.
   ========================================================================== */

#codex-drawer .cx-panel[inert] { pointer-events: none; }
#codex-drawer .cx-context { margin: 0 0 1.25rem; line-height: 1.6; }
#codex-drawer .cx-caveats ul { margin: .4rem 0 0; padding-left: 1.25rem; }
#codex-drawer .cx-caveats li + li { margin-top: .3rem; }

:root {
  /* The page. The handle rides this number too, so the two widths are ONE
     number and cannot drift apart. Declared here rather than on #codex-drawer
     so a sheet loaded after this one can still move both with a single line. */
  --cx-w: min(560px, 94vw);

  --cxb-band: 46px;      /* binding head board: also the page's top offset   */
  --cxb-gut: 30px;       /* spine slice at the drawer's left edge            */
  --cxb-tab-h: 52px;     /* full index tab, tuck included                    */
  --cxb-tuck: 18px;      /* how far each tab hides under the board           */
  --cxb-fore: 108px;     /* the page block's box; most of it is off-frame    */
  --cxb-peek: 22px;      /* how much of it shows when the book is shut       */

  /* THE BLANK TWIN, 2026-08-31. ledger-spread-blank.webp is the same 1600x1000
     canvas with the printed ruling erased and everything else — the spine, the
     worn edges, BOTH dried-red margin rules — untouched, so every crop and
     every offset in §1 below is still true and the swap is this one url. The
     ruling it lost is redrawn at runtime on `.cx-body` (§4), because a printed
     line is only worth having if the writing lands on it. */
  --cxb-page: url('../assets/ui/ledger-spread-blank.webp');  /* 1600x1000  */
  --cxb-tabs: url('../assets/ui/index-tabs-cut.webp');       /*  600x400 a */
  --cxb-edge: url('../assets/ui/book-fore-edge-cut.webp');   /*  200x1000a */
  --cxb-slip: url('../assets/ui/note-slip-cut.webp');        /*  700x420 a */
  --cxb-clip: url('../assets/ui/pin-sprites.png');           /*  800x400 a */

  --cxb-key: rgba(58, 63, 69, 0.85);
  --cxb-hair: rgba(34, 36, 43, 0.14);
}

/* The mount. `color` is the last-resort ink for anything this sheet does not
   reach, chosen against the panel's near-black ground.

   ------------------------------------------------------------ THE RULE LOCK
   Owner ruling 2026-08-31: on a papered surface the writing sits ON the line.
   The plate is blank now (see --cxb-page) and the ruling is a CSS fact, so the
   book's own type grid is what the rules are drawn to rather than the other way
   round. The mechanism, the shim's derivation and the measured face metrics are
   documented once, on :root in css/ui.css under THE RULE LOCK; only the numbers
   this sheet needs are restated here. Every --rp-* read below carries a literal
   fallback for the same reason the --pp-* reads do: a build that loads this
   sheet without ui.css must still get a legible ruled page.

   --rp lives on the DRAWER, not on :root, so nothing outside the book inherits
   a pitch from the book. 26px is the panel's own rhythm: it holds the 13.5px
   typed file rows, the 14.5px ledger heads and the 12.5px prose lines with the
   air a ledger has, and it is within a pixel of the pitch the erased ruling
   rendered at (29.5 source px x the 0.84 crop scale = 24.8px at cx-w 560).

   THE SHIM is `position: relative; top: (--rp − k x 1em) / 2` — the paint
   moves, the box does not, so a shimmed line still occupies exactly one rule
   and the grid under it cannot drift. `1em` is the element's OWN font-size. */
#codex-drawer { position: fixed; inset: 0 0 0 auto; z-index: 40; pointer-events: none;
  font-family: var(--font-ui, 'Segoe UI', system-ui, sans-serif); color: #F4F7F9;
  --rp: 26px;
  --cxb-rp-top: 16px;                /* .cx-body's padding-top = the grid's top */
  /* one tile: a hairline at the top, --rp tall, tiled down the page */
  --cxb-rules: linear-gradient(to bottom,
    var(--rp-ink, rgba(43, 58, 85, 0.28)) 0,
    var(--rp-ink, rgba(43, 58, 85, 0.28)) var(--rp-w, 1px),
    rgba(0, 0, 0, 0) var(--rp-w, 1px)); }

/* ==========================================================================
   1 · THE PAGE
   --------------------------------------------------------------------------
   THE CROP, derived once and reused by the gutter so the two cannot drift:

     background-size-x: 240%  -> the 1600px plate is drawn 2.4 x the panel
                                 wide, so the visible slice is 1600/2.4 =
                                 666.7 source px: the right page plus its
                                 outer margin.
     background-position-x: 85.714%  ->  k / (2(k-1)) for k = 2.4, which puts
                                 source x=800 (the spine) exactly on the
                                 panel's left edge and source x=1466.7 on its
                                 right. The printed red margin rule at source
                                 x=1435 therefore lands 27px in from the right
                                 edge at cx-w 560 — an outer margin rule, not
                                 a stripe through the text column.
     background-size-y: calc(100% - var(--cxb-band)) bottom-anchored -> the
                                 page starts BELOW the binding board.

   At cx-w 560 / 800px tall the scales are 0.84 x and 0.80 y: under 5% of
   anisotropy, invisible on painted paper.
   ========================================================================== */

#codex-drawer .cx-panel { position: absolute; top: 0; right: 0; height: 100%;
  box-sizing: border-box;
  width: var(--cx-w);
  background-color: #17110c;
  background-image: var(--cxb-page);
  background-repeat: no-repeat;
  background-size: 240% calc(100% - var(--cxb-band));
  background-position: 85.714% 100%;
  box-shadow:
    -2px 0 0 rgba(0, 0, 0, 0.42),
    -16px 0 40px rgba(6, 4, 2, 0.58);
  transform: translateX(102%);
  transition: transform var(--pp-t-settle, 320ms) var(--pp-ease-paper, ease);
  display: flex; flex-direction: column; pointer-events: auto; }
#codex-drawer.cx-open .cx-panel { transform: translateX(0); }

/* ---- the spine, in the W0 `.cx-gutter` hook -------------------------------
   The hook ships as an empty div between head and body — codex.js gives it
   aria-hidden and no content, so nothing here can be read aloud. Out of flow
   it becomes a 30px slice of the SAME plate at the SAME scale as the page:
   `calc(var(--cx-w) * 2.4)` IS the panel's background-size-x, and -1.167 x
   cx-w puts source x=778 at its left edge, so the slice runs 778 -> ~814 and
   carries the crease. The panel picks up again at source ~836. The RULES are
   horizontal, so a horizontal jump cannot break one — which is exactly why the
   vertical size and anchor must match the panel's. */
#codex-drawer .cx-gutter {
  display: block;                    /* the hook is painted now, not parked */
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  z-index: 0;
  width: var(--cxb-gut);
  pointer-events: none;
  background-image: var(--cxb-page);
  background-repeat: no-repeat;
  background-size: calc(var(--cx-w) * 2.4) calc(100% - var(--cxb-band));
  background-position: calc(var(--cx-w) * -1.167) 100%;
}
#codex-drawer .cx-gutter::after {
  content: '';
  position: absolute;
  inset: var(--cxb-band) 0 0 0;
  background:
    linear-gradient(90deg,
      rgba(23, 17, 12, 0.34) 0%,
      rgba(23, 17, 12, 0.52) 26%,
      rgba(23, 17, 12, 0.10) 62%,
      rgba(0, 0, 0, 0) 100%);
}

/* ==========================================================================
   2 · THE BINDING HEAD BOARD + THE INDEX TABS
   ========================================================================== */

#codex-drawer .cx-head {
  position: relative;
  z-index: 2;                        /* over the gutter (0) and the body (1) */
  padding: 0 16px 10px;
}
#codex-drawer .cx-head::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: var(--cxb-band);
  z-index: 2;
  background:
    linear-gradient(180deg,
      #1a1310 0%,
      #2e2219 40%,
      #3b2c21 76%,
      #241a13 96%,
      #0f0a07 100%);
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
}

/* COMMONPLACE BOOK blocked on the cloth — runtime type over a blank surface.
   Ivory, not typewriter ink: --pp-type on a near-black ground is a deletion,
   not a detune. */
#codex-drawer .cx-title {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  height: var(--cxb-band);
  margin: 0;
  padding-left: 16px;                /* clears the spine slice */
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  text-transform: uppercase;
  color: rgba(234, 223, 200, 0.86);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
}

#codex-drawer .cx-tabs {
  display: flex;
  position: relative;
  z-index: 1;                        /* UNDER the board — that is the tuck */
  gap: 8px;
  margin-top: calc(var(--cxb-tuck) * -1);
  margin-left: 16px;
}

/* THE TAB CROP. index-tabs-cut is a 3x1 row of 200x400 cells of TALL cloth
   tabs; this bar wants short wide ones, so the crop takes the BOTTOM END of
   each tab — its real painted cut edge and rubbed corner — and lets the rest
   run up under the board.

     background-size: 405% 200px
       405% of the button width makes one cell 1.35 x the button, so the tab
       inside it (148/200 of the cell) draws exactly button-wide. 200px fixes
       the vertical scale at 0.5 whatever the drawer height does.
     background-position-x = x0 * 0.0022131, from p = x0*(bgW/600)/(bgW - W)
       with bgW = 4.05W. Tab left edges at source x 22 / 228 / 425
       -> 4.87% / 50.47% / 94.07%.
     background-position-y lands each tab's painted bottom edge at element
       y=45 (7px of headroom for the contact shadow). The three tabs end at
       source y 370 / 366 / 374, so the three offsets differ.

   2.2x anisotropy on a crosshatch weave reads as ribbed cloth; the three
   cells keep their real colour differences (warm tan / grey-green / sand).
   `transform: translateY(0)` is load-bearing: it isolates the stacking context
   that keeps the ::before plate's z-index:-1 inside the button. */
#codex-drawer .cx-tab {
  flex: 1;
  position: relative;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--cxb-tab-h);
  padding: var(--cxb-tuck) 4px 4px;
  border: 0;
  border-radius: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.14em;
  line-height: 1.1;
  text-align: center;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--pp-type-dim, rgba(34, 36, 43, 0.62));
  transform: translateY(0);
  transition:
    transform var(--pp-t-lift, 140ms) var(--pp-ease-paper, ease),
    color var(--pp-t-lift, 140ms) var(--pp-ease-paper, ease);
}
#codex-drawer .cx-tab::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--cxb-tabs);
  background-repeat: no-repeat;
  background-size: 405% 200px;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.38));
  transition: filter var(--pp-t-lift, 140ms) var(--pp-ease-paper, ease);
}
#codex-drawer .cx-tab:nth-child(1)::before { background-position: 4.87% -140px; }
#codex-drawer .cx-tab:nth-child(2)::before { background-position: 50.47% -138px; }
#codex-drawer .cx-tab:nth-child(3)::before { background-position: 94.07% -142px; }

/* ORDER CONTRACT: hover/focus tie .cx-active at (1,2,0) on `transform` and
   `color` (and on `filter` for the ::before pair). They come FIRST so the tab
   you are pointing at while it is already active keeps the deeper active
   travel instead of springing back to the hover travel. */
#codex-drawer .cx-tab:hover,
#codex-drawer .cx-tab:focus-visible {
  transform: translateY(3px);
  color: var(--pp-type, #22242b);
}
#codex-drawer .cx-tab:hover::before,
#codex-drawer .cx-tab:focus-visible::before {
  filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.44));
}
#codex-drawer .cx-tab.cx-active {
  transform: translateY(5px);
  color: var(--pp-type, #22242b);
  font-weight: 700;
}
#codex-drawer .cx-tab.cx-active::before {
  filter: drop-shadow(0 5px 7px rgba(0, 0, 0, 0.46));
}
#codex-drawer .cx-tab:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--cxb-key);
}

/* ==========================================================================
   3 · THE HANDLE — the book's fore-edge
   --------------------------------------------------------------------------
   book-fore-edge-cut is 200x1000 with the page block at x6-193 / y30-969 and
   a dark cover sliver overhanging top and bottom. The plate fills the handle
   box (`background-size: 100% 100%`), so the block sits ~3px inside each side
   and ~3% of the height in from top and bottom — the transparent margins ARE
   the object's air, not a mistake.

   Closed: right = peek - fore = -86px, so 22px of block shows past the
   frame's right edge and the viewport clips the rest. The peek is a NEGATIVE
   OFFSET on a 108px box, not a 22px plate: squashing the whole 200x1000 plate
   into a 22px column would compress the page striations ~9x into moire, where
   at 108px the squash is 1.3x and the block still reads as paper. The click
   target is the visible 22px x 88vh strip — large in the axis that matters.

   Open: right = var(--cx-w). The block travels with the page, so it reads as
   the page being pulled OUT of the book. That rule is (1,2,0) over this one's
   (1,1,0) and wins outright; the :hover and :focus-visible rules below tie it
   at (1,2,0) but share no property with it.

   Law 8: nothing here touches `display` — stage.css's `body.bare` hide is
   never fought.
   ========================================================================== */

#codex-drawer .cx-handle {
  position: absolute;
  top: 6vh;
  bottom: 6vh;
  right: calc(var(--cxb-peek) - var(--cxb-fore));
  width: var(--cxb-fore);
  z-index: 1;
  padding: 0;
  border: 0;
  border-radius: 0;
  background-color: transparent;
  background-image: var(--cxb-edge);
  background-repeat: no-repeat;
  background-size: 100% 100%;
  cursor: pointer;
  pointer-events: auto;
  filter: drop-shadow(-3px 0 6px rgba(0, 0, 0, 0.55));
  transform: translateX(0);
  transition:
    right var(--pp-t-settle, 320ms) var(--pp-ease-paper, ease),
    transform var(--pp-t-lift, 140ms) var(--pp-ease-paper, ease),
    filter var(--pp-t-lift, 140ms) var(--pp-ease-paper, ease);
}
#codex-drawer.cx-open .cx-handle { right: var(--cx-w); }
/* Hover offers the pull: the block juts a little further into the frame. */
#codex-drawer .cx-handle:hover {
  transform: translateX(-7px);
  filter: drop-shadow(-5px 0 9px rgba(0, 0, 0, 0.6));
}
/* Focus does the same thing MORE, plus a graphite keyline: on a 22px sliver a
   ring alone is a hairline, so the affordance is ring + travel together. */
#codex-drawer .cx-handle:focus-visible {
  transform: translateX(-14px);
  filter: drop-shadow(-5px 0 9px rgba(0, 0, 0, 0.6));
  outline: 2px solid var(--cxb-key);
  outline-offset: 0;
}

/* Law 5: clip-path, never display:none — the span stays in the tree and
   codex.js's aria-label on the button is the accessible name. */
#codex-drawer .cx-handle-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  white-space: nowrap;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
}

/* ==========================================================================
   4 · THE BODY — ink on the page
   ========================================================================== */

/* THE RULING IS DRAWN HERE AND NOT ON THE PANEL, and that is the whole point:
   `.cx-body` is what SCROLLS. A ruling printed on `.cx-panel` would hold still
   while the writing slid over it, which is worse than no ruling at all.
   `background-attachment: local` is the shipped idiom for exactly this — it is
   what js/feedback.js's ruled writing lines use inside their textarea — and it
   makes the rules travel with the type at every scroll position.

     background-size  100% − --cxb-gut  positioned at 100%  ->  the rules run
       from the spine slice's right edge out to the page's outer margin. They
       stop at the gutter deliberately: `.cx-body` paints above `.cx-gutter`, so
       a full-width ruling would draw un-shaded lines straight across the fold.
     background-position-y  --cxb-rp-top + --rp-drop  ->  the body's own
       padding-top is where the type grid starts, so the first rule lands one
       --rp-drop under it and every rule after it is a whole --rp below, which
       is where the shimmed baselines are.

   The pasted objects on the page — cue-card clippings, the case file, the
   note-slips — are opaque and simply cover the rules under them. That is
   correct: paper laid on a ruled page hides its lines, and their own interiors
   are NOT quantized (a clipping keeps its own rhythm; only its outer margin
   sits on the book's grid). */
#codex-drawer .cx-body {
  flex: 1;
  overflow-y: auto;
  position: relative;
  z-index: 1;
  /* right padding is 34px on purpose: the crop puts the plate's PRINTED red
     margin rule 27px in from the right edge at cx-w 560 (17px at 94vw), so a
     34px gutter keeps every line of type inside the ruled column instead of
     running across the rule. Left is wider because the spine is on that side. */
  padding: var(--cxb-rp-top) 34px 34px calc(var(--cxb-gut) + 12px);
  color: var(--pp-type, #22242b);
  background-image: var(--cxb-rules);
  background-attachment: local;
  background-repeat: repeat-y;
  background-size: calc(100% - var(--cxb-gut)) var(--rp);
  background-position: 100% calc(var(--cxb-rp-top) + var(--rp-drop, 0.1rem));
  scrollbar-width: thin;
  scrollbar-color: rgba(58, 63, 69, 0.45) transparent;
}

/* The faculties header block. Its `border-bottom` was a printed rule, imitated
   on a page that had none; the page prints its own now, one --rp-drop under
   this line's own baseline, so the border comes off — two lines a pixel apart
   is a mistake, not a flourish. The pair's -10px tuck becomes a whole −1 rule
   so the two heads still read as one block and the block still ends on a rule. */
#codex-drawer .cx-mirror {
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  line-height: var(--rp);
  position: relative;
  top: calc((var(--rp) - var(--rp-k-type, 0.44) * 1em) / 2);
  text-transform: uppercase;
  color: var(--pp-type-dim, rgba(34, 36, 43, 0.62));
  border-bottom: 0;
  padding-bottom: 0;
  margin: 0 0 var(--rp);
}
#codex-drawer .cx-mirror + .cx-mirror {
  border-bottom: 0;
  margin-top: calc(var(--rp) * -1);
  padding-bottom: 0;
}

/* The empty page's note — the line the owner caught floating mid-rule. It is
   marginalia in the hand, so it keeps its half-degree of tilt (over one line
   that is a pixel and a third at the ends, which is what a hand does on ruled
   paper); what it loses is the fractional padding that put it between lines. */
#codex-drawer .cx-empty {
  font-family: var(--font-hand, 'Segoe Script', cursive);
  font-size: 17px;
  line-height: var(--rp);
  position: relative;
  top: calc((var(--rp) - var(--rp-k-hand, 0.66) * 1em) / 2);
  color: var(--pp-graphite, #3a3f45);
  padding: 0 8px 0 10px;
  transform: rotate(-0.5deg);
}

/* ==========================================================================
   5 · CUE CARDS — pasted clippings
   ========================================================================== */

/* A CLIPPING FLOATS OVER THE RULING AND ITS INTERIOR IS NOT QUANTIZED — a
   pasted card keeps the rhythm it was set in, and forcing its lines onto the
   book's grid would be the one place the ruling stopped meaning "this is the
   page". Its OUTER margin is on the grid so the page still reads tidy, and
   nothing quantized ever follows a clipping (js/codex.js renders the header
   block, then either the empty note or the cards — the cards are last), so an
   unquantized card height cannot push a written line off a rule. */
#codex-drawer .cx-entry {
  position: relative;
  border: 1px solid var(--cxb-hair);
  padding: 13px 15px 14px;
  margin: 0 2px var(--rp) 2px;
  background-color: var(--pp-card, #ede6d6);
  background-image:
    radial-gradient(120% 90% at 14% 0%, rgba(255, 255, 255, 0.30), rgba(0, 0, 0, 0) 58%),
    radial-gradient(100% 80% at 90% 100%, rgba(90, 70, 40, 0.13), rgba(0, 0, 0, 0) 55%);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.30),
    0 9px 17px -9px rgba(0, 0, 0, 0.42);
  transform: rotate(-0.45deg);
}
#codex-drawer .cx-entry:nth-child(even) {
  transform: rotate(0.4deg);
}

/* The paperclip. pin-sprites is 800x400; the upright clip in row 2 col 2 sits
   at roughly x214-294 / y206-376, so at 16x34 the scale is 0.2 —
   background-size 160x80, background-position -43px/-41px. Same fixed-element
   + scaled-sheet + positioned-cell idiom as `.cw-pin` (chapter-map.css:186).
   The case file clips its photo on with a drawn clip, so the book's fastener
   is a CLIP, not a corkboard pin. */
#codex-drawer .cx-entry::before {
  content: '';
  position: absolute;
  top: -12px;
  left: 18px;
  width: 16px;
  height: 34px;
  background-image: var(--cxb-clip);
  background-repeat: no-repeat;
  background-size: 160px 80px;
  background-position: -43px -41px;
  filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.42));
  pointer-events: none;
}
#codex-drawer .cx-entry:nth-child(even)::before {
  left: auto;
  right: 22px;
  transform: scaleX(-1);
}

#codex-drawer .cx-cue-id {
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 9.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--pp-type-dim, rgba(34, 36, 43, 0.62));
}
#codex-drawer .cx-cue-name {
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 14.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  line-height: 1.25;
  margin: 4px 0 7px;
  color: var(--pp-type, #22242b);
}
#codex-drawer .cx-line {
  font-family: var(--font-prose, Georgia, serif);
  font-size: 12.5px;
  line-height: 1.55;
  margin: 5px 0;
  color: var(--pp-graphite, #3a3f45);
}
#codex-drawer .cx-line b {
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 0.84em;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--pp-type-dim, rgba(34, 36, 43, 0.62));
}
#codex-drawer .cx-sight {
  font-family: var(--font-hand, 'Segoe Script', cursive);
  font-size: 15px;
  line-height: 1.3;
  color: var(--pp-graphite, #3a3f45);
  border-left: 1px solid rgba(58, 63, 69, 0.38);
  padding: 1px 0 1px 10px;
  margin: 7px 0 4px;
}
#codex-drawer .cx-stim {
  font-family: var(--font-prose, Georgia, serif);
  font-style: italic;
}

#codex-drawer .cx-chip {
  display: inline-block;
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 8.8px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--pp-type-dim, rgba(34, 36, 43, 0.62));
  border: 1px solid rgba(34, 36, 43, 0.30);
  padding: 1px 6px;
  margin-left: 6px;
  vertical-align: middle;
}
/* Brass grounded for cream — #c9973f on paper is a highlighter. Still inside
   the paper/ink/graphite/BRASS range; not a saturated element. (1,2,0) over
   .cx-chip's (1,1,0); `cx-brass` is only ever put on a chip.) */
#codex-drawer .cx-chip.cx-brass {
  color: #7a5a22;
  border-color: rgba(122, 90, 34, 0.45);
}

/* ==========================================================================
   6 · SAM'S LEDGER — ruled entries
   --------------------------------------------------------------------------
   `:has()` splits one class into two objects. `.cx-entry` serves both the cues
   tab and Sam's Ledger; 50-ui-redress §1 wants pasted clippings in one and
   ruled entries in the other. The discriminator is structural — a ledger card
   contains `.cx-tell-name` — so the clipping is the default and
   `:has(.cx-tell-name)` flattens it. Without `:has()` the ledger stays
   clippings: a degrade, not a break.

   ORDER CONTRACT: `:has(.cx-tell-name)` and `:nth-child(even)` are both
   (1,2,0), so this section MUST follow §5 or the even-numbered ledger rows
   keep their rotation.
   ========================================================================== */

/* A LEDGER ENTRY IS NOT A CLIPPING: `:has()` flattens it onto the page itself,
   so unlike §5's cards its lines DO sit on the book's ruling and every one of
   them is quantized. Head, then one prose line per rule, then a rule of air. */
#codex-drawer .cx-tell-name {
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 14.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  line-height: var(--rp);
  position: relative;
  top: calc((var(--rp) - var(--rp-k-type, 0.44) * 1em) / 2);
  color: var(--pp-type, #22242b);
}
/* the entry's `border-bottom` was the page's ruling, imitated — the page has
   its own now, and a border a pixel above a printed rule reads as a smudge */
#codex-drawer .cx-entry:has(.cx-tell-name) {
  border: 0;
  background: none;
  box-shadow: none;
  transform: none;
  padding: 0 2px;
  margin: 0 2px var(--rp);
}
#codex-drawer .cx-entry:has(.cx-tell-name) .cx-line {
  line-height: var(--rp);
  position: relative;
  top: calc((var(--rp) - var(--rp-k-prose, 0.698) * 1em) / 2);
  margin: 0;
}
#codex-drawer .cx-entry:has(.cx-tell-name)::before {
  display: none;                     /* decorative pseudo only — never a label */
}
/* The `No. N` chip is an inline-block INSIDE the head line, and it must not be
   allowed to inherit the head's 26px line-height: with its 1px border and 1px
   vertical padding that makes a 30px box, and a 30px middle-aligned box in a
   26px line box GROWS the line box — ~4px of drift per entry, compounding down
   the ledger. A tight own line-height keeps the chip hugging its label and the
   head's line box exactly one rule tall. */
#codex-drawer .cx-entry:has(.cx-tell-name) .cx-chip {
  line-height: 1.3;
}

/* ==========================================================================
   7 · THE CASE FILES — the list of files is ruled
   ========================================================================== */

#codex-drawer .cx-people { display: flex; flex-direction: column;
  gap: 0;
  margin-top: 2px; }
/* ONE FILE PER RULE. The row's `border-bottom` was a drawn ruling standing in
   for the page's; the page prints its own now, one --rp-drop under this row's
   own baseline, so the border comes off and the LINE the name sits on is the
   book's. Its hover underline lands a hairline above that printed rule, which
   is why the sweep still reads as "the rule darkens under the pointer" rather
   than as a second line appearing. */
#codex-drawer .cx-person {
  display: block; width: 100%; text-align: left;
  font: inherit;                     /* must precede the font-* below */
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 13.5px;
  letter-spacing: 0.06em;
  line-height: var(--rp);
  position: relative;
  top: calc((var(--rp) - var(--rp-k-type, 0.44) * 1em) / 2);
  border: 0;
  padding: 0 4px 0 2px;
  cursor: pointer;
  color: var(--pp-type, #22242b);
  /* the `.paper-underline` technique (ui-paper.css:230) — a graphite line
     drawn under the entry, not a lit row. */
  background-color: transparent;
  background-image: linear-gradient(var(--pp-graphite, #3a3f45), var(--pp-graphite, #3a3f45));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1.5px;
  transition: background-size var(--pp-t-lift, 140ms) var(--pp-ease-paper, ease);
}
/* (1,2,0) over the resting rule's (1,1,0). Only the underline sweep is stated:
   the colour and the rule line it used to have to restore are now the resting
   values themselves. */
#codex-drawer .cx-person:hover,
#codex-drawer .cx-person:focus-visible {
  background-size: 100% 1.5px;
}
#codex-drawer .cx-person:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--cxb-key);
}

/* `display: block` with `width: fit-content` and not plain `display: block`:
   the button is the only typed line on the page here and has to own a block box
   to be shimmed onto a rule, but a full-width block would silently turn "‹
   back" into a click target the width of the page. */
#codex-drawer .cx-back {
  background: none; border: none;
  font: inherit;                     /* must precede the font-* below */
  font-family: var(--font-type, 'Courier New', monospace);
  font-size: 9.5px;
  letter-spacing: 0.26em;
  line-height: var(--rp);
  display: block;
  width: fit-content;
  position: relative;
  top: calc((var(--rp) - var(--rp-k-type, 0.44) * 1em) / 2);
  text-transform: uppercase;
  color: var(--pp-type-dim, rgba(34, 36, 43, 0.62));
  padding: 0 2px;
  margin: 0 0 var(--rp);
  cursor: pointer;
}
#codex-drawer .cx-back:hover, #codex-drawer .cx-back:focus-visible {
  color: var(--pp-type, #22242b);
  text-decoration: underline;
  text-decoration-color: var(--pp-graphite, #3a3f45);
  text-underline-offset: 3px;
}
#codex-drawer .cx-back:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--cxb-key);
}

/* --- THE CASE FILE PAGE ---------------------------------------------------
 * SHIPPED AND AT HOME. Everything from here to the zoom is the page as it was
 * built and is carried through unchanged — the elected look deliberately does
 * not touch `.cx-file`, `.cx-photo`, `.cx-takes`, `.cx-note`, `.cx-sec`,
 * `.cx-clipping` or the ticks.
 *
 * css/ui-paper.css is loaded globally (index.html, after theme.css) and every
 * --pp-* token below comes from it. Each one carries a literal fallback anyway:
 * a storyboard build that never loaded the paper layer must still get a light
 * page with dark ink on it, and inheriting the drawer's #F4F7F9 onto cream
 * paper would erase the whole file. Colour is stated on .cx-file for that
 * reason — nothing here inherits its ink.
 * NO RED, NO GREEN, no glow, no pulse: the page is paper, and paper does not
 * grade. --pp-red is not referenced anywhere in this block. */

#codex-drawer .cx-file { background: var(--pp-ledger, #e8dfc9); color: var(--pp-type, #22242b);
  border-radius: 2px; padding: 20px 16px 24px;
  box-shadow: 0 2px 3px rgba(0,0,0,0.6), 0 14px 26px -8px rgba(0,0,0,0.55);
  background-image: radial-gradient(120% 90% at 12% 0%, rgba(255,255,255,0.28), rgba(0,0,0,0) 60%),
                    radial-gradient(100% 80% at 92% 100%, rgba(90,70,40,0.14), rgba(0,0,0,0) 55%); }
#codex-drawer .cx-file-name { font-family: var(--font-type, 'Courier New', monospace);
  letter-spacing: var(--track-type, 0.06em); font-size: 17px; font-weight: 700;
  margin: 0 0 14px; color: var(--pp-type, #22242b); }
#codex-drawer .cx-small { font-family: var(--font-type, 'Courier New', monospace);
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--pp-type-dim, rgba(34,36,43,0.62)); }
/* (1,2,0) over §5's .cx-chip (1,1,0). It restates the five properties the
   drawer's chip changed — face, size, tracking, radius, padding — because the
   file's chip is a ROUNDED UI chip on paper, not a square typed stamp. */
#codex-drawer .cx-file .cx-chip {
  font-family: var(--font-ui, 'Segoe UI', system-ui, sans-serif);
  font-size: 10px;
  letter-spacing: 2px;
  border-radius: 999px;
  padding: 1px 8px;
  color: var(--pp-type-dim, rgba(34,36,43,0.62));
  border-color: rgba(34,36,43,0.30); margin-left: 0; margin-right: 6px; }

/* The photo, clipped to the page: a real paperclip over the top edge, a contact
   shadow, and a degree and a half of rotation. CSS only — no plate asset. */
#codex-drawer .cx-shot { display: block; width: min(272px, 86%); margin: 4px auto 0;
  padding: 0; background: none; border: none; cursor: zoom-in; }
#codex-drawer .cx-shot[disabled] { cursor: default; }
#codex-drawer .cx-photo { position: relative; background: #efe9dc; padding: 9px 9px 11px;
  border: 1px solid rgba(34,36,43,0.16); transform: rotate(-1.4deg);
  box-shadow: 0 2px 4px rgba(0,0,0,0.35), 0 10px 18px -8px rgba(0,0,0,0.45);
  transition: transform 140ms ease, box-shadow 140ms ease; }
#codex-drawer .cx-shot:hover .cx-photo, #codex-drawer .cx-shot:focus-visible .cx-photo {
  transform: rotate(-1.4deg) translateY(-2px);
  box-shadow: 0 3px 7px rgba(0,0,0,0.4), 0 14px 22px -8px rgba(0,0,0,0.5); }
#codex-drawer .cx-clipmark { position: absolute; top: -15px; left: 26px; width: 13px; height: 40px;
  border: 2px solid rgba(58,63,69,0.5); border-radius: 7px; border-bottom-color: transparent; }
#codex-drawer .cx-clipmark::after { content: ''; position: absolute; top: 6px; left: 2px;
  width: 5px; height: 27px; border: 2px solid rgba(58,63,69,0.42);
  border-radius: 4px; border-bottom-color: transparent; }
#codex-drawer .cx-frame { aspect-ratio: 16 / 9; background: #d6cdb9;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.22); overflow: hidden; }
#codex-drawer .cx-frame img { display: block; width: 100%; height: 100%; object-fit: cover; }
#codex-drawer .cx-cap { text-align: center; margin: 10px 0 0; }

/* The version stack. Superseded takes sit under the current one, smallest
   possible: a leaf, not a gallery. */
#codex-drawer .cx-takes { display: flex; flex-wrap: wrap; gap: 7px; justify-content: center;
  margin: 12px 0 0; }
#codex-drawer .cx-take { width: 52px; height: 33px; padding: 0; overflow: hidden;
  background: #d6cdb9; border: 1px solid rgba(34,36,43,0.3); cursor: pointer;
  transition: transform 140ms ease; }
#codex-drawer .cx-take img { display: block; width: 100%; height: 100%; object-fit: cover; }
#codex-drawer .cx-take:hover, #codex-drawer .cx-take:focus-visible { transform: translateY(-2px); }
#codex-drawer .cx-take[aria-pressed="true"] { outline: 1px solid var(--pp-type, #22242b);
  outline-offset: 2px; }

#codex-drawer .cx-note { font-family: var(--font-hand, 'Segoe Script', cursive);
  color: var(--pp-graphite, #3a3f45); font-size: 16px; line-height: 1.3;
  margin: 18px 4px 0; }
#codex-drawer .cx-sec { font-family: var(--font-type, 'Courier New', monospace);
  font-size: 10px; letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--pp-type-dim, rgba(34,36,43,0.62));
  border-bottom: 1px solid var(--pp-rule, rgba(43,58,85,0.28));
  padding-bottom: 4px; margin: 20px 0 10px; }
#codex-drawer .cx-clipping { background: #f2ecdd; border: 1px solid rgba(34,36,43,0.13);
  box-shadow: 0 1px 2px rgba(0,0,0,0.2); padding: 8px 10px 9px; margin: 0 0 9px;
  transform: rotate(-0.3deg); }
#codex-drawer .cx-clipping:nth-child(even) { transform: rotate(0.35deg); }
#codex-drawer .cx-clip-name { font-size: 13px; color: var(--pp-type, #22242b); margin-bottom: 2px; }
#codex-drawer .cx-clip-stim { font-style: italic; font-size: 12.5px;
  color: var(--pp-graphite, #3a3f45); margin-top: 4px; line-height: 1.4; }
/* The ticks. Faint, unlabelled, and never the cue's name — a mark in the margin
   of a page nobody paid to read. */
#codex-drawer .cx-ticks { display: flex; flex-wrap: wrap; gap: 8px 14px; margin: 22px 4px 0; }
#codex-drawer .cx-tick { color: var(--pp-type-faint, rgba(34,36,43,0.34)); }
#codex-drawer .cx-tick i { font-family: var(--font-hand, 'Segoe Script', cursive);
  font-style: normal; font-size: 15px; margin-right: 5px; }

/* ==========================================================================
   8 · PINGS — torn note-slips
   --------------------------------------------------------------------------
   note-slip-cut is 700x420, slip at x28-671 / y45-373, ONE ragged edge along
   the top. Stretched onto a ping card (~340 x ~110) the vertical squash is
   1.85x — the tear goes a little shallower and nothing else — so the plate is
   used whole rather than 9-sliced. Its transparent canvas margins (~14px each
   side, ~12px top and bottom at that size) are why the padding is generous:
   the text has to sit inside the PAPER, not inside the element box.

   The plate rides a ::before so `drop-shadow()` follows the torn silhouette
   instead of boxing it, and so the typed text does not inherit a shadow.
   `.cx-ping` carries a transform in both states, which isolates the stacking
   context and makes `z-index:-1` safe.
   ========================================================================== */

#codex-drawer .cx-pings { position: absolute; right: 14px; bottom: 16px; width: min(340px, 86vw);
  display: flex; flex-direction: column; gap: 10px; pointer-events: none; }
#codex-drawer .cx-ping {
  position: relative;
  min-height: 96px;
  padding: 24px 26px 20px;
  color: var(--pp-type, #22242b);
  opacity: 0;
  transform: translateX(26px) rotate(-1.1deg);
  transition:
    transform var(--pp-t-settle, 320ms) var(--pp-ease-paper, ease),
    opacity var(--pp-t-settle, 320ms) var(--pp-ease-paper, ease);
  pointer-events: auto; cursor: pointer; }
/* (1,2,0) over the resting rule's (1,1,0) — without it every slip freezes in
   its off-stage position. The rotation is restated because `transform` is one
   property, not two. */
#codex-drawer .cx-ping.cx-in { transform: translateX(0) rotate(-1.1deg); opacity: 1; }
#codex-drawer .cx-ping::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--cxb-slip);
  background-repeat: no-repeat;
  background-size: 100% 100%;
  filter:
    drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55))
    drop-shadow(0 12px 20px rgba(0, 0, 0, 0.40));
}
#codex-drawer .cx-ping .cx-line { color: var(--pp-graphite, #3a3f45); margin: 4px 0; }
#codex-drawer .cx-ping .cx-cue-name { margin: 3px 0 6px; }

/* ==========================================================================
   9 · THE ZOOM — the plate lifted closer, not a black lightbox
   --------------------------------------------------------------------------
   Function untouched: codex.js owns the dialog role, click-to-close and the
   Escape handler, and there is no animation in it at all, so there is nothing
   for reduced motion to switch off.
   ========================================================================== */

#codex-drawer .cx-zoom { position: fixed; inset: 0; z-index: 3; display: flex;
  align-items: center; justify-content: center; padding: 24px; cursor: zoom-out;
  background-color: rgba(20, 15, 11, 0.90);
  background-image:
    radial-gradient(78% 62% at 50% 40%, rgba(232, 223, 201, 0.11), rgba(0, 0, 0, 0) 62%);
  pointer-events: auto; }
#codex-drawer .cx-zoom img { max-width: min(92vw, 1200px); max-height: 86vh;
  box-sizing: border-box;
  /* the photo-mount border the act-break images already use (index.html:731):
     a card mount with a deeper bottom margin. */
  border: clamp(8px, 1.4vw, 16px) solid var(--pp-card, #ede6d6);
  border-bottom-width: clamp(18px, 3vw, 34px);
  background: #d6cdb9;
  box-shadow: var(--pp-shadow-contact,
    0 2px 3px rgba(0, 0, 0, 0.72),
    0 14px 26px -8px rgba(0, 0, 0, 0.78));
  transform: rotate(-0.35deg); }

/* A readable book at every reading size. The printed ruling grows with its
   type; clipping interiors still follow their own prose rhythm. The existing
   page, cloth tabs, paperclips and fore-edge remain the book's materials. */
#codex-drawer {
  --cx-scale: var(--reading-scale, 1);
  --rp: calc(28px * var(--cx-scale));
  --cxb-band: calc(60px * var(--cx-scale));
  --cxb-tab-h: calc(64px * var(--cx-scale));
  --cxb-tuck: calc(12px * var(--cx-scale));
  font-size: calc(16px * var(--cx-scale));
}
#codex-drawer .cx-head { flex: none; padding: 0 12px 12px calc(var(--cxb-gut) + 4px); }
#codex-drawer .cx-heading {
  position: relative; z-index: 3; display: flex; align-items: center;
  gap: 10px; min-height: var(--cxb-band);
}
#codex-drawer .cx-title {
  flex: 1; min-width: 0; height: auto; padding-left: 0;
  font-size: calc(14px * var(--cx-scale)); line-height: 1.35;
  letter-spacing: .03em; text-indent: 0;
}
#codex-drawer .cx-close {
  flex: none; min-width: 44px; min-height: 44px;
  padding: 9px 10px; max-width: calc(96px * var(--cx-scale));
  font: 700 calc(14px * var(--cx-scale))/1.3 var(--font-type, 'Courier New', monospace);
  color: #eee3cd; background: rgba(0, 0, 0, .12);
  border: 1px solid rgba(238, 227, 205, .6); border-radius: 2px;
  cursor: pointer;
}
#codex-drawer .cx-close:hover { background: rgba(238, 227, 205, .12); }
#codex-drawer .cx-close:focus-visible { outline: 2px solid #eee3cd; outline-offset: 3px; }
#codex-drawer .cx-tabs { margin-left: 0; gap: 6px; }
#codex-drawer .cx-tab {
  min-width: 0; min-height: 44px; white-space: normal;
  font-size: calc(14px * var(--cx-scale)); line-height: 1.25;
  letter-spacing: .025em; color: var(--pp-graphite, #3a3f45);
}
#codex-drawer .cx-tab:nth-child(n)::before {
  background-size: 405% calc(var(--cxb-tab-h) * 4);
  background-position-y: calc(var(--cxb-tab-h) * -2.82);
}
#codex-drawer .cx-body { min-height: 0; overflow-wrap: anywhere; }
#codex-drawer .cx-faculties { margin: 0 0 calc(var(--rp) / 2); }
#codex-drawer .cx-faculty-toggle {
  min-height: 44px; box-sizing: border-box; padding: 7px 0;
  font: 700 calc(14px * var(--cx-scale))/1.5 var(--font-type, 'Courier New', monospace);
  color: var(--pp-type, #22242b); cursor: pointer;
}
#codex-drawer .cx-faculty-toggle:focus-visible { outline: 2px solid var(--cxb-key); outline-offset: 2px; }
#codex-drawer .cx-faculty-list {
  padding-left: 1.25em; margin: 4px 0 12px;
  font: calc(14px * var(--cx-scale))/1.6 var(--font-type, 'Courier New', monospace);
  color: #53504a;
}
#codex-drawer .cx-faculty-list li + li { margin-top: .35em; }
#codex-drawer .cx-body :is(.cx-context, .cx-line, .cx-clip-name, .cx-clip-stim, .cx-sight, .cx-note) {
  font-size: calc(16px * var(--cx-scale));
}
#codex-drawer .cx-body :is(.cx-mirror, .cx-cue-id, .cx-small, .cx-sec, .cx-back, .cx-chip),
#codex-drawer .cx-body .cx-file .cx-chip,
#codex-drawer .cx-body .cx-line b {
  font-size: calc(14px * var(--cx-scale)); letter-spacing: .04em;
  color: #53504a;
}
#codex-drawer .cx-body :is(.cx-cue-name, .cx-tell-name, .cx-person) {
  font-size: calc(16px * var(--cx-scale));
}
#codex-drawer .cx-file-name { font-size: calc(18px * var(--cx-scale)); }
#codex-drawer .cx-empty { font-size: calc(17px * var(--cx-scale)); }
#codex-drawer .cx-back { min-height: 44px; }
#codex-drawer .cx-person { min-height: calc(var(--rp) * 2); }
#codex-drawer .cx-take { min-width: 52px; min-height: 44px; }
#codex-drawer .cx-frame:has(img) { aspect-ratio: auto; }
#codex-drawer .cx-frame img { height: auto; object-fit: contain; }
#codex-drawer .cx-body .cx-chip { max-width: 100%; box-sizing: border-box; }
#codex-drawer .cx-body .cx-chip.cx-brass { color: #7a5a22; }
@media (min-width: 360px) {
  #codex-drawer .cx-close { max-width: none; white-space: nowrap; }
}
@media (max-width: 480px) {
  #codex-drawer .cx-body { padding-left: calc(var(--cxb-gut) + 4px); padding-right: 22px; }
  #codex-drawer .cx-entry { padding-left: 12px; padding-right: 12px; }
  #codex-drawer .cx-file { padding-left: 12px; padding-right: 12px; }
}

/* ==========================================================================
   10 · REDUCED MOTION
   --------------------------------------------------------------------------
   Every duration above is a --pp-t-* token and ui-paper.css already zeroes
   those under reduce; this block is the belt to that braces, for the hardcoded
   140ms transitions on the case-file page and for any build that loads this
   sheet without the paper layer.

   ORDER CONTRACT: a media query adds no specificity, so these rules tie their
   non-reduce partners and MUST stay last in the file. No keyframes anywhere;
   no state is gated on a transition, and the end state of every rule above is
   the state you see with motion off.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  #codex-drawer .cx-panel,
  #codex-drawer .cx-handle,
  #codex-drawer .cx-ping,
  #codex-drawer .cx-tab,
  #codex-drawer .cx-tab::before,
  #codex-drawer .cx-person { transition: none; }
  #codex-drawer .cx-photo, #codex-drawer .cx-take { transition: none; }
  #codex-drawer .cx-handle:hover { transform: translateX(0); }
  #codex-drawer .cx-shot:hover .cx-photo, #codex-drawer .cx-shot:focus-visible .cx-photo {
    transform: rotate(-1.4deg); }
  #codex-drawer .cx-take:hover, #codex-drawer .cx-take:focus-visible { transform: none; }
}
