/* ============================================================================
   STARK v5 — "Chalkboard"

   DIRECTION
   v3/v4 dressed a YouTube-playlist tracker as an illuminated manuscript:
   Cinzel small-caps, EB Garamond, "ornamental dividers", brass on navy. The
   ornament did no work. This system throws the costume out and rebuilds the
   site as an instrument for one job — telling you where you are and what's
   next.

   RULES OF THE SYSTEM
   1. Colour means state, never decoration.
        chalk yellow  = done
        chalk blue    = you are here
        chalk pink    = destructive
      Everything else is board and chalk. If a thing has colour, it is
      reporting something.
   2. The rule that fills. Every row's underline IS its progress bar. The
      divider carries the data. There is no separate "progress bar" widget
      and no decorative divider anywhere in this file.
   3. Numbers are monospace, always. Position and sequence are the subject,
      so they get their own typeface and never reflow.
   4. One animated moment: a rule redrawing when progress changes. No
      staggered page-load reveals (v3 had them on nearly every element).
   ============================================================================ */

/* ==========================================================================
   §1  TOKENS
   ========================================================================== */
:root {
  /* -- ground: dark slate-green, the board -- */
  --board:      #171D1A;
  --board-sunk: #131816;   /* wells, inputs, insets */
  --board-lift: #1E2622;   /* raised panels, hover */
  --board-edge: #29332E;   /* solid borders */

  /* -- chalk: the only "ink" -- */
  --chalk:      #E9EAE4;
  --chalk-dim:  rgba(233,234,228,.68);
  --chalk-mute: rgba(233,234,228,.44);
  --rule:       rgba(233,234,228,.13);
  --rule-firm:  rgba(233,234,228,.22);

  /* -- state colours. Each has exactly one meaning. -- */
  --mark:       #E3C26D;   /* done / complete */
  --mark-soft:  rgba(227,194,109,.13);
  --live:       #7FB3C4;   /* current position */
  --live-soft:  rgba(127,179,196,.14);
  --strike:     #D08C86;   /* destructive only */

  /* -- type -- */
  --display: 'Bricolage Grotesque', 'Instrument Sans', system-ui, sans-serif;
  --body:    'Instrument Sans', system-ui, -apple-system, sans-serif;
  --mono:    'DM Mono', ui-monospace, 'SF Mono', monospace;

  --t-hero: clamp(2.3rem, 6vw, 4.25rem);
  --t-xl:   clamp(1.6rem, 3.2vw, 2.25rem);
  --t-lg:   1.3125rem;
  --t-md:   1rem;
  --t-sm:   .875rem;
  --t-xs:   .8125rem;
  --t-mono: .75rem;

  /* -- rhythm: 4px base -- */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 72px;

  /* -- geometry: near-square. An instrument, not a pill. -- */
  --r: 5px;
  --r-lg: 8px;

  --shell: 1280px;
  --rail: 268px;
}

/* ==========================================================================
   §2  RESET + BASE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--board);
  color: var(--chalk);
  font-family: var(--body);
  font-size: var(--t-md);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
input, textarea, select { font: inherit; color: inherit; }
img { display: block; max-width: 100%; }
iframe { border: 0; display: block; }

::selection { background: var(--mark); color: var(--board); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--board-edge); border-radius: 99px; }
::-webkit-scrollbar-thumb:hover { background: var(--rule-firm); }

:focus-visible {
  outline: 2px solid var(--live);
  outline-offset: 2px;
  border-radius: 3px;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--mark); color: var(--board);
  padding: var(--s3) var(--s5); font-weight: 600; border-radius: 0 0 var(--r) 0;
}
.skip:focus { left: 0; }

.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ==========================================================================
   §3  TYPE ROLES
   ========================================================================== */
.display {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.02;
  font-variation-settings: 'opsz' 72;
}
.t-hero { font-size: var(--t-hero); }
.t-xl   { font-size: var(--t-xl); }
.t-lg   { font-size: var(--t-lg); font-weight: 600; letter-spacing: -.015em; }
.t-md   { font-size: var(--t-md); }
.t-sm   { font-size: var(--t-sm); }
.t-xs   { font-size: var(--t-xs); }

.num {
  font-family: var(--mono);
  font-size: var(--t-mono);
  font-weight: 400;
  letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
}
.num-lg { font-family: var(--mono); font-size: 1.5rem; font-weight: 500; letter-spacing: -.01em; line-height: 1; }

/* The eyebrow is mono, not small-caps serif — it labels data, so it reads
   as data. */
.label {
  font-family: var(--mono);
  font-size: var(--t-mono);
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--chalk-mute);
}
.dim  { color: var(--chalk-dim); }
.mute { color: var(--chalk-mute); }
.on-mark { color: var(--mark); }
.on-live { color: var(--live); }

.prose { max-width: 62ch; color: var(--chalk-dim); line-height: 1.65; }

/* ==========================================================================
   §4  SIGNATURE — THE RULE THAT FILLS
   The only progress affordance in the system. It is the bottom border of a
   row, so structure and data are the same object.
   ========================================================================== */
.ruled {
  position: relative;
  border-bottom: 2px solid var(--rule);
}
.ruled > .fill {
  position: absolute;
  left: 0; bottom: -2px;
  height: 2px;
  background: var(--mark);
  width: 0;
  transition: width .5s cubic-bezier(.2,.8,.3,1);
  pointer-events: none;
}
.ruled.is-live > .fill { background: var(--live); }
/* A standalone measure, for places with no row to underline (page headers). */
.measure { position: relative; height: 2px; background: var(--rule); border-radius: 2px; overflow: hidden; }
.measure > .fill { position: absolute; inset: 0 auto 0 0; background: var(--mark); width: 0; transition: width .5s cubic-bezier(.2,.8,.3,1); }

@media (prefers-reduced-motion: reduce) {
  .ruled > .fill, .measure > .fill { transition: none; }
  * { animation-duration: .01ms !important; animation-iteration-count: 1 !important; }
}

/* ==========================================================================
   §5  APP SHELL — top bar (desktop) / bottom bar (mobile)
   v4 ran five navigation systems at once. This is two: one global bar, and
   on the Learning page one contextual outline.
   ========================================================================== */
.topbar {
  position: sticky; top: 0; z-index: 60;
  background: rgba(23,29,26,.86);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--rule);
}
.topbar-in {
  max-width: var(--shell); margin: 0 auto;
  padding: var(--s3) var(--s4);
  display: flex; align-items: center; gap: var(--s5);
}
@media (min-width: 900px) { .topbar-in { padding: var(--s3) var(--s6); } }

.brand { display: flex; align-items: baseline; gap: .5rem; flex: none; }
.brand-mark {
  font-family: var(--display); font-weight: 800; font-size: 1.0625rem;
  letter-spacing: .02em; color: var(--chalk);
}
.brand-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--mark); flex: none; align-self: center; }

.topnav { display: none; gap: var(--s1); margin-left: var(--s4); }
@media (min-width: 720px) { .topnav { display: flex; } }
.topnav a {
  padding: 7px var(--s3); border-radius: var(--r);
  font-size: var(--t-sm); font-weight: 500; color: var(--chalk-mute);
  transition: color .14s, background .14s;
}
.topnav a:hover { color: var(--chalk); background: var(--board-lift); }
.topnav a[aria-current="page"] { color: var(--chalk); background: var(--board-lift); }

.topbar-end { margin-left: auto; display: flex; align-items: center; gap: var(--s3); }

.botnav {
  position: fixed; inset: auto 0 0 0; z-index: 60;
  display: flex; background: rgba(23,29,26,.94); backdrop-filter: blur(14px);
  border-top: 1px solid var(--rule);
  padding-bottom: env(safe-area-inset-bottom);
}
@media (min-width: 720px) { .botnav { display: none; } }
.botnav a {
  flex: 1; min-height: 54px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  font-size: .6875rem; font-weight: 500; color: var(--chalk-mute);
}
.botnav a[aria-current="page"] { color: var(--mark); }

.shell { max-width: var(--shell); margin: 0 auto; padding: var(--s5) var(--s4) var(--s8); }
@media (min-width: 900px) { .shell { padding: var(--s6) var(--s6) var(--s8); } }
@media (max-width: 719px) { .shell { padding-bottom: 96px; } }

/* ==========================================================================
   §6  PAGE HEAD
   ========================================================================== */
.head { margin-bottom: var(--s6); }
.head-top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; }
.head h1 { font-family: var(--display); font-weight: 700; letter-spacing: -.03em; line-height: 1.03; }
.head .label { display: block; margin-bottom: var(--s2); }

/* ==========================================================================
   §7  LEDGER ROWS — the primary content object
   Replaces v4's 4:3 art cards. A row shows more in less space, and its rule
   reports progress without a second element.
   ========================================================================== */
.ledger { display: flex; flex-direction: column; }

.row {
  display: grid;
  grid-template-columns: 2.25rem 1fr auto;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  padding: var(--s4) var(--s2);
  text-align: left;
  position: relative;
  border-bottom: 2px solid var(--rule);
  transition: background .14s;
}
.row:hover { background: var(--board-lift); }
.row > .fill { position: absolute; left: 0; bottom: -2px; height: 2px; background: var(--mark); width: 0; transition: width .5s cubic-bezier(.2,.8,.3,1); }
.row:last-child { border-bottom-color: transparent; }

.row.no-idx { grid-template-columns: 1fr auto; }
.row-dot { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.row-idx {
  font-family: var(--mono); font-size: var(--t-mono);
  color: var(--chalk-mute); font-variant-numeric: tabular-nums;
  display: flex; align-items: center; justify-content: center;
  width: 2.25rem; height: 2.25rem;
  border: 1px solid var(--rule); border-radius: var(--r);
  flex: none; transition: all .18s;
}
.row-body { min-width: 0; }
.row-title { font-size: var(--t-md); font-weight: 500; letter-spacing: -.01em; }
.row-meta { font-family: var(--mono); font-size: var(--t-mono); color: var(--chalk-mute); margin-top: 3px; letter-spacing: .03em; }
.row-end { display: flex; align-items: center; gap: var(--s3); flex: none; color: var(--chalk-mute); }
.row-pct { font-family: var(--mono); font-size: var(--t-mono); font-variant-numeric: tabular-nums; }

/* state: complete */
.row.is-done .row-idx { color: var(--mark); border-color: var(--mark); background: var(--mark-soft); }
.row.is-done .row-title { color: var(--chalk-dim); }
.row.is-done .row-pct { color: var(--mark); }

/* state: you are here */
.row.is-live { background: var(--live-soft); }
.row.is-live > .fill { background: var(--live); }
.row.is-live .row-idx { color: var(--live); border-color: var(--live); }
.row.is-live .row-title { color: var(--chalk); font-weight: 600; }

/* Depth in the outline. Indentation is the only hierarchy signal needed. */
.row.d1 { padding-left: var(--s5); }
.row.d2 { padding-left: var(--s7); }
.row.d3 { padding-left: 64px; }
.row.d1 .row-title, .row.d2 .row-title { font-size: var(--t-sm); }
.row.d3 .row-title { font-size: var(--t-sm); font-weight: 400; }

.row-caret { transition: transform .18s; }
.row[aria-expanded="true"] .row-caret { transform: rotate(90deg); }

/* ==========================================================================
   §8  PANELS, WELLS, MEDIA
   ========================================================================== */
.panel { background: var(--board-lift); border: 1px solid var(--rule); border-radius: var(--r-lg); }
.panel-pad { padding: var(--s5); }
.well { background: var(--board-sunk); border: 1px solid var(--rule); border-radius: var(--r); }

.screen {
  aspect-ratio: 16/9; background: #0B0E0D;
  border: 1px solid var(--board-edge); border-radius: var(--r-lg);
  overflow: hidden; position: relative;
}
.screen iframe { width: 100%; height: 100%; }
.screen-empty { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--s2); color: var(--chalk-mute); }

/* ==========================================================================
   §9  CONTROLS
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 42px; padding: 0 var(--s4);
  border-radius: var(--r); font-size: var(--t-sm); font-weight: 600;
  letter-spacing: -.005em; white-space: nowrap;
  transition: background .14s, border-color .14s, color .14s, transform .08s;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--mark); color: var(--board); }
.btn-primary:hover { background: #EDCE7E; }
.btn-ghost { border: 1px solid var(--rule-firm); color: var(--chalk); }
.btn-ghost:hover { border-color: var(--chalk-mute); background: var(--board-lift); }
.btn-quiet { color: var(--chalk-dim); }
.btn-quiet:hover { color: var(--chalk); background: var(--board-lift); }
.btn-strike { background: var(--strike); color: var(--board); }
.btn-strike:hover { filter: brightness(1.08); }
.btn-done { background: var(--mark-soft); color: var(--mark); border: 1px solid var(--mark); }
.btn-icon { width: 42px; padding: 0; flex: none; }
.btn-lg { min-height: 50px; padding: 0 var(--s5); font-size: var(--t-md); }
.btn[disabled] { opacity: .35; pointer-events: none; }

.tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .04em;
  padding: 3px 9px; border-radius: 3px;
  border: 1px solid var(--rule-firm); color: var(--chalk-mute);
}
.tag-mark { border-color: var(--mark); color: var(--mark); background: var(--mark-soft); }
.tag-live { border-color: var(--live); color: var(--live); background: var(--live-soft); }

.chipbar { display: flex; gap: var(--s2); flex-wrap: wrap; }
.chip {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .04em;
  padding: 7px var(--s3); border-radius: var(--r);
  border: 1px solid var(--rule); color: var(--chalk-mute);
  transition: all .14s; min-height: 34px;
}
.chip:hover { border-color: var(--chalk-mute); color: var(--chalk); }
.chip[aria-pressed="true"] { background: var(--chalk); border-color: var(--chalk); color: var(--board); }

/* fields */
.field { display: block; margin-bottom: var(--s4); }
.field > .label { display: block; margin-bottom: 6px; }
.input {
  width: 100%; background: var(--board-sunk);
  border: 1px solid var(--rule-firm); border-radius: var(--r);
  padding: 11px var(--s3); font-size: var(--t-sm);
  transition: border-color .14s, box-shadow .14s;
}
.input:focus { outline: none; border-color: var(--live); box-shadow: 0 0 0 3px var(--live-soft); }
.input::placeholder { color: var(--chalk-mute); }
textarea.input { resize: vertical; min-height: 76px; line-height: 1.5; }

.search { position: relative; }
.search .input { padding-left: 38px; }
.search-ico { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: var(--chalk-mute); pointer-events: none; }

.swatches { display: flex; gap: var(--s2); flex-wrap: wrap; }
.swatch { width: 30px; height: 30px; border-radius: var(--r); border: 1px solid var(--rule); }
.swatch[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--board), 0 0 0 4px var(--chalk); }

/* ==========================================================================
   §10  ICONS
   ========================================================================== */
/* Icons are ligature glyphs, so if the icon font fails to load the span's
   text content ("check", "account_balance") renders as a literal word — the
   exact failure the audit flagged in v3. They stay hidden until a probe in
   app.js confirms the font is actually active. Width is reserved either way,
   so nothing shifts. Every .i is aria-hidden, so hiding costs nothing. */
.i {
  font-family: 'Material Symbols Outlined';
  font-weight: normal; font-style: normal; font-size: 20px; line-height: 1;
  display: inline-block; vertical-align: middle;
  width: 1em; height: 1em; overflow: hidden;
  font-feature-settings: 'liga'; -webkit-font-smoothing: antialiased;
  user-select: none; flex: none;
  visibility: hidden;
}
html.icons-ok .i { visibility: visible; }
.i-sm { font-size: 17px; }
.i-lg { font-size: 28px; }

/* ==========================================================================
   §11  HOME
   The hero is the resume state, not a brand splash. The most characteristic
   thing about this site is that it knows where you stopped.
   ========================================================================== */
.hero { padding: var(--s7) 0 var(--s6); border-bottom: 1px solid var(--rule); }
.hero-measure { margin-top: var(--s5); max-width: 520px; }
.hero-measure .measure { height: 3px; }
.hero-lede { margin-top: var(--s4); font-size: var(--t-md); }
@media (min-width: 900px) { .hero-lede { font-size: 1.0625rem; } }

.resume {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: var(--s4);
  width: 100%; text-align: left;
  margin-top: var(--s6); padding: var(--s4);
  background: var(--board-lift); border: 1px solid var(--rule-firm);
  border-left: 3px solid var(--live);
  border-radius: var(--r-lg);
  transition: border-color .16s, background .16s;
}
.resume:hover { background: var(--board-edge); border-left-color: var(--live); }
.resume-play {
  width: 46px; height: 46px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--live); color: var(--board);
}
.resume-body { min-width: 0; }

.figures { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; margin-top: var(--s6); background: var(--rule); border: 1px solid var(--rule); border-radius: var(--r-lg); overflow: hidden; }
@media (min-width: 620px) { .figures { grid-template-columns: repeat(4, 1fr); } }
.figure { background: var(--board); padding: var(--s4); }
.figure-cap { font-family: var(--mono); font-size: var(--t-mono); text-transform: uppercase; letter-spacing: .12em; color: var(--chalk-mute); margin-top: 6px; }

.halls { display: grid; gap: var(--s5); margin-top: var(--s7); }
@media (min-width: 860px) { .halls { grid-template-columns: 1fr 1fr; gap: var(--s6); } }
.hall { display: flex; flex-direction: column; padding: var(--s5); border: 1px solid var(--rule); border-radius: var(--r-lg); transition: border-color .16s, background .16s; }
.hall:hover { border-color: var(--rule-firm); background: var(--board-lift); }
.hall-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s3); }
.hall-list { margin: var(--s4) 0; flex: 1; }
.hall-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding-top: var(--s4); border-top: 1px solid var(--rule); }

/* ==========================================================================
   §12  LEARNING — outline rail + stage
   The outline REPLACES the card grid rather than duplicating it. That was
   the audit's redundancy finding: two nav surfaces showing identical data.
   ========================================================================== */
.work { display: grid; gap: var(--s5); align-items: start; }
/* On mobile the outline is an index, not the content — it goes after the
   stage. (v4 stacked its tree on top, pushing every lesson below the fold.) */
.work > .outline { order: 2; }
.work > .stage   { order: 1; }
@media (min-width: 1000px) {
  .work { grid-template-columns: var(--rail) 1fr; gap: var(--s6); }
  .work > .outline { order: 0; }
  .work > .stage   { order: 1; }
}

.outline { border: 1px solid var(--rule); border-radius: var(--r-lg); overflow: hidden; }
@media (min-width: 1000px) {
  .outline { position: sticky; top: 76px; max-height: calc(100vh - 100px); display: flex; flex-direction: column; }
  .outline-scroll { overflow-y: auto; flex: 1; }
}
.outline-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s2); padding: var(--s3) var(--s4); border-bottom: 1px solid var(--rule); background: var(--board-lift); }
.outline-scroll { padding: var(--s2); max-height: 340px; overflow-y: auto; }
@media (min-width: 1000px) { .outline-scroll { max-height: none; } }

/* Outline rows are tighter than ledger rows — they are an index, not content. */
.onode {
  display: grid; grid-template-columns: 18px 1fr auto; align-items: center; gap: var(--s2);
  width: 100%; text-align: left; padding: 7px var(--s2);
  border-radius: var(--r); font-size: var(--t-xs); color: var(--chalk-dim);
  transition: background .13s, color .13s;
}
.onode:hover { background: var(--board-lift); color: var(--chalk); }
.onode-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.onode.is-open { color: var(--chalk); font-weight: 600; }
.onode.is-live { background: var(--live-soft); color: var(--live); font-weight: 600; }
.onode.is-done .onode-ico { color: var(--mark); }
.onode-ico { transition: transform .18s; }
.onode.is-open > .onode-ico.caret { transform: rotate(90deg); }
.okids { margin-left: 9px; padding-left: 9px; border-left: 1px solid var(--rule); }
.oadd { display: flex; align-items: center; gap: 6px; width: 100%; padding: 6px var(--s2); font-family: var(--mono); font-size: var(--t-mono); color: var(--chalk-mute); border-radius: var(--r); }
.oadd:hover { background: var(--board-lift); color: var(--mark); }

.stage { min-width: 0; }

/* player */
.player-grid { display: grid; gap: var(--s5); align-items: start; }
@media (min-width: 1100px) { .player-grid { grid-template-columns: minmax(0,1fr) 300px; } }
.player-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap;
  margin-top: var(--s4); padding-top: var(--s4);
  border-top: 1px solid var(--rule);
}
.notes { font-size: var(--t-sm); line-height: 1.7; color: var(--chalk-dim); white-space: pre-wrap; }
@media (min-width: 1100px) { .notes-panel { max-height: 62vh; overflow-y: auto; } }

.keys { font-family: var(--mono); font-size: var(--t-mono); color: var(--chalk-mute); letter-spacing: .03em; }
/* Keyboard hints are noise on a touch screen. */
@media (max-width: 719px) { .keys { display: none; } }
.keys kbd { border: 1px solid var(--rule-firm); border-radius: 3px; padding: 1px 5px; margin: 0 1px; }

/* ==========================================================================
   §13  LIBRARY — shelves
   Covers are real content, so they stay. Everything around them gets quiet.
   ========================================================================== */
.shelf { margin-bottom: var(--s7); }
.shelf-head { display: flex; align-items: baseline; gap: var(--s3); padding-bottom: var(--s3); margin-bottom: var(--s4); border-bottom: 2px solid var(--rule); position: relative; }
.shelf-head > .fill { position: absolute; left: 0; bottom: -2px; height: 2px; background: var(--mark); width: 0; transition: width .5s cubic-bezier(.2,.8,.3,1); }
.shelf-dot { width: 7px; height: 7px; border-radius: 2px; flex: none; }

.books { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: var(--s4); }
.book { text-align: left; width: 100%; transition: transform .18s; }
.book:hover { transform: translateY(-4px); }
.cover {
  aspect-ratio: 2/3; border-radius: var(--r); overflow: hidden; position: relative;
  background: var(--board-sunk); border: 1px solid var(--board-edge);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 16px -8px rgba(0,0,0,.7);
  transition: border-color .18s, box-shadow .18s;
}
.book:hover .cover { border-color: var(--rule-firm); box-shadow: 0 12px 26px -10px rgba(0,0,0,.8); }
.cover img { width: 100%; height: 100%; object-fit: cover; }
.cover-letter { font-family: var(--display); font-size: 2rem; font-weight: 700; color: var(--chalk-mute); }
.cover-edge { position: absolute; left: 0; top: 0; bottom: 0; width: 4px; }
.cover-state { position: absolute; top: 6px; right: 6px; width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(11,14,13,.86); }
.book-title { font-size: var(--t-xs); font-weight: 500; margin-top: var(--s2); line-height: 1.35; }
.book-author { font-family: var(--mono); font-size: var(--t-mono); color: var(--chalk-mute); margin-top: 2px; }

/* ==========================================================================
   §14  MODAL / TOAST / EMPTY
   ========================================================================== */
.veil {
  position: fixed; inset: 0; z-index: 120;
  background: rgba(8,11,10,.72); backdrop-filter: blur(3px);
  display: none; align-items: center; justify-content: center; padding: var(--s4);
}
.veil.open { display: flex; }
.sheet {
  width: 100%; max-width: 460px; max-height: 88vh; overflow-y: auto;
  background: var(--board-lift); border: 1px solid var(--board-edge);
  border-radius: var(--r-lg); padding: var(--s5);
  box-shadow: 0 24px 60px -20px rgba(0,0,0,.9);
}
.sheet-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s5); padding-bottom: var(--s3); border-bottom: 1px solid var(--rule); }
.sheet-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); margin-top: var(--s5); }

.toast {
  position: fixed; left: 50%; bottom: var(--s5); z-index: 200;
  transform: translate(-50%, 12px); opacity: 0; pointer-events: none;
  display: flex; align-items: center; gap: var(--s2);
  background: var(--chalk); color: var(--board);
  padding: 10px var(--s4); border-radius: var(--r);
  font-size: var(--t-sm); font-weight: 600;
  transition: opacity .2s, transform .2s;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
@media (max-width: 719px) { .toast { bottom: 74px; } }

.empty { padding: var(--s7) var(--s5); text-align: center; border: 1px dashed var(--rule-firm); border-radius: var(--r-lg); }
.empty .i { color: var(--chalk-mute); }
.empty p { margin-top: var(--s3); color: var(--chalk-dim); font-size: var(--t-sm); }

/* utility (deliberately tiny — layout lives in named components) */
.stack > * + * { margin-top: var(--s4); }
.stack-sm > * + * { margin-top: var(--s2); }
.cluster { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.between { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.grow { flex: 1; min-width: 0; }
.hide { display: none !important; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.clamp2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
