/* =========================================================================
   bstar — design system: "The Press".

   The app makes books, so the interface borrows from the people who made
   them by hand: a mid-century publishing house. Cream stock, warm ink, a
   small set of print colours laid down flat, and the conventions of the
   printed page used as interface -- a contents page, running heads, plates,
   a colophon. Retro through *craft*, not through novelty fonts and neon.

   Hand-written rather than generated. The app is about a dozen screens; a
   utility framework would add a Node build stage to the Dockerfile and a
   compile step between editing a template and seeing the result, in exchange
   for nothing this file does not already do.

   Four rules hold the whole thing together:

   1. **Everything comes from a token.** No literal colour, radius or shadow
      below the token block. Retheming means editing one section. (The
      token block must stay the first rule in the file: the test that
      polices this treats everything after the first closing brace as
      "below the tokens".)
   2. **Spacing comes from the scale.** `--s-1` ... `--s-10`. If a value is
      not on the scale it is wrong, and arbitrary values like `.85rem` are
      what make a layout feel improvised.
   3. **Ink means "you can press this".** Buttons, a selected choice and the
      dialog carry a solid ink outline and a hard offset shadow, like a
      letterpress block. Sheets that merely *hold* content get a hairline
      and a soft tan offset instead. If everything is inked, nothing is.
   4. **Never a box in a box.** Inside a card, structure comes from rules,
      space and type -- the way a printed page does it.

   One theme, on purpose, and it is paper. A second one doubles the surface
   area and halves the attention each gets -- and "a book at night" is what
   the reader's own device settings are for.
   ========================================================================= */

:root {
  color-scheme: dark;

  /* --- stock -----------------------------------------------------------
     The page is uncoated cream; cards are a brighter sheet laid on top of
     it; wells (code, recessed panels) are a shade darker. Three steps,
     close together, the way three papers from one mill would be. */
  --paper: #0c0810;
  --paper-deep: #07050a;
  --paper-edge: #1d1424;
  --sheet: #130d18;
  --sheet-2: #1a1121;
  --field: #0f0a13;

  /* Hairlines. Tan rather than grey: grey on cream reads as dirt. */
  --rule: #2b1f33;
  --rule-2: #433050;
  /* The soft offset under a sheet -- a shadow printed, not rendered. */
  --shade: rgb(0 0 0 / 55%);

  /* --- ink -------------------------------------------------------------
     Warm near-black, never pure: pure black on cream is harsh in a way
     real ink never is. --ink-3 is the lightest *text* colour (5:1 on the
     paper); --ink-4 is for ornaments and rules only. */
  --ink: #f2ecf4;
  --ink-2: #d2c6d8;
  --ink-3: #a898b0;
  --ink-4: #6e5e78;
  --on-ink: #0c0810;

  /* --- print colours ---------------------------------------------------
     A four-colour job: orange, teal, mustard, navy. The accent is the
     orange, deepened until cream type on it passes AA (5.2:1). */
  --accent: #a51cff;
  --accent-2: #bc4dff;   /* hover, and the bright orange in artwork */
  --accent-deep: #cf8aff;   /* link text: 6:1 on the paper */
  --on-accent: #ffffff;
  --accent-wash: rgb(165 28 255 / 12%);
  --accent-edge: rgb(165 28 255 / 55%);

  --teal: #3d0f63;
  --teal-2: #57198a;
  --mustard: #ff3d6e;
  --mustard-2: #ff8fb8;
  --navy: #2a0c45;
  --plum: #e8174a;
  --olive: #4a0d2c;
  --blush: #ff6fb5;
  /* A story's cover colour and the colour of type on it. Overridden per
     book on the shelf; these are the defaults for the first in a run. */
  --cover:       var(--accent);
  --cover-ink:   var(--on-ink);

  /* --- status ----------------------------------------------------------
     Distinct from the accent on purpose: an error must never be mistaken
     for the button that fixes it. */
  --ok: #4cc38a;
  --ok-wash: rgb(76 195 138 / 12%);
  --ok-edge: rgb(76 195 138 / 40%);

  --warn: #f0b44c;
  --warn-wash: rgb(240 180 76 / 12%);
  --warn-edge: rgb(240 180 76 / 40%);

  --bad: #ff5a6a;
  --bad-wash: rgb(255 90 106 / 10%);
  --bad-edge: rgb(255 90 106 / 40%);

  --info-wash: rgb(194 24 107 / 10%);
  --info-edge: rgb(194 24 107 / 38%);

  /* --- texture ---------------------------------------------------------
     Paper grain: SVG noise, tiled, so the page reads as stock and not as
     a flat fill. Brown speckle at a few per cent; any stronger and it is
     a filter effect rather than a material. */
  --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 -1 .06'/></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>");
  /* Halftone, for the printed-colour look on covers and artwork. */
  --halftone: none;
  --halftone-light: none;
  /* The darkened fold on a book cover's spine. */
  --spine-shade: linear-gradient(90deg, rgb(0 0 0 / 22%), rgb(0 0 0 / 0%) 14%);
  --scrim: rgb(4 3 6 / 78%);

  /* --- spacing scale ---------------------------------------------------- */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 20px;
  --s-6: 24px;
  --s-7: 32px;
  --s-8: 40px;
  --s-9: 56px;
  --s-10: 80px;

  /* --- shape ------------------------------------------------------------
     Nearly square. Big radii are a 2010s screen idiom; a trimmed page
     has corners. */
  --r-1: 0px;
  --r-2: 2px;
  --r-3: 2px;
  --r-4: 3px;
  --r-full: 999px;

  --bw: 1px;   /* the ink rule: buttons, fields, anything you touch */

  /* Hard offsets. Ink for things you press; tan for sheets. */
  --press-1: none;
  --press-2: none;
  --press-3: 0 0 0 1px var(--rule-2);
  --sheet-lift: none;
  /* Letterpress bite into a field: the impression, not a glow. */
  --deboss: inset 0 1px 2px rgb(0 0 0 / 50%);

  /* --- type -------------------------------------------------------------
     Self-hosted (the CSP allows same-origin fonts only), all SIL OFL:
     Fraunces -- a 1970s "soft serif" with optical sizes, for display and
     for the book itself; Work Sans -- drawn from early grotesques, for
     the interface; IBM Plex Mono -- typewriter voice for labels, prices
     and anything that is a figure rather than a word. */
  --display: "Big Shoulders Display", "Arial Narrow", Impact, sans-serif;
  --serif: "Archivo", ui-sans-serif, system-ui, sans-serif;
  --sans: "Archivo", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;

  /* Fraunces' SOFT axis rounds the terminals toward the phototype faces
     of the period; WONK adds its leaning "h" and "n". Soft everywhere,
     wonk only at display sizes where it reads as character, not error. */
  --soft: "SOFT" 0, "WONK" 0;
  --soft-wonk: "SOFT" 0, "WONK" 0;

  --t-xs:   0.75rem;
  --t-sm:   0.8125rem;
  --t-base: 0.9375rem;
  --t-md:   1.0625rem;
  --t-lg:   1.3125rem;
  --t-xl:   1.75rem;
  --t-2xl:  2.375rem;
  --t-3xl:  3.5rem;    /* landing hero only */

  --measure: 68ch;
  --measure-tight: 58ch;

  --shell: 1120px;
  --shell-wide: 1320px;
  --shell-narrow: 580px;

  --ease: cubic-bezier(0.3, 0.7, 0.4, 1);
  --fast: 110ms var(--ease);

  /* --- noir additions ----------------------------------------------------
     The after-dark edition: crimson into magenta, a soft bloom on what you
     can press, and a blur that keeps covers veiled until a visitor has
     confirmed they are an adult. */
  --magenta: #ff4fa3;
  --crimson: #e8174a;
  --grad-accent: #a51cff;
  --grad-page: none;
  --glow: 0 0 0 1px var(--accent);
  --glow-soft: none;
  --veil:        blur(22px) saturate(0.6) brightness(0.7);
  --glass: #0c0810;

  /* --- v2 "Nocturne" ------------------------------------------------------
     Neon purple is the only loud colour and it is used flat: one solid
     fill for the primary action, a rule, a current tab. Dark purple, red
     and pink are supporting inks for status, covers and small marks. No
     gradients, no glows, no glass. */
  --neon: #b026ff;
  --neon-ink: #d9a8ff;
  --plum-deep: #2a0b42;
  --red: #ff2d4a;
  --pink: #ff5fae;
  --body-serif: "Archivo", ui-sans-serif, system-ui, sans-serif;
  /* reader v4 surfaces (light page, panels) */
  --rd-c-0a0610: #0a0610;
  --rd-c-110919: #110919;
  --rd-c-120a1a: #120a1a;
  --rd-c-1d0a22: #1d0a22;
  --rd-c-1d1420: #1d1420;
  --rd-c-2a0a1a: #2a0a1a;
  --rd-c-2a0c45: #2a0c45;
  --rd-c-4a3d4f: #4a3d4f;
  --rd-c-6f6274: #6f6274;
  --rd-c-8b1fd6: #8b1fd6;
  --rd-c-a01570: #a01570;
  --rd-c-b8acbb: #b8acbb;
  --rd-c-d48bff: #d48bff;
  --rd-c-d9cfc4: #d9cfc4;
  --rd-c-f6f0e8: #f6f0e8;
  --rd-c-fff: #fff;
}

/* ============================================================== fonts == */

@font-face {
  font-family: "Big Shoulders Display";
  src: url("fonts/big-shoulders-display-latin-wght-normal.woff2") format("woff2-variations"),
       url("fonts/big-shoulders-display-latin-wght-normal.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Archivo";
  src: url("fonts/archivo-latin-wght-normal.woff2") format("woff2-variations"),
       url("fonts/archivo-latin-wght-normal.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Archivo";
  src: url("fonts/archivo-latin-wght-italic.woff2") format("woff2-variations"),
       url("fonts/archivo-latin-wght-italic.woff2") format("woff2");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/ibm-plex-mono-latin-600-normal.woff2") format("woff2");
  font-weight: 600 700;
  font-style: normal;
  font-display: swap;
}

/* ============================================================== reset == */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-color: var(--rule-2) var(--paper);
}

body {
  margin: 0;
  min-height: 100dvh;
  background-color: var(--paper);
  background-image: var(--grain);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg, video { max-width: 100%; height: auto; display: block; }
/* The reset above would otherwise un-hide a `hidden` SVG sprite. */
[hidden] { display: none !important; }

::selection { background: var(--mustard-2); color: var(--ink); }

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

::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--paper); }
::-webkit-scrollbar-thumb {
  background: var(--rule-2);
  border-radius: var(--r-full);
  border: 3px solid var(--paper);
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-4); }

/* ========================================================= typography == */

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--display);
  font-variation-settings: var(--soft);
  font-optical-sizing: auto;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-wrap: balance;
}
h1 { font-size: var(--t-xl); font-weight: 650; }
h2 { font-size: var(--t-lg); }
h3 { font-size: var(--t-md); letter-spacing: 0; }
h4 { font-size: var(--t-base); letter-spacing: 0; }

p { margin: 0 0 var(--s-4); max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

a {
  color: var(--accent-deep);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: var(--accent-edge);
  transition: color var(--fast), text-decoration-color var(--fast);
}
a:hover { color: var(--accent); text-decoration-color: currentColor; }

strong { font-weight: 600; color: var(--ink); }
em { font-style: italic; }

code {
  font-family: var(--mono);
  font-size: 0.86em;
  padding: 0.08em 0.34em;
  border-radius: var(--r-1);
  background: var(--paper-deep);
  color: var(--ink);
}

hr {
  border: 0;
  height: 0;
  border-top: 1px solid var(--rule-2);
  margin: var(--s-7) 0;
}

.muted  { color: var(--ink-2); }
.faint  { color: var(--ink-3); }
.small  { font-size: var(--t-sm); line-height: 1.55; }
.xs     { font-size: var(--t-xs); }
/* Tabular figures everywhere a number can change: counts, prices, progress.
   Without it the layout twitches as digits swap width. */
.tnum   { font-variant-numeric: tabular-nums; }
.mono   { font-family: var(--mono); }
.nowrap { white-space: nowrap; }
.measure { max-width: var(--measure); }

/* A small typewritten label: section eyebrows, table heads, stamps. */
.overline {
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ============================================================== shell == */

.shell { position: relative; min-height: 100dvh; display: flex; flex-direction: column; }

/* A masthead, not an app bar: it scrolls away with the page like the head
   of a printed sheet. Sticky chrome would also have needed the reader's
   running head to know its height, which is how a magic `top: 53px` once
   ended up in this file. */
.site-header {
  position: relative;
  z-index: 40;
  border-bottom: 3px double var(--ink);
}
.site-header .inner {
  max-width: var(--shell-wide);
  margin: 0 auto;
  padding: var(--s-4) var(--s-6);
  display: flex;
  align-items: center;
  gap: var(--s-6);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--display);
  font-variation-settings: var(--soft-wonk);
  font-size: 1.375rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--ink);
  text-decoration: none;
  flex: 0 0 auto;
}
.brand:hover { color: var(--ink); }
.brand .mark {
  width: 38px;
  height: 38px;
  flex: 0 0 38px;
  transition: transform 300ms var(--ease);
}
.brand:hover .mark { transform: rotate(-8deg); }

.site-nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--s-1);
}
.site-nav .navlink {
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-2);
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-2);
  transition: background var(--fast), color var(--fast);
}
.site-nav .navlink:hover { background: var(--paper-deep); color: var(--ink); }
/* The current section is set in reverse, like a tab cut into a ledger. */
.site-nav .navlink[aria-current="page"] { background: var(--ink); color: var(--on-ink); }
.site-nav .divider {
  width: 1px;
  height: 22px;
  background: var(--rule-2);
  margin: 0 var(--s-2);
}

main {
  flex: 1;
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: var(--s-8) var(--s-6) var(--s-10);
}
main.narrow { max-width: var(--shell-narrow); }
main.wide   { max-width: var(--shell-wide); }
/* The reader: wide enough for the page and its mount, no wider. */
main.reading { max-width: 1260px; }

/* The colophon: the note at the back of a book that says how it was made. */
.site-footer {
  border-top: 1px solid var(--rule-2);
  padding: var(--s-7) var(--s-6) var(--s-8);
  text-align: center;
  color: var(--ink-3);
  font-size: var(--t-sm);
}
.site-footer .colophon-mark {
  width: 22px;
  height: 22px;
  margin: 0 auto var(--s-3);
  opacity: 0.85;
}
.site-footer .colophon-line {
  margin: var(--s-1) auto 0;
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.site-footer em { font-family: var(--serif); font-size: var(--t-base); color: var(--ink-2); }

/* --- page header ------------------------------------------------------- */

.page-head { margin-bottom: var(--s-7); }
/* The eyebrow is a running head: the book's title, set small, with a rule
   leading into it. */
.page-head .eyebrow {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--s-3);
}
.page-head .eyebrow::before {
  content: "";
  width: 28px;
  height: 0;
  border-top: 2px solid currentColor;
  flex: 0 0 28px;
}
.page-head.center .eyebrow { justify-content: center; }
.page-head.center .eyebrow::after {
  content: "";
  width: 28px;
  height: 0;
  border-top: 2px solid currentColor;
  flex: 0 0 28px;
}
.page-head .eyebrow a { color: inherit; text-decoration: none; }
.page-head .eyebrow a:hover { color: var(--accent-deep); text-decoration: underline; }
.page-head h1 { font-size: var(--t-2xl); letter-spacing: -0.02em; }
.page-head .lede {
  margin: var(--s-3) 0 0;
  color: var(--ink-2);
  font-size: var(--t-md);
  line-height: 1.6;
  max-width: var(--measure);
}
.page-head.center .lede { margin-left: auto; margin-right: auto; }

/* ========================================================== utilities == */

.stack    > * + * { margin-top: var(--s-4); }
.stack-xs > * + * { margin-top: var(--s-2); }
.stack-sm > * + * { margin-top: var(--s-3); }
.stack-lg > * + * { margin-top: var(--s-6); }
.stack-xl > * + * { margin-top: var(--s-7); }

.row { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.row.tight { gap: var(--s-2); }
.row.top { align-items: flex-start; }
.row.between { justify-content: space-between; }
.row.end { justify-content: flex-end; }
.row.nowrap { flex-wrap: nowrap; }
.spacer { flex: 1 1 auto; }

.grid { display: grid; gap: var(--s-5); }
/* Two columns means two: at 320px the shell fitted three, which squeezed
   every settings heading onto two lines. */
.grid.cols-2 { grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.grid.gap-lg { gap: var(--s-6); }

/* Main column beside a narrower rail, collapsing when there is no room. */
.grid.split {
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: var(--s-6);
  align-items: start;
}
@media (max-width: 1000px) { .grid.split { grid-template-columns: 1fr; } }

.grow { flex: 1 1 auto; min-width: 0; }
.fixed-sm { flex: 0 0 8rem; }
.fixed-md { flex: 0 0 11rem; }
.min-10 { min-width: 10rem; }
.min-12 { min-width: 12rem; }

.mt-0 { margin-top: 0 !important; }
.mb-0 { margin-bottom: 0 !important; }
.mt-4 { margin-top: var(--s-4); }
.mt-6 { margin-top: var(--s-6); }
.mb-4 { margin-bottom: var(--s-4); }
.mb-6 { margin-bottom: var(--s-6); }

.center { text-align: center; }
.break-all { word-break: break-all; }

/* ============================================================== cards == */

/* A sheet laid on the page: brighter stock, a hairline, and a flat tan
   offset where a real sheet would throw its shadow. */
.card {
  background: var(--sheet);
  border: 1px solid var(--rule-2);
  border-radius: var(--r-2);
  padding: var(--s-6);
  box-shadow: var(--sheet-lift);
}
.card.flush { padding: 0; overflow: hidden; }
.card.tight { padding: var(--s-4); }
.card.raised { background: var(--field); }

/* A card the whole of which is a link. */
a.card {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: transform var(--fast), box-shadow var(--fast), border-color var(--fast);
}
a.card:hover {
  color: inherit;
  border-color: var(--ink);
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 var(--shade);
}

/* The head of a card is set like a section opening: title, a rule. */
.card-head {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--rule);
}
.card-head h2, .card-head h3 { margin: 0; }
.card-head .spacer { flex: 1; }
.card-head label { margin: 0; }

/* A run of rows inside a flush card. */
.rows > * { padding: var(--s-4) var(--s-6); border-top: 1px solid var(--rule); }
.rows > *:first-child { border-top: 0; }

/* ============================================================== forms == */

label {
  display: block;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--ink);
  margin-bottom: var(--s-2);
}
label .hint { font-weight: 400; color: var(--ink-3); }

input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="search"], input[type="url"],
input[type="tel"], input[type="date"], select, textarea {
  width: 100%;
  padding: 0.6rem 0.75rem;
  font: inherit;
  font-size: var(--t-base);
  color: var(--ink);
  background: var(--field);
  border: var(--bw) solid var(--ink-2);
  border-radius: var(--r-2);
  box-shadow: var(--deboss);
  transition: border-color var(--fast), box-shadow var(--fast), background var(--fast);
}

/* Every text-like control, so a new `type` cannot silently fall back to the
   browser's default chrome the way `url` once did. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):hover,
select:hover, textarea:hover { border-color: var(--ink); }

/* Focus is a hard orange offset -- the same language as a pressed button,
   so "this is where your typing goes" looks like part of the set. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):focus,
select:focus, textarea:focus {
  outline: none;
  border-color: var(--ink);
  background: var(--sheet);
  box-shadow: 3px 3px 0 var(--accent-2);
}

input::placeholder, textarea::placeholder { color: var(--ink-3); opacity: 0.8; }

select {
  appearance: none;
  padding-right: 2.25rem;
  /* Chevron drawn as a background gradient -- no icon font, no asset. */
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink) 50%),
    linear-gradient(135deg, var(--ink) 50%, transparent 50%);
  background-position: right 1.05rem center, right 0.75rem center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

textarea {
  resize: vertical;
  min-height: 7rem;
  line-height: 1.65;
}
textarea.code {
  font-family: var(--mono);
  font-size: var(--t-sm);
  min-height: 22rem;
  tab-size: 2;
  line-height: 1.7;
}

input[type="file"] {
  width: 100%;
  font-size: var(--t-sm);
  padding: var(--s-5) var(--s-4);
  color: var(--ink-2);
  background: var(--paper);
  border: var(--bw) dashed var(--ink-3);
  border-radius: var(--r-2);
  cursor: pointer;
  transition: border-color var(--fast), background var(--fast);
}
input[type="file"]:hover { border-color: var(--ink); background: var(--sheet-2); }
input[type="file"]::file-selector-button {
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  margin-right: var(--s-3);
  padding: 0.4rem 0.8rem;
  color: var(--ink);
  background: var(--sheet);
  border: var(--bw) solid var(--ink);
  border-radius: var(--r-2);
  box-shadow: var(--press-1);
  cursor: pointer;
}

/* The range control drawn as a slide rule: an inked track and a square
   knob, rather than the browser's glossy pill. */
input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 28px;
  background: transparent;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 8px;
  background: var(--paper-deep);
  border: var(--bw) solid var(--ink);
  border-radius: var(--r-full);
}
input[type="range"]::-moz-range-track {
  height: 8px;
  background: var(--paper-deep);
  border: var(--bw) solid var(--ink);
  border-radius: var(--r-full);
}
input[type="range"]::-moz-range-progress {
  height: 8px;
  background: var(--mustard);
  border-radius: var(--r-full);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 22px;
  height: 22px;
  margin-top: -9px;
  background: var(--accent);
  border: var(--bw) solid var(--ink);
  border-radius: var(--r-2);
  box-shadow: var(--press-1);
}
input[type="range"]::-moz-range-thumb {
  width: 20px;
  height: 20px;
  background: var(--accent);
  border: var(--bw) solid var(--ink);
  border-radius: var(--r-2);
  box-shadow: var(--press-1);
}
input[type="range"]:focus { outline: none; }
input[type="range"]:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--accent); outline-offset: 3px; }
input[type="range"]:focus-visible::-moz-range-thumb { outline: 2px solid var(--accent); outline-offset: 3px; }

.field + .field { margin-top: var(--s-4); }
.field-note {
  font-size: var(--t-sm);
  color: var(--ink-3);
  margin-top: var(--s-2);
  max-width: var(--measure-tight);
  line-height: 1.5;
}

.checkline { display: flex; align-items: center; gap: var(--s-2); }
.checkline label { margin: 0; font-weight: 500; color: var(--ink-2); }
input[type="checkbox"], input[type="radio"] {
  accent-color: var(--accent);
  width: 1.05rem;
  height: 1.05rem;
  cursor: pointer;
}

/* ============================================================ buttons == */

/* A button is a block of type locked in the chase: inked outline, hard
   shadow, and it goes *down* into the paper when pressed.

   Every variant speaks through the same five variables rather than
   overriding `box-shadow` and `transform` itself. Size and colour are
   separate classes that combine (`.btn.ghost.sm.danger`), and with
   property overrides whichever rule came last in the file won -- which is
   how a ghost button once grew an ink shadow just for being small. */
.btn {
  --btn-bg: var(--sheet);
  --btn-bg-hover: var(--paper);
  --btn-fg: var(--ink);
  --btn-line: var(--ink);
  --btn-shadow: var(--press-2);
  --btn-shadow-hover: 4px 4px 0 var(--ink);
  --btn-lift: translate(-1px, -1px);
  --btn-sink: translate(3px, 3px);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: 0.55rem 1rem;
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.01em;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  border-radius: var(--r-2);
  border: var(--bw) solid var(--btn-line);
  background: var(--btn-bg);
  color: var(--btn-fg);
  box-shadow: var(--btn-shadow);
  transition: transform var(--fast), box-shadow var(--fast), background var(--fast),
              color var(--fast), border-color var(--fast);
}
.btn:hover {
  color: var(--btn-fg);
  background: var(--btn-bg-hover);
  transform: var(--btn-lift);
  box-shadow: var(--btn-shadow-hover);
}
.btn:active {
  transform: var(--btn-sink);
  box-shadow: none;
}
.btn[disabled], .btn[aria-disabled="true"] {
  opacity: 0.45;
  pointer-events: none;
  box-shadow: none;
}

/* --- size: changes the shadow's reach, never whether there is one. --- */
.btn.sm {
  padding: 0.35rem 0.65rem;
  font-size: var(--t-xs);
  --btn-shadow: var(--press-1);
  --btn-shadow-hover: 3px 3px 0 var(--ink);
  --btn-sink: translate(2px, 2px);
}
.btn.lg { padding: 0.8rem 1.5rem; font-size: var(--t-base); }
.btn.block { width: 100%; }

/* --- colour ----------------------------------------------------------- */
.btn.primary {
  --btn-bg: var(--accent);
  --btn-bg-hover: var(--accent-2);
  --btn-fg: var(--on-accent);
}

.btn.danger {
  --btn-bg: transparent;
  --btn-bg-hover: var(--bad-wash);
  --btn-fg: var(--bad);
  --btn-line: var(--bad);
  --btn-shadow: 2px 2px 0 var(--bad-edge);
  --btn-shadow-hover: 3px 3px 0 var(--bad-edge);
}
/* The dialog's own OK turns solid red for destructive confirmations. */
.btn.danger.confirm-ok {
  --btn-bg: var(--bad);
  --btn-bg-hover: var(--bad);
  --btn-fg: var(--on-accent);
  --btn-line: var(--ink);
  --btn-shadow: var(--press-2);
  --btn-shadow-hover: 4px 4px 0 var(--ink);
}

/* Ghost last, so it wins over size and colour alike: a ghost is flat
   whatever else it is. A ghost danger is a quiet "Delete" in a row of
   actions -- red type only -- and is behind a confirmation either way. */
.btn.ghost {
  --btn-bg: transparent;
  --btn-bg-hover: var(--paper-deep);
  --btn-line: transparent;
  --btn-shadow: none;
  --btn-shadow-hover: none;
  --btn-lift: none;
  --btn-sink: translateY(1px);
}
.btn.ghost:not(.danger) { --btn-fg: var(--ink-2); }
.btn.ghost:not(.danger):hover { color: var(--ink); }
.btn.ghost.danger { --btn-bg-hover: var(--bad-wash); }

/* A form that exists only to wrap one button should not add layout. */
form.inline { display: inline-flex; }

/* ============================================================= badges == */

/* A rubber stamp: typewriter caps inside a ruled box. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: 0.12rem 0.45rem;
  border-radius: var(--r-1);
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.5;
  color: var(--ink-2);
  border: var(--bw) solid currentColor;
  background: transparent;
  white-space: nowrap;
}
.badge.accent { color: var(--accent); background: var(--accent-wash); }
.badge.ok     { color: var(--ok);     background: var(--ok-wash); }
.badge.warn   { color: var(--warn);   background: var(--warn-wash); }
.badge.bad    { color: var(--bad);    background: var(--bad-wash); }
/* On a shelf the stamp is struck by hand: a little off square. */
.badge.stamp { transform: rotate(-3deg); border-width: 2px; outline: 1px solid currentColor; outline-offset: 1px; }

/* A dot that carries the status colour without a word next to it. */
.dot { width: 7px; height: 7px; border-radius: var(--r-full); background: currentColor; flex: 0 0 7px; }

/* ============================================================ notices == */

/* A slip of coloured paper tucked into the page: tinted, with a heavy
   spine in the status colour so the state reads before the words do. */
.notice {
  padding: var(--s-4) var(--s-5);
  border-radius: var(--r-2);
  border: 1px solid var(--rule-2);
  border-left: 5px solid var(--ink-3);
  background: var(--sheet-2);
  color: var(--ink-2);
  font-size: var(--t-sm);
  line-height: 1.6;
}
.notice strong { color: var(--ink); }
.notice > :last-child { margin-bottom: 0; }

.notice.info    { border-color: var(--info-edge); border-left-color: var(--teal); background: var(--info-wash); }
.notice.ok      { border-color: var(--ok-edge);   border-left-color: var(--ok);   background: var(--ok-wash); }
.notice.warning { border-color: var(--warn-edge); border-left-color: var(--mustard); background: var(--warn-wash); }
.notice.error   { border-color: var(--bad-edge);  border-left-color: var(--bad);  background: var(--bad-wash); }
.notice.ok strong      { color: var(--ok); }
.notice.warning strong { color: var(--warn); }
.notice.error strong   { color: var(--bad); }

.flash {
  position: fixed;
  top: var(--s-5);
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  max-width: min(92vw, 30rem);
  background: var(--sheet);
  border: var(--bw) solid var(--ink);
  border-left-width: 6px;
  box-shadow: var(--press-2);
  color: var(--ink);
  font-weight: 500;
  animation: flash-in 280ms var(--ease);
}
.flash.info    { border-left-color: var(--teal); background: var(--sheet); }
.flash.ok      { border-left-color: var(--ok); background: var(--sheet); }
.flash.warning { border-left-color: var(--mustard); background: var(--sheet); }
.flash.error   { border-left-color: var(--bad); background: var(--sheet); }
@keyframes flash-in {
  from { opacity: 0; transform: translate(-50%, -10px) rotate(-1deg); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ============================================================ stepper == */

/* The wizard is set as a book's running head: four parts between two
   rules, numbered in Roman the way parts of a book are. */
.steps {
  display: flex;
  align-items: stretch;
  margin-bottom: var(--s-8);
  border-top: 1px solid var(--ink);
  border-bottom: 1px solid var(--ink);
  overflow-x: auto;
  scrollbar-width: none;
}
.steps::-webkit-scrollbar { display: none; }

.steps .step {
  flex: 1 1 0;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
  position: relative;
}
.steps .step .n {
  min-width: 1.6rem;
  height: 1.6rem;
  padding: 0 var(--s-1);
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-variation-settings: var(--soft);
  font-style: italic;
  font-size: var(--t-md);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-3);
}
.steps .step.done { color: var(--ink-2); }
.steps .step.done .n {
  width: 1.5rem;
  min-width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--r-full);
  background: var(--teal);
  color: var(--on-ink);
  font-family: var(--sans);
  font-style: normal;
  font-size: var(--t-xs);
}
/* The current part is marked the way a bookmark ribbon would: a band of
   colour that runs off the bottom rule. */
.steps .step.current { color: var(--ink); font-weight: 600; background: var(--sheet); }
.steps .step.current::after {
  content: "";
  position: absolute;
  left: var(--s-4);
  right: var(--s-4);
  bottom: -1px;
  height: 4px;
  background: var(--accent);
}
.steps .step.current .n { color: var(--accent); }
.steps .sep {
  width: 1px;
  flex: 0 0 1px;
  margin: var(--s-2) 0;
  background: var(--rule-2);
}

/* ======================================================== choice cards == */
/* Radio groups that look like cards: tiers, image models, presets. Each
   is an edition you could order; the chosen one is inked. */

.choice {
  display: block;
  position: relative;
  cursor: pointer;
  /* It is a <label>, and labels are set semibold; the card's body copy
     must not be. */
  font-weight: 400;
  padding: var(--s-4) var(--s-5);
  background: var(--sheet);
  border: var(--bw) solid var(--rule-2);
  border-radius: var(--r-2);
  transition: border-color var(--fast), background var(--fast), transform var(--fast), box-shadow var(--fast);
}
.choice:hover { border-color: var(--ink); }
.choice input { position: absolute; opacity: 0; pointer-events: none; }
.choice:has(input:checked) {
  border-color: var(--ink);
  background: var(--field);
  box-shadow: 5px 5px 0 var(--accent-2), 5px 5px 0 var(--bw) var(--ink);
  transform: translate(-2px, -2px);
}
.choice:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 4px; }

.choice .title { display: flex; align-items: baseline; gap: var(--s-3); }
/* A drawn radio before the name, so the card says "pick one" without the
   browser's own control, which cannot be styled to match. */
.choice .name {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--display);
  font-variation-settings: var(--soft);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--ink);
}
.choice .name::before {
  content: "";
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  border-radius: var(--r-full);
  border: var(--bw) solid var(--ink);
  background: var(--field);
  transition: background var(--fast), box-shadow var(--fast);
}
.choice:has(input:checked) .name::before {
  background: var(--accent);
  box-shadow: inset 0 0 0 2.5px var(--field);
}
.choice .price {
  margin-left: auto;
  font-family: var(--mono);
  font-weight: 600;
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.choice .desc { color: var(--ink-2); font-size: var(--t-sm); margin-top: var(--s-2); line-height: 1.5; }
.choice .meta {
  color: var(--ink-3);
  font-family: var(--mono);
  font-size: var(--t-xs);
  margin-top: var(--s-3);
  padding-top: var(--s-2);
  border-top: 1px dashed var(--rule-2);
  font-variant-numeric: tabular-nums;
}
.choice:has(input:checked) .price { color: var(--accent); }

/* ============================================================ receipt == */
/* The live estimate, set as a till receipt: typewritten lines with dotted
   leaders and a torn edge. It is the one number on the page that decides
   whether money is spent, so it gets the most recognisable form there is
   for "this is what it costs". */

.receipt {
  position: relative;
  padding: var(--s-5) var(--s-5) var(--s-4);
  background: var(--field);
  border: 1px solid var(--rule-2);
  border-bottom: 0;
  font-family: var(--mono);
  font-size: var(--t-sm);
  color: var(--ink-2);
  margin-bottom: 10px;
}
/* The torn foot: a row of half-circles cut from the sheet. */
.receipt::after {
  content: "";
  position: absolute;
  left: -1px;
  right: -1px;
  bottom: -10px;
  height: 10px;
  background:
    radial-gradient(circle at 6px 10px, transparent 5px, var(--field) 5.5px) 0 0 / 12px 10px repeat-x;
}
.receipt-head {
  font-size: var(--t-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: center;
  color: var(--ink-3);
  padding-bottom: var(--s-3);
  margin-bottom: var(--s-3);
  border-bottom: 1px dashed var(--rule-2);
}
.receipt-head strong { display: block; color: var(--ink); letter-spacing: 0.1em; font-size: var(--t-sm); }
.receipt-lines { margin: 0; }
.receipt-lines > div {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  font-variant-numeric: tabular-nums;
}
.receipt-lines > div + div { margin-top: var(--s-1); }
/* Dotted leaders, the way a contents page or a menu joins a label to its
   figure across the gap. On the receipt it hangs off the <dt>, since a
   <dl> group may hold nothing but terms and definitions. */
.receipt-lines dt {
  flex: 1;
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  margin: 0;
}
.receipt-lines dt::after {
  content: "";
  flex: 1;
  min-width: var(--s-4);
  border-bottom: 2px dotted var(--ink-4);
  transform: translateY(-4px);
}
.leaders .fill {
  flex: 1;
  min-width: var(--s-4);
  border-bottom: 2px dotted var(--ink-4);
  transform: translateY(-4px);
}
.receipt-lines dd { margin: 0; color: var(--ink); }
.receipt-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-top: var(--s-3);
  padding-top: var(--s-3);
  border-top: 2px solid var(--ink);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
}
.receipt-total strong {
  font-size: var(--t-lg);
  letter-spacing: 0;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.receipt-note {
  margin: var(--s-3) 0 0;
  font-size: var(--t-xs);
  color: var(--warn);
  line-height: 1.5;
}

/* =========================================================== progress == */

/* A barber-pole bar: the stripes are the "still moving" signal, so a
   finished run keeps its fill but loses the motion. */
.progress {
  height: 16px;
  border-radius: var(--r-full);
  background: var(--paper-deep);
  border: var(--bw) solid var(--ink);
  overflow: hidden;
  box-shadow: var(--deboss);
}
.progress > span {
  display: block;
  height: 100%;
  min-width: 12px;
  border-right: var(--bw) solid var(--ink);
  background:
    repeating-linear-gradient(-45deg, var(--accent) 0 8px, var(--accent-2) 8px 16px);
  background-size: 22.6px 22.6px;
  animation: stripes 900ms linear infinite;
  transition: width 500ms var(--ease);
}
.progress.done > span { animation: none; background: var(--teal); }
.progress.indeterminate > span {
  width: 36% !important;
  border-left: var(--bw) solid var(--ink);
  border-radius: var(--r-full);
  animation: stripes 900ms linear infinite, sweep 1.6s var(--ease) infinite alternate;
}
@keyframes stripes { to { background-position: 22.6px 0; } }
@keyframes sweep {
  from { margin-left: 0; }
  to   { margin-left: 64%; }
}

.spinner {
  width: 14px;
  height: 14px;
  display: inline-block;
  border-radius: var(--r-full);
  border: 2px solid var(--rule-2);
  border-top-color: var(--accent);
  border-right-color: var(--accent);
  animation: spin 800ms linear infinite;
  vertical-align: -2px;
  flex: 0 0 14px;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* A soft pulse for text that is waiting on something. */
.pulse { animation: pulse 1.8s var(--ease) infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ============================================================== shelf == */
/* The story list is a shelf of paperbacks. Each gets a cover in one of the
   print colours, cycled by position, with its initial set large in the
   display face -- the way a series used colour to tell titles apart. */

.shelf { display: grid; gap: var(--s-6); grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); }
@media (max-width: 720px) { .shelf { grid-template-columns: 1fr; } }

.book {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr);
  gap: var(--s-5);
  align-items: start;
}
.shelf > :nth-child(6n+2) .cover { --cover: var(--teal); }
.shelf > :nth-child(6n+3) .cover { --cover: var(--navy); }
.shelf > :nth-child(6n+4) .cover { --cover: var(--mustard); --cover-ink: var(--ink); }
.shelf > :nth-child(6n+5) .cover { --cover: var(--plum); }
.shelf > :nth-child(6n+6) .cover { --cover: var(--olive); }

.cover {
  position: relative;
  aspect-ratio: 2 / 3;
  border: var(--bw) solid var(--ink);
  border-radius: var(--r-1) var(--r-3) var(--r-3) var(--r-1);
  background:
    var(--spine-shade),
    var(--halftone-light) 0 0 / 6px 6px,
    var(--cover);
  color: var(--cover-ink);
  box-shadow: var(--press-2);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: var(--s-2) var(--s-2) var(--s-2) var(--s-3);
  overflow: hidden;
  text-decoration: none;
  transition: transform 200ms var(--ease), box-shadow 200ms var(--ease);
}
a.cover:hover { color: var(--cover-ink); transform: translate(-2px, -3px) rotate(-1.5deg); box-shadow: 5px 6px 0 var(--ink); }
.cover .initial {
  font-family: var(--display);
  font-variation-settings: var(--soft-wonk);
  font-size: 3.25rem;
  font-weight: 800;
  line-height: 0.9;
  margin-top: var(--s-2);
}
/* The publisher's band across the foot of the cover. */
.cover .band {
  border-top: 1px solid currentColor;
  padding-top: var(--s-1);
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.9;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.book h3 { font-size: var(--t-lg); line-height: 1.15; }
.book .byline { font-family: var(--serif); font-style: italic; color: var(--ink-2); }
.book .facts {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--ink-3);
  letter-spacing: 0.02em;
}

/* ========================================================= characters == */
/* A cast card is an index card: a red rule under the name, and reference
   photos mounted with corners like prints in an album. */

.char {
  background: var(--sheet);
  border: 1px solid var(--rule-2);
  border-radius: var(--r-2);
  padding: var(--s-5);
  box-shadow: var(--sheet-lift);
  transition: border-color var(--fast), opacity var(--fast);
}
.char:focus-within { border-color: var(--ink); }
.char.excluded { opacity: 0.55; }
.char-head {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: var(--s-4);
  padding-bottom: var(--s-3);
  border-bottom: 2px solid var(--accent-2);
}
.char-head h3 { margin: 0; font-size: var(--t-lg); }
.char .persona { font-family: var(--serif); font-style: italic; color: var(--ink-2); }

.photos {
  display: flex;
  gap: var(--s-4);
  flex-wrap: wrap;
  margin-top: var(--s-5);
  padding: var(--s-4);
  background: var(--paper-deep);
  border-radius: var(--r-2);
  box-shadow: var(--deboss);
}
.photos figure {
  margin: 0;
  position: relative;
  padding: 4px;
  background: var(--field);
  box-shadow: 1px 2px 0 var(--shade);
}
.photos figure:nth-child(odd)  { transform: rotate(-2deg); }
.photos figure:nth-child(even) { transform: rotate(1.5deg); }
/* Photo corners: four little black triangles holding the print down. */
.photos figure::before,
.photos figure::after {
  content: "";
  position: absolute;
  width: 12px;
  height: 12px;
  pointer-events: none;
}
.photos figure::before {
  top: -2px;
  left: -2px;
  background: linear-gradient(135deg, var(--ink) 50%, transparent 50%);
}
.photos figure::after {
  bottom: -2px;
  right: -2px;
  background: linear-gradient(-45deg, var(--ink) 50%, transparent 50%);
}
.photos img {
  width: 64px;
  height: 64px;
  object-fit: cover;
  background: var(--paper-deep);
  filter: sepia(0.12) contrast(1.02);
}
.photos .remove {
  position: absolute;
  top: -9px;
  right: -9px;
  z-index: 1;
  width: 22px;
  height: 22px;
  padding: 0;
  display: grid;
  place-items: center;
  font-size: 14px;
  line-height: 1;
  border-radius: var(--r-full);
  border: var(--bw) solid var(--ink);
  background: var(--sheet);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--press-1);
  transition: background var(--fast), color var(--fast);
}
.photos .remove:hover { background: var(--bad); color: var(--on-accent); }

.photo-add {
  width: 72px;
  height: 72px;
  margin: 0;
  border-radius: var(--r-2);
  border: var(--bw) dashed var(--ink-3);
  background: var(--sheet);
  display: grid;
  place-items: center;
  color: var(--ink-2);
  font-family: var(--display);
  font-size: 1.75rem;
  font-weight: 400;
  cursor: pointer;
  transition: border-color var(--fast), color var(--fast), background var(--fast);
}
.photo-add:hover { border-color: var(--ink); border-style: solid; color: var(--accent); background: var(--field); }
.photo-add input { display: none; }

/* ============================================================ details == */

details.disclose > summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--ink);
  padding: var(--s-2) 0;
}
details.disclose > summary::-webkit-details-marker { display: none; }
details.disclose > summary::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 6px solid var(--accent);
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  transition: transform var(--fast);
}
details.disclose[open] > summary::before { transform: rotate(90deg); }
details.disclose > summary:hover { color: var(--accent); }

/* ============================================================= reader == */
/* The product itself, so it is set as a book and not as a web page: a
   sheet on the desk, chapter openings with an ornament, a drop cap, and
   every illustration numbered as a plate. */

.reader {
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--s-9) var(--s-9) var(--s-10);
  background: var(--sheet);
  border: 1px solid var(--rule-2);
  box-shadow: var(--sheet-lift), 8px 8px 0 -1px var(--sheet-2), 8px 8px 0 0 var(--rule-2);
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-variation-settings: "SOFT" 0, "WONK" 0;
  font-size: 1.1875rem;
  font-weight: 380;
  line-height: 1.72;
  color: var(--ink);
  counter-reset: plate;
  hanging-punctuation: first;
}
.reader h2 {
  font-family: var(--display);
  font-variation-settings: var(--soft);
  font-weight: 500;
  font-size: var(--t-md);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-align: center;
  color: var(--ink-2);
  margin: var(--s-10) 0 var(--s-7);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
}
/* A fleuron-like ornament under the chapter title, drawn rather than set:
   the ornament glyphs are missing from too many fallback fonts. */
.reader h2::after {
  content: "";
  width: 64px;
  height: 10px;
  background:
    radial-gradient(circle, var(--accent-2) 3.5px, transparent 4px) center / 10px 10px no-repeat,
    linear-gradient(var(--ink-4), var(--ink-4)) left center / 24px 1px no-repeat,
    linear-gradient(var(--ink-4), var(--ink-4)) right center / 24px 1px no-repeat;
}
.reader > h2:first-child { margin-top: 0; }

.reader p {
  margin: 0;
  text-indent: 1.5em;
  max-width: none;
  text-wrap: pretty;
  hyphens: auto;
}
/* The first paragraph of a chapter, and the first after a plate, is set
   flush -- an indent there marks a break that is already obvious. */
.reader p:first-of-type, .reader figure + p, .reader h2 + p { text-indent: 0; }
/* Drop cap on a chapter's opening paragraph, whether or not a plate
   stands between it and the heading. */
.reader h2 + p::first-letter,
.reader h2 + figure + p::first-letter {
  float: left;
  font-family: var(--display);
  font-variation-settings: var(--soft-wonk);
  font-weight: 700;
  font-size: 4.1em;
  line-height: 0.8;
  margin: 0.08em 0.1em 0 0;
  color: var(--accent);
}
.reader h2 + p::first-line,
.reader h2 + figure + p::first-line {
  font-variant-caps: small-caps;
  letter-spacing: 0.04em;
}

.reader figure {
  margin: var(--s-7) calc(var(--s-5) * -1);
  counter-increment: plate;
}
/* The print is tipped in: an ink keyline and a narrow mount. A clip is
   mounted the same way, so a film reads as the same book. */
.reader figure img,
.reader figure video {
  width: 100%;
  border: 1px solid var(--ink);
  padding: 6px;
  background: var(--field);
  box-shadow: var(--sheet-lift);
}
/* A clip holds its frame before it loads, so the page does not jump. */
.reader figure video {
  display: block;
  aspect-ratio: 16 / 9;
}
.reader figcaption {
  font-family: var(--serif);
  font-style: italic;
  font-size: var(--t-base);
  color: var(--ink-2);
  text-align: center;
  margin: var(--s-4) auto 0;
  text-indent: 0;
  max-width: 52ch;
  line-height: 1.5;
}
.reader figcaption::before {
  content: "Plate " counter(plate, upper-roman);
  display: block;
  margin-bottom: var(--s-1);
  font-family: var(--mono);
  font-style: normal;
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}
.reader .placeholder {
  border: var(--bw) dashed var(--ink-4);
  background:
    var(--halftone) 0 0 / 7px 7px,
    var(--paper);
  padding: var(--s-9) var(--s-5);
  text-align: center;
  color: var(--ink-2);
  font-family: var(--sans);
  font-size: var(--t-sm);
  text-indent: 0;
}
.reader .placeholder::before {
  content: "Plate " counter(plate, upper-roman);
  display: block;
  margin-bottom: var(--s-2);
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* The end of the book. */
.reader::after {
  content: "* * *";
  display: block;
  margin-top: var(--s-9);
  text-align: center;
  letter-spacing: 0.6em;
  color: var(--ink-4);
  font-family: var(--display);
}

/* The running head above the book: title, count of plates, actions. */
.reader-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--paper);
  background-image: var(--grain);
  border-bottom: 1px solid var(--ink);
  padding: var(--s-3) 0;
  margin-bottom: var(--s-7);
}
.reader-bar .title {
  font-family: var(--display);
  font-variation-settings: var(--soft);
  font-weight: 600;
  font-size: var(--t-md);
}
.reader-bar .by { font-family: var(--serif); font-style: italic; color: var(--ink-2); }
/* The actions never wrap under the title; the title gives way instead. */
.reader-bar .row { flex-wrap: nowrap; }
.reader-bar .grow {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* =============================================================== diff == */

.code-block, .diff {
  font-family: var(--mono);
  font-size: var(--t-xs);
  line-height: 1.65;
  background: var(--paper-deep);
  border-radius: var(--r-2);
  padding: var(--s-3) var(--s-4);
  overflow-x: auto;
  color: var(--ink);
  box-shadow: var(--deboss);
}
.diff div, .code-block div { white-space: pre; }
.diff .add  { color: var(--ok);  background: var(--ok-wash); }
.diff .del  { color: var(--bad); background: var(--bad-wash); text-decoration: line-through; text-decoration-color: var(--bad-edge); }
.diff .hunk { color: var(--teal); }
.diff .meta { color: var(--ink-3); }

/* ============================================================== table == */

table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
th, td { text-align: left; padding: var(--s-2) 0; border-bottom: 1px dotted var(--rule-2); vertical-align: baseline; }
th {
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
td { color: var(--ink-2); }
/* Code in a head cell keeps its own case: `{{ story_text }}` shouted as
   `{{ STORY_TEXT }}` names a variable that does not exist. */
th code { text-transform: none; letter-spacing: 0; }
tr:last-child th, tr:last-child td { border-bottom: 0; }
/* A two-column key/value table, which is most of the tables here: set
   like a specification plate, key in caps, value in the book face. */
table.kv th { width: 45%; padding-right: var(--s-3); }
table.kv td { text-align: right; color: var(--ink); font-variant-numeric: tabular-nums; }
table.kv td strong { font-family: var(--mono); font-size: var(--t-md); color: var(--accent); }

/* ============================================================== empty == */

.empty {
  position: relative;
  text-align: center;
  padding: var(--s-10) var(--s-6);
  border: var(--bw) dashed var(--ink-4);
  border-radius: var(--r-3);
  background:
    radial-gradient(circle at 50% 0%, var(--paper) 0 45%, transparent 75%),
    var(--halftone) 0 0 / 8px 8px,
    var(--paper);
}
/* A section break, the book's own way of saying "nothing more here". */
.empty::before {
  content: "* * *";
  display: block;
  margin-bottom: var(--s-4);
  font-family: var(--display);
  font-size: var(--t-lg);
  letter-spacing: 0.5em;
  color: var(--accent-2);
}
.empty h2 { margin-bottom: var(--s-3); font-size: var(--t-xl); }
.empty p { margin-left: auto; margin-right: auto; color: var(--ink-2); max-width: 46ch; }
.empty .btn { margin-top: var(--s-5); }

/* =============================================================== misc == */

.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline-block; }
/* Dim a region while its request is in flight, so a slow save is visible. */
.htmx-request.char, .htmx-request .dimmable { opacity: 0.6; transition: opacity var(--fast); }

.kbd {
  font-family: var(--mono);
  font-size: var(--t-xs);
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--ink-3);
  border-bottom-width: 3px;
  border-radius: var(--r-2);
  background: var(--sheet);
  color: var(--ink);
}

/* =============================================================== hero == */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--s-9);
  align-items: center;
  padding: var(--s-6) 0 var(--s-9);
}
.hero-copy { max-width: 35rem; }
.hero-kicker {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  border: var(--bw) solid var(--ink);
  border-radius: var(--r-full);
  background: var(--mustard);
  font-family: var(--mono);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  box-shadow: var(--press-1);
}
.hero h1 {
  font-size: var(--t-3xl);
  font-weight: 800;
  font-variation-settings: var(--soft-wonk);
  line-height: 1.02;
  letter-spacing: -0.03em;
  margin: var(--s-5) 0 0;
}
.hero h1 em {
  font-weight: 700;
  color: var(--accent);
  /* A hand-inked underline beneath the key word. */
  background: linear-gradient(var(--mustard-2), var(--mustard-2)) 0 88% / 100% 0.2em no-repeat;
  padding: 0 0.04em;
}
.hero .lede {
  margin-top: var(--s-5);
  font-size: var(--t-md);
  color: var(--ink-2);
  line-height: 1.65;
}

/* The illustration beside the headline: two jackets on a sunburst. Every
   fill comes from a class so the artwork re-colours with the tokens. */
.hero-art { position: relative; }
.hero-art svg { width: 100%; height: auto; overflow: visible; }
.art .f-ink     { fill: var(--ink); }
.art .f-paper   { fill: var(--paper); }
.art .f-sheet   { fill: var(--sheet); }
.art .f-accent  { fill: var(--accent-2); }
.art .f-teal    { fill: var(--teal); }
.art .f-teal-2  { fill: var(--teal-2); }
.art .f-mustard { fill: var(--mustard); }
.art .f-mustard-2 { fill: var(--mustard-2); }
.art .f-navy    { fill: var(--navy); }
.art .f-blush   { fill: var(--blush); }
.art .f-shade   { fill: var(--shade); }
.art .s-ink     { stroke: var(--ink); fill: none; }
.art .s-sheet   { stroke: var(--sheet); fill: none; }
.art .t-display { font-family: var(--display); font-variation-settings: var(--soft-wonk); }
.art .t-mono    { font-family: var(--mono); letter-spacing: 0.16em; }
.art .spin-slow { transform-origin: 318px 88px; animation: spin 40s linear infinite; }

/* The landing's how-it-works, set as a contents page: part numbers, titles
   joined to their page by dotted leaders, a line of summary beneath. */
.contents {
  padding: var(--s-8) var(--s-8) var(--s-7);
  background: var(--sheet);
  border: 1px solid var(--rule-2);
  box-shadow: var(--sheet-lift);
  border-radius: var(--r-2);
}
.contents-title {
  text-align: center;
  font-size: var(--t-lg);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  font-weight: 500;
  margin-bottom: var(--s-7);
}
.contents ol { list-style: none; margin: 0; padding: 0; }
.contents li {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr);
  column-gap: var(--s-3);
  padding: var(--s-4) 0;
  border-top: 1px solid var(--rule);
}
.contents li:first-child { border-top: 0; }
.contents .part {
  font-family: var(--display);
  font-variation-settings: var(--soft);
  font-style: italic;
  font-size: var(--t-lg);
  color: var(--accent);
  line-height: 1.2;
}
.leaders {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
}
.leaders .label {
  font-family: var(--display);
  font-variation-settings: var(--soft);
  font-weight: 600;
  font-size: var(--t-lg);
  line-height: 1.2;
}
.leaders .page { font-family: var(--mono); font-size: var(--t-sm); color: var(--ink-3); }
.contents li p { margin: var(--s-1) 0 0; grid-column: 2; color: var(--ink-2); font-size: var(--t-sm); }

/* Two notes from the publisher, set as a pair of columns under a rule. */
.notes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--s-8);
  margin-top: var(--s-9);
  padding-top: var(--s-7);
  border-top: 3px double var(--ink);
}
.notes h3 { font-size: var(--t-lg); margin-bottom: var(--s-2); }
.notes .overline { color: var(--accent); margin-bottom: var(--s-2); }

@media (max-width: 900px) {
  .hero { grid-template-columns: 1fr; gap: var(--s-7); padding-top: 0; }
  .hero h1 { font-size: var(--t-2xl); }
  .hero-art { max-width: 420px; margin: 0 auto; }
  .contents { padding: var(--s-6) var(--s-5); }
}

/* ============================================================= errata == */

/* The status number on an error page: set large and hollow, like a
   numeral cut in wood and inked only at the edges. */
.error-code {
  font-family: var(--display);
  font-variation-settings: var(--soft-wonk);
  font-size: 6rem;
  font-weight: 800;
  line-height: 1;
  color: var(--sheet);
  -webkit-text-stroke: 2px var(--ink);
  text-shadow: 5px 5px 0 var(--accent-2);
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--s-4);
}

/* A labelled divider between two alternative inputs ("paste ... or ...
   upload"), with the word set in italic between two rules. */
.or-rule {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  color: var(--ink-3);
  font-family: var(--serif);
  font-style: italic;
  font-size: var(--t-md);
}
.or-rule::before, .or-rule::after {
  content: "";
  flex: 1;
  height: 0;
  border-top: 1px solid var(--rule-2);
}

/* Status-coloured text, for a line of explanation next to a figure. */
.ok-text   { color: var(--ok); }
.warn-text { color: var(--warn); }
.bad-text  { color: var(--bad); }

/* One provider's key block inside the Settings card. Separated by a rule
   rather than nested in its own card: a card inside a card is the fastest way
   to make a settings page look like a pile of boxes. */
.provider + .provider { margin-top: var(--s-5); padding-top: var(--s-5); border-top: 1px solid var(--rule); }

/* A whole row that is a link, inside a flush card: a ledger line. */
.prompt-row {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  color: inherit;
  text-decoration: none;
  transition: background var(--fast);
}
.prompt-row:hover { color: inherit; background: var(--field); }
.prompt-row strong {
  font-family: var(--display);
  font-variation-settings: var(--soft);
  font-size: var(--t-md);
}
.prompt-row .chev {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  display: grid;
  place-items: center;
  border: var(--bw) solid var(--rule-2);
  border-radius: var(--r-full);
  color: var(--ink-3);
  transition: transform var(--fast), color var(--fast), border-color var(--fast), background var(--fast);
}
.prompt-row:hover .chev {
  color: var(--on-accent);
  background: var(--accent);
  border-color: var(--ink);
  transform: translateX(3px);
}
/* The row's padding comes from `.rows`, so the anchor needs it back. */
.rows > .prompt-row { padding: var(--s-5) var(--s-6); }

/* One preset in the endpoint editor's disclosure. */
.preset {
  padding: var(--s-4);
  background: var(--paper);
  border: 1px dashed var(--rule-2);
  border-radius: var(--r-2);
}

/* Variables shown as a wrapped run of chips rather than a table: they are
   labels, not rows of data, and a two-column table of one-word keys wastes
   half the rail. */
.var-list { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.var-list code { font-size: var(--t-xs); }

/* --- Confirmation dialog ---------------------------------------------- */
/* In-app rather than `window.confirm`: a browser chrome dialog cannot show
   a price as anything but plain text, looks nothing like the app, and is
   suppressible on some platforms -- which would make a spend confirmation
   silently disappear. A native <dialog> keeps the focus trap, Esc-to-close
   and ::backdrop that reimplementing this by hand always gets wrong.

   Dressed as an airmail envelope: the striped border is the most familiar
   picture there is of "this is about to be sent". */
.confirm-dialog {
  width: min(31rem, calc(100vw - var(--s-8)));
  padding: calc(var(--s-6) + 10px) var(--s-6) var(--s-6);
  color: var(--ink);
  background: var(--sheet);
  border: 2px solid var(--ink);
  border-radius: var(--r-2);
  box-shadow: var(--press-3);
  overflow: hidden;
}
.confirm-dialog[open] { animation: dialog-in 220ms var(--ease); }
.confirm-dialog::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 10px;
  border-bottom: 2px solid var(--ink);
  background: repeating-linear-gradient(
    -45deg,
    var(--accent-2) 0 12px, var(--sheet) 12px 24px,
    var(--navy) 24px 36px, var(--sheet) 36px 48px
  );
}
.confirm-dialog::backdrop {
  background: var(--scrim);
}
@keyframes dialog-in {
  from { opacity: 0; transform: translateY(8px) rotate(-0.6deg); }
  to   { opacity: 1; transform: none; }
}
.confirm-title {
  margin: 0;
  font-size: var(--t-xl);
}
.confirm-body {
  margin: 0;
  color: var(--ink-2);
  line-height: 1.6;
}
/* The destructive and the safe choice must not be adjacent by accident:
   Cancel sits left of a gap wide enough that a mis-aimed click lands on
   neither. */
.confirm-actions {
  gap: var(--s-4);
  margin-top: var(--s-2);
  padding-top: var(--s-4);
  border-top: 1px dashed var(--rule-2);
}

/* ========================================================= responsive == */

@media (max-width: 720px) {
  main { padding: var(--s-6) var(--s-4) var(--s-9); }
  .site-header .inner { padding: var(--s-3) var(--s-4); gap: var(--s-3); }
  .brand { font-size: var(--t-md); gap: var(--s-2); }
  .brand .mark { width: 32px; height: 32px; flex-basis: 32px; }
  .site-nav { gap: 0; }
  .site-nav .navlink { padding: var(--s-2); letter-spacing: 0.04em; }
  .site-nav .divider { display: none; }
  .page-head h1 { font-size: var(--t-xl); }
  .page-head .lede { font-size: var(--t-base); }
  .card { padding: var(--s-5); }
  .grid.cols-2 { grid-template-columns: 1fr; }
  .steps .step { padding: var(--s-3) var(--s-2); letter-spacing: 0.04em; }
  .reader {
    padding: var(--s-7) var(--s-5) var(--s-8);
    font-size: 1.0625rem;
    box-shadow: var(--sheet-lift);
  }
  .reader figure { margin-left: 0; margin-right: 0; }
  .book { grid-template-columns: 84px minmax(0, 1fr); gap: var(--s-4); }
  .cover .initial { font-size: 2.5rem; }
  .error-code { font-size: 4.5rem; }
}

/* Dotted leaders need room to run; on a phone the label and its "page"
   stack instead, and the page becomes a small caption under the title. */
@media (max-width: 560px) {
  .contents li { grid-template-columns: 2.25rem minmax(0, 1fr); }
  .leaders { display: block; }
  .leaders .fill { display: none; }
  .leaders .page {
    display: block;
    margin-top: var(--s-1);
    font-size: var(--t-xs);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent);
  }
  .contents-title { letter-spacing: 0.2em; }
}

/* On a phone the wordmark gives way to the colophon alone, so the
   navigation never has to wrap under it. */
@media (max-width: 480px) {
  .reader-bar .by { display: none; }
  .brand .word { display: none; }
  .steps .step .label { display: none; }
  .steps .step.current .label { display: inline; }
}

/* ============================================================ tickets == */

/* The sign-in and registration forms are a reader's ticket: the one sheet
   in the app that is inked all round, with a band of the house mustard
   across its head. It is the front door, so it is the one place that
   earns the heavier treatment. */
.card.ticket {
  position: relative;
  border: var(--bw) solid var(--ink);
  box-shadow: var(--press-3);
  padding-top: calc(var(--s-6) + 12px);
  overflow: hidden;
}
.card.ticket::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 12px;
  border-bottom: var(--bw) solid var(--ink);
  background:
    var(--halftone) 0 0 / 5px 5px,
    var(--mustard);
}

/* The error page's empty state drops the section-break ornament: the big
   numeral is ornament enough. */
.empty.erratum::before { display: none; }
.empty.erratum .overline { color: var(--accent); margin-bottom: var(--s-2); }

/* ======================================================= page furniture == */

/* A section title that runs into a rule, the way a book sets a part
   heading across the measure. Used where a heading introduces a group of
   cards rather than sitting inside one. */
.section-rule {
  display: flex;
  align-items: center;
  gap: var(--s-4);
}
.section-rule h2 { font-size: var(--t-lg); white-space: nowrap; }
.section-rule::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--ink);
}

/* A note in a side rail that does not need a sheet of its own: a rule
   down its left edge, as a marginal note would have. */
.aside-note {
  padding: var(--s-1) 0 var(--s-1) var(--s-5);
  border-left: 3px solid var(--mustard);
}
.aside-note .overline { color: var(--ink-2); }

/* The draft's "Ready to read" panel is the page's one decision, so its
   sheet is inked like the button inside it. */
.card.ready { border: var(--bw) solid var(--ink); box-shadow: var(--press-3); }

.char .persona {
  font-size: var(--t-base);
  line-height: 1.5;
  margin-bottom: var(--s-4);
}
.photo-note {
  margin-top: var(--s-3);
  font-size: var(--t-xs);
  color: var(--ink-3);
  line-height: 1.5;
}

/* Run status: the percentage set large in the typewriter face, like the
   counter on a press. */
.run-pct {
  font-family: var(--mono);
  font-size: var(--t-lg);
  font-weight: 600;
  color: var(--accent);
}
.run-facts {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--ink-3);
  padding-top: var(--s-3);
  border-top: 1px dashed var(--rule-2);
}

/* --- A public book's cover --------------------------------------------- */
/* Its first illustration, tipped in like a plate, with the publisher's
   band laid across the foot on a strip of paper so it stays legible over
   whatever the picture is. */
.cover.plate { padding: 0; }
.cover.plate img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.cover.plate .band {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--s-1) var(--s-2);
  background: var(--sheet);
  color: var(--ink);
  border-top: var(--bw) solid var(--ink);
  opacity: 1;
}

/* === The bookshop ======================================================= */
/* The marketplace borrows from a shop counter: tabs are index cards stuck
   out of a box, the arrows are rubber stamps, the pager is a ledger footer.
   Only the things you can press carry ink. */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.market-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s-4);
  border-bottom: var(--bw) solid var(--ink);
  padding-bottom: var(--s-2);
}

/* Index-card tabs: the current one sits forward, in ink. */
.tabs { display: flex; gap: var(--s-1); }
.tab {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--rule-2);
  border-bottom: 0;
  border-radius: var(--r-1) var(--r-1) 0 0;
  background: var(--sheet-2);
  color: var(--ink-3);
  text-decoration: none;
  transition: color var(--fast), background var(--fast);
}
.tab:hover { color: var(--ink); background: var(--sheet); }
.tab[aria-current="page"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--on-ink);
}

.market-search { display: flex; gap: var(--s-2); align-items: center; }
.market-search input[type="search"] { min-width: 16rem; }

/* Votes: two small stamps and the tally between them. */
.votes { display: flex; flex-direction: column; align-items: center; gap: var(--s-1); }
.votes-form { display: flex; flex-direction: column; align-items: center; gap: var(--s-1); }
.vote {
  width: 2rem;
  height: 1.625rem;
  display: grid;
  place-items: center;
  border: var(--bw) solid var(--ink);
  border-radius: var(--r-1);
  background: var(--sheet);
  color: var(--ink-3);
  font-size: var(--t-xs);
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--press-1);
  transition: transform var(--fast), background var(--fast), color var(--fast);
}
.vote:hover { color: var(--ink); transform: translate(-1px, -1px); }
.vote:active { transform: translate(1px, 1px); box-shadow: none; }
.vote.up.on { background: var(--accent); color: var(--on-accent); }
.vote.down.on { background: var(--navy); color: var(--on-ink); }
.votes .score {
  font-family: var(--mono);
  font-weight: 600;
  font-size: var(--t-sm);
  min-width: 2rem;
  text-align: center;
}

/* In the reader's bar the votes lie down. */
.reader-bar .votes,
.reader-bar .votes-form { flex-direction: row; }

.reader-credit { margin-bottom: var(--s-6); }

/* A ledger footer, not a row of page-number chips. */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-5);
  margin-top: var(--s-8);
  padding-top: var(--s-4);
  border-top: 1px solid var(--rule);
}

/* === Comments =========================================================== */
/* Marginalia: set in the reading face, separated by rules, never boxed. */
.comments { max-width: var(--measure); margin: var(--s-10) auto 0; }
.comment-list { list-style: none; margin: 0; padding: 0; }
.comment {
  padding: var(--s-4) 0;
  border-top: 1px solid var(--rule);
}
.comment:first-child { border-top: 0; }
.comment-body {
  margin: var(--s-2) 0;
  font-family: var(--serif);
  color: var(--ink-2);
  /* The author's line breaks, kept without turning any text into markup. */
  white-space: pre-line;
  overflow-wrap: anywhere;
}
.comment-edit > summary { list-style: none; display: inline-flex; }
.comment-edit > summary::-webkit-details-marker { display: none; }
.comment-form { padding-top: var(--s-4); border-top: var(--bw) solid var(--ink); }

/* ================================================================ noir == */
/* The after-dark edition. Everything above still holds -- tokens, scale,
   no box in a box -- but the press metaphor gives way to a lounge: deep
   charcoal sheets, crimson-to-magenta for what you can press, a soft bloom
   on hover instead of a hard offset, and type set like an evening
   programme (a high-contrast display serif over a clean sans). */

body {
  background-color: var(--paper);
  background-image: var(--grad-page), var(--grain);
  background-attachment: fixed;
}
::selection { background: var(--magenta); color: var(--on-accent); }

h1, h2, h3, .brand, .hero h1 {
  font-family: var(--display);
  font-variation-settings: var(--soft);
  letter-spacing: -0.01em;
}
h1 { font-weight: 600; }
h1 em, h2 em, .hero h1 em {
  font-style: italic;
  background: var(--grad-accent);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
a { color: var(--accent-deep); }
a:hover { color: var(--ink); }

/* --- masthead: glass bar, sticky ---------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  border-bottom: 1px solid var(--rule);
  background: var(--glass);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}
.brand { color: var(--ink); font-style: italic; font-weight: 600; }
.brand .word { background: var(--grad-accent); -webkit-background-clip: text; background-clip: text; color: transparent; }
.brand .mark { color: var(--magenta); filter: drop-shadow(var(--glow-soft)); }
.site-footer .colophon-mark { color: var(--accent); }
.badge-18 {
  display: inline-grid;
  place-items: center;
  min-width: 2.1rem;
  height: 1.35rem;
  padding: 0 var(--s-1);
  border: 1px solid var(--accent-edge);
  border-radius: var(--r-full);
  font-family: var(--sans);
  font-size: 0.6875rem;
  font-weight: 700;
  font-style: normal;
  letter-spacing: 0.04em;
  color: var(--accent-deep);
  -webkit-text-fill-color: var(--accent-deep);
}
.site-nav .navlink {
  font-family: var(--sans);
  letter-spacing: 0.08em;
  color: var(--ink-3);
  border-radius: var(--r-full);
}
.site-nav .navlink:hover { background: var(--accent-wash); color: var(--ink); }
.site-nav .navlink[aria-current="page"] {
  background: var(--accent-wash);
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--accent-edge);
}

/* --- surfaces ------------------------------------------------------------ */
.card {
  background: linear-gradient(180deg, var(--sheet-2), var(--sheet));
  border: 1px solid var(--rule);
  border-radius: var(--r-3);
  box-shadow: var(--sheet-lift);
}
a.card:hover { border-color: var(--accent-edge); transform: translateY(-3px); box-shadow: var(--glow), var(--sheet-lift); }
.card-head { border-bottom-color: var(--rule); }

/* --- buttons: pills with a bloom ----------------------------------------- */
.btn {
  --btn-bg: var(--sheet-2);
  --btn-bg-hover: var(--paper-edge);
  --btn-fg: var(--ink);
  --btn-line: var(--rule-2);
  --btn-shadow: none;
  --btn-shadow-hover: var(--glow-soft);
  --btn-lift: translateY(-1px);
  --btn-sink: translateY(1px);
  border-radius: var(--r-full);
  padding: 0.6rem 1.15rem;
  letter-spacing: 0.02em;
}
.btn:hover { border-color: var(--accent-edge); }
.btn.sm { --btn-shadow: none; --btn-shadow-hover: var(--glow-soft); --btn-sink: translateY(1px); }
.btn.primary {
  --btn-bg: var(--crimson);
  --btn-bg-hover: var(--magenta);
  --btn-fg: var(--on-accent);
  --btn-line: transparent;
  --btn-shadow: 0 6px 22px rgb(200 16 46 / 30%);
  --btn-shadow-hover: var(--glow);
  background: var(--grad-accent);
}
.btn.ghost { --btn-bg: transparent; --btn-line: var(--rule-2); }

/* --- fields -------------------------------------------------------------- */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), select, textarea {
  background: var(--field);
  color: var(--ink);
  border-color: var(--rule-2);
  border-radius: var(--r-2);
}
input:focus, select:focus, textarea:focus {
  border-color: var(--magenta);
  box-shadow: 0 0 0 3px var(--accent-wash);
  outline: none;
}
::placeholder { color: var(--ink-4); }

/* --- the bookshop: a gallery of large covers ------------------------------ */
.market-bar { border-bottom: 1px solid var(--rule); }
.tab {
  border: 1px solid var(--rule);
  border-radius: var(--r-full);
  background: transparent;
  margin-bottom: var(--s-2);
  font-family: var(--sans);
}
.tab:hover { background: var(--accent-wash); color: var(--ink); }
.tab[aria-current="page"] { background: var(--grad-accent); border-color: transparent; color: var(--on-accent); }
.tabs { gap: var(--s-2); flex-wrap: wrap; }

.shelf:has(.market-book) {
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: var(--s-7) var(--s-6);
}
.market-book {
  grid-template-columns: 1fr;
  gap: var(--s-4);
  padding: var(--s-3);
  border: 1px solid var(--rule);
  border-radius: var(--r-4);
  background: linear-gradient(180deg, var(--sheet-2), var(--sheet));
  transition: transform 220ms var(--ease), box-shadow 220ms var(--ease), border-color 220ms var(--ease);
}
.market-book:hover { transform: translateY(-4px); border-color: var(--accent-edge); box-shadow: var(--glow), var(--sheet-lift); }
.market-book .cover { aspect-ratio: 3 / 4; border-radius: var(--r-3); }
.market-book h3 a { color: var(--ink); text-decoration: none; }
.market-book h3 a:hover { color: var(--accent-deep); }

.cover {
  border: 1px solid var(--rule-2);
  box-shadow: 0 14px 30px rgb(0 0 0 / 60%);
  background: var(--halftone-light) 0 0 / 6px 6px, linear-gradient(160deg, var(--cover), var(--paper));
}
.cover img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 500ms var(--ease), filter 300ms var(--ease);
}
a.cover:hover { transform: none; box-shadow: var(--glow); }
.market-book:hover .cover img { transform: scale(1.04); }
.cover .band {
  position: relative;
  z-index: 1;
  margin-top: auto;
  padding: var(--s-2);
  border-top: 0;
  background: linear-gradient(0deg, rgb(0 0 0 / 75%), transparent);
  color: var(--ink);
  opacity: 1;
}
.shelf > :nth-child(6n+1) .cover { --cover: var(--crimson); }
.shelf > :nth-child(6n+4) .cover { --cover: var(--plum); --cover-ink: var(--ink); }

.vote {
  border: 1px solid var(--rule-2);
  border-radius: var(--r-full);
  background: var(--sheet-2);
  box-shadow: none;
}
.vote:hover { border-color: var(--accent-edge); box-shadow: var(--glow-soft); }
.vote.up.on { background: var(--grad-accent); border-color: transparent; }

/* --- hero ------------------------------------------------------------------ */
.hero h1 { font-weight: 500; line-height: 1.05; }
.hero-kicker { color: var(--accent-deep); letter-spacing: 0.18em; }
.hero-art { filter: drop-shadow(0 0 60px rgb(224 17 95 / 25%)) saturate(0.9); }

/* --- reader ------------------------------------------------------------------ */
.reader figure img, .plate img { border-radius: var(--r-2); box-shadow: 0 18px 50px rgb(0 0 0 / 65%); }

/* --- tables, notices, misc ----------------------------------------------------- */
table th { color: var(--ink-3); border-color: var(--rule); }
table td { border-color: var(--rule); }
.notice { border-radius: var(--r-2); }
.site-footer { border-top: 1px solid var(--rule); color: var(--ink-3); }

/* --- 18+ notice and legal links in the footer --------------------------------- */
.footer-legal {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-2) var(--s-5);
  font-size: var(--t-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.footer-legal a { color: var(--ink-3); text-decoration: none; }
.footer-legal a:hover { color: var(--accent-deep); text-shadow: var(--glow-soft); }
.footer-adult { max-width: var(--measure); margin: 0 auto; text-align: center; font-size: var(--t-xs); color: var(--ink-4); }

/* --- age gate ------------------------------------------------------------------ */
/* Until a visitor confirms they are 18+, every cover, thumbnail and plate is
   veiled. The class is set on <html> by the server from a cookie, so the
   veil is there on first paint and needs no script. */
html.age-locked .cover img,
html.age-locked .plate img,
html.age-locked .reader img,
html.age-locked main img { filter: var(--veil); transform: scale(1.08); }
html.age-locked body { overflow: hidden; }

.age-gate {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: var(--s-6);
  background: var(--scrim);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.age-gate .panel {
  width: min(520px, 100%);
  text-align: center;
  padding: var(--s-8) var(--s-7);
  border: 1px solid var(--accent-edge);
  border-radius: var(--r-4);
  background: linear-gradient(180deg, var(--sheet-2), var(--paper));
  box-shadow: var(--glow), 0 30px 80px rgb(0 0 0 / 70%);
}
.age-gate .seal {
  width: 76px;
  height: 76px;
  margin: 0 auto var(--s-5);
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  background: var(--grad-accent);
  color: var(--on-accent);
  font-family: var(--display);
  font-size: var(--t-xl);
  font-weight: 700;
  box-shadow: var(--glow);
}
.age-gate h2 { font-size: var(--t-2xl); margin: 0 0 var(--s-3); }
.age-gate p { color: var(--ink-2); }
.age-gate .actions { display: flex; flex-wrap: wrap; gap: var(--s-3); justify-content: center; margin-top: var(--s-6); }
.age-gate .fine { margin-top: var(--s-5); font-size: var(--t-xs); color: var(--ink-4); }

/* --- legal pages ------------------------------------------------------------- */
.legal { max-width: var(--measure); margin: 0 auto; }
.legal h1 { font-size: var(--t-2xl); }
.legal h2 { font-size: var(--t-lg); margin-top: var(--s-7); }
.legal p, .legal li { color: var(--ink-2); line-height: 1.75; }

/* --- mobile ------------------------------------------------------------------ */
@media (max-width: 760px) {
  .site-header .inner { flex-wrap: wrap; gap: var(--s-3); padding: var(--s-3) var(--s-4); }
  .site-nav { margin-left: 0; width: 100%; overflow-x: auto; flex-wrap: nowrap; padding-bottom: var(--s-1); }
  .shelf:has(.market-book) { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4); }
  .market-book { padding: var(--s-2); }
  .market-book .facts { display: none; }
  .market-search input[type="search"] { min-width: 0; width: 100%; }
  .market-search { width: 100%; }
}
@media (max-width: 420px) {
  .shelf:has(.market-book) { grid-template-columns: 1fr; }
}

/* ============================================================= v2 ==== */
/* "Nocturne". A late-night magazine rather than an app template: a
   high-contrast Didone for headlines, a sturdy grotesque for everything
   you read and press, square corners, hairline rules for structure, and
   one flat neon purple that means "this is the thing to do". */

body { background-image: none; background-color: var(--paper); font-family: var(--sans); font-size: 1rem; line-height: 1.55; }
::selection { background: var(--neon); color: var(--on-accent); }
:focus-visible { outline: 2px solid var(--neon); outline-offset: 2px; }

h1, h2, h3, h4 { font-family: var(--display); font-variation-settings: normal; letter-spacing: -0.01em; color: var(--ink); }
h1 { font-weight: 700; font-size: clamp(2.25rem, 4.5vw, 3.5rem); line-height: 1; }
h2 { font-weight: 600; line-height: 1.05; }
h3 { font-weight: 600; }
a { color: var(--neon-ink); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--pink); }

/* Section labels: a quiet italic, not shouted capitals. */
.overline, .page-head .eyebrow, .hero-kicker {
  text-transform: none; letter-spacing: 0; font-family: var(--display);
  font-style: italic; font-weight: 500; font-size: var(--t-md); color: var(--pink);
}
.page-head .eyebrow::before, .page-head .eyebrow::after { display: none; }
.page-head { border-bottom: 1px solid var(--rule); padding-bottom: var(--s-6); margin-bottom: var(--s-8); }
.page-head h1 { font-size: clamp(2.25rem, 4.5vw, 3.25rem); }
.page-head .lede { font-size: var(--t-md); color: var(--ink-2); }

/* --- masthead --------------------------------------------------------- */
.site-header {
  position: static; background: var(--paper); border-bottom: 0;
  -webkit-backdrop-filter: none; backdrop-filter: none; box-shadow: none;
}
.site-header .inner { border-bottom: 3px solid var(--neon); padding-top: var(--s-5); padding-bottom: var(--s-3); align-items: flex-end; }
.brand { gap: var(--s-2); align-items: baseline; }
.brand .word {
  background: none; -webkit-background-clip: border-box; background-clip: border-box;
  color: var(--ink); font-family: var(--display); font-style: italic; font-weight: 800;
  font-size: 2rem; letter-spacing: -0.03em; line-height: 1;
}
.brand:hover .word { color: var(--neon-ink); }
.badge-18 {
  background: none; border: 1px solid var(--red); color: var(--red); box-shadow: none;
  border-radius: 0; font-family: var(--mono); font-size: var(--t-xs); padding: 0 var(--s-1); transform: translateY(-0.6em);
}
.site-nav { gap: var(--s-5); }
.site-nav .navlink {
  padding: 0 0 var(--s-1); border-radius: 0; background: none; text-transform: none; letter-spacing: 0;
  font-family: var(--sans); font-size: var(--t-base); font-weight: 500; color: var(--ink-2);
  border-bottom: 2px solid transparent;
}
.site-nav .navlink:hover { background: none; color: var(--ink); border-bottom-color: var(--rule-2); }
.site-nav .navlink[aria-current="page"] { background: none; color: var(--ink); border-bottom-color: var(--pink); box-shadow: none; }
.site-nav .divider { background: var(--rule-2); }

/* --- buttons: rectangles, flat ----------------------------------------- */
.btn {
  border-radius: 0; box-shadow: none; --btn-shadow: none; --btn-shadow-hover: none; --btn-sink: none;
  background: var(--sheet-2); border: 1px solid var(--rule-2); color: var(--ink);
  font-family: var(--sans); font-weight: 600; letter-spacing: 0; text-transform: none;
}
.btn:hover { background: var(--plum-deep); border-color: var(--neon); color: var(--ink); transform: none; box-shadow: none; }
.btn.primary { background: var(--neon); border-color: var(--neon); color: var(--on-accent); }
.btn.primary:hover { background: var(--accent-2); border-color: var(--accent-2); color: var(--on-accent); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--ink-2); text-decoration: underline; text-decoration-color: var(--rule-2); text-underline-offset: 4px; }
.btn.ghost:hover { background: transparent; border-color: transparent; color: var(--ink); text-decoration-color: var(--pink); }
.btn.danger, .btn.ghost.danger { color: var(--red); }
.btn.danger:hover { border-color: var(--red); background: transparent; }

input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), select, textarea {
  border-radius: 0; background: var(--field); border: 1px solid var(--rule-2); box-shadow: none;
}
input:focus, select:focus, textarea:focus { border-color: var(--neon); box-shadow: 0 0 0 1px var(--neon); }

/* --- sheets: a hairline, no lift --------------------------------------- */
.card { border-radius: 0; background: var(--sheet); border: 1px solid var(--rule); box-shadow: none; }
a.card:hover { transform: none; box-shadow: none; border-color: var(--neon); }
.card.ready { border: 1px solid var(--neon); box-shadow: none; }
.notice, .flash { border-radius: 0; box-shadow: none; }
.badge { border-radius: 0; box-shadow: none; text-transform: none; letter-spacing: 0; font-family: var(--mono); }
.badge.stamp { transform: none; }
.empty { border-radius: 0; border: 1px solid var(--rule); background: transparent; box-shadow: none; text-align: left; padding: var(--s-7); }
.empty::before { display: none; }
.empty p { margin-left: 0; }

/* --- the feed (home) ---------------------------------------------------- */
main.feed-page { max-width: var(--shell-wide); }
.feed-layout { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: var(--s-9); align-items: start; }
.feed-side { position: sticky; top: var(--s-6); border-left: 1px solid var(--rule); padding-left: var(--s-6); }
.side-block + .side-block { margin-top: var(--s-7); padding-top: var(--s-6); border-top: 1px solid var(--rule); }
.side-block h3 { font-size: var(--t-lg); font-style: italic; margin: 0 0 var(--s-3); }
.side-block p { color: var(--ink-2); font-size: var(--t-sm); }
.side-actions { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; }
.side-links { list-style: none; margin: 0; padding: 0; }
.side-links li { border-bottom: 1px solid var(--rule); }
.side-links a { display: block; padding: var(--s-2) 0; color: var(--ink); text-decoration: none; font-weight: 500; }
.side-links a:hover { color: var(--pink); padding-left: var(--s-1); }
.side-fine p { font-size: var(--t-xs); color: var(--ink-3); }

.desk { margin-bottom: var(--s-10); }
.desk-head, .library-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; border-bottom: 1px solid var(--rule-2); padding-bottom: var(--s-3); margin-bottom: var(--s-5); }
.desk-head h1 { margin: 0; }
.library-head h2 { margin: 0; font-size: clamp(1.9rem, 3.4vw, 2.75rem); font-style: italic; }
.library-note { margin: 0; color: var(--ink-3); font-size: var(--t-sm); max-width: 44ch; }
.link-more { font-weight: 600; color: var(--neon-ink); text-decoration: none; white-space: nowrap; }
.link-more:hover { color: var(--pink); text-decoration: underline; }

.new-story {
  display: flex; gap: var(--s-5); align-items: center; padding: var(--s-5) var(--s-6);
  background: var(--plum-deep); border: 1px solid var(--neon); color: var(--ink); text-decoration: none;
}
.new-story:hover { background: var(--neon); color: var(--on-accent); }
.new-story:hover .new-story-sub, .new-story:hover .new-story-plus { color: var(--on-accent); }
.new-story strong { display: block; font-family: var(--display); font-size: var(--t-xl); font-weight: 700; line-height: 1.1; }
.new-story-sub { display: block; color: var(--ink-2); font-size: var(--t-sm); margin-top: var(--s-1); }
.new-story-plus { font-family: var(--display); font-size: 3.5rem; line-height: 0.8; font-weight: 400; color: var(--pink); }

.desk-list { list-style: none; margin: 0; padding: 0; }
.desk-list li { border-bottom: 1px solid var(--rule); }
.desk-item { display: grid; grid-template-columns: 48px minmax(0, 1fr) auto; gap: var(--s-4); align-items: center; padding: var(--s-4) 0; color: var(--ink); text-decoration: none; }
.desk-item:hover { color: var(--ink); background: var(--sheet); }
.desk-item:hover .desk-title { color: var(--neon-ink); }
.desk-initial { font-family: var(--display); font-style: italic; font-weight: 800; font-size: 2.25rem; line-height: 1; color: var(--pink); text-align: center; }
.desk-title { display: block; font-family: var(--display); font-weight: 600; font-size: var(--t-lg); line-height: 1.2; }
.desk-meta { display: block; color: var(--ink-3); font-size: var(--t-sm); }
.status-tag { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); display: inline-flex; gap: var(--s-1); align-items: center; padding-right: var(--s-2); }
.status-tag.status-complete { color: var(--pink); }
.status-tag.status-failed { color: var(--red); }
.status-tag.status-generating, .status-tag.status-extracting { color: var(--neon-ink); }

/* --- library grid: an editorial spread, not a row of identical cards --- */
.market-bar { border-bottom: 0; padding-bottom: 0; margin-bottom: var(--s-5); gap: var(--s-4); }
.tabs { gap: var(--s-5); }
.tab { border: 0; border-radius: 0; background: none; padding: 0 0 var(--s-1); color: var(--ink-3); font-weight: 600; text-transform: none; letter-spacing: 0; border-bottom: 2px solid transparent; }
.tab:hover { background: none; color: var(--ink); }
.tab[aria-current="page"] { background: none; color: var(--ink); border-bottom-color: var(--neon); }

.shelf:has(.market-book) { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-8) var(--s-6); }
.market-book {
  display: block; padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none; transform: none;
}
.market-book:hover { transform: none; box-shadow: none; border: 0; }
.market-book .cover { aspect-ratio: 4 / 5; border-radius: 0; border: 0; box-shadow: none; margin-bottom: var(--s-3); background: var(--plum-deep); }
.market-book .cover .band { display: none; }
.market-book:hover .cover img { transform: none; opacity: 0.88; }
.market-book h3 { font-size: var(--t-lg); line-height: 1.15; }
.market-book h3 a { color: var(--ink); }
.market-book h3 a:hover { color: var(--neon-ink); text-decoration: underline; text-decoration-color: var(--neon); }
.market-book .byline { font-family: var(--display); font-style: italic; color: var(--ink-2); }
.market-book .facts { font-family: var(--sans); font-size: var(--t-xs); color: var(--ink-3); letter-spacing: 0; }
/* The lead story spans two columns, like the top item on a front page. */
.shelf:has(.market-book) > .market-book:first-child { grid-column: span 2; }
.shelf:has(.market-book) > .market-book:first-child .cover { aspect-ratio: 16 / 10; }
.shelf:has(.market-book) > .market-book:first-child h3 { font-size: var(--t-2xl); }

.cover { border-radius: 0; border: 0; box-shadow: none; }
a.cover:hover { transform: none; box-shadow: none; }
.vote { border-radius: 0; box-shadow: none; }
.vote:hover { box-shadow: none; border-color: var(--neon); }
.vote.up.on { background: var(--neon); }

/* Your stories list page */
.shelf > :nth-child(6n+1) .cover { --cover: var(--plum-deep); --cover-ink: var(--pink); }
.shelf > :nth-child(6n+2) .cover { --cover: var(--red); --cover-ink: var(--paper); }
.shelf > :nth-child(6n+3) .cover { --cover: var(--neon); --cover-ink: var(--paper); }
.shelf > :nth-child(6n+4) .cover { --cover: var(--pink); --cover-ink: var(--paper); }
.shelf > :nth-child(6n+5) .cover { --cover: var(--navy); --cover-ink: var(--neon-ink); }
.shelf > :nth-child(6n+6) .cover { --cover: var(--olive); --cover-ink: var(--pink); }
.cover .initial { font-family: var(--display); font-style: italic; font-weight: 800; }
.book:not(.market-book) { border-bottom: 1px solid var(--rule); padding-bottom: var(--s-5); }

/* --- reader -------------------------------------------------------------- */
.reader { font-family: var(--display); font-size: 1.1875rem; }
.reader h2 { font-style: italic; }
.reader h2::after { background: var(--neon); }
.reader p { font-family: var(--rd-family); line-height: var(--rd-lh); color: var(--ink); }
.reader h2 + p::first-letter, .reader h2 + figure + p::first-letter { color: var(--pink); font-family: var(--display); }
.reader figure img, .plate img { border-radius: 0; box-shadow: none; }
.reader figcaption { font-family: var(--sans); text-transform: none; letter-spacing: 0; color: var(--ink-3); }
.reader-bar { border-radius: 0; background: var(--paper); border-bottom: 1px solid var(--rule-2); box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
.reader-bar .title { font-family: var(--display); font-weight: 700; font-style: italic; }

/* --- steps: plain text trail -------------------------------------------- */
.steps .step .n { border-radius: 0; box-shadow: none; }
.steps .step.current .n { background: var(--neon); border-color: var(--neon); }

/* --- footer -------------------------------------------------------------- */
.site-footer { border-top: 3px solid var(--plum-deep); text-align: left; }
.footer-name em { font-family: var(--display); font-weight: 700; color: var(--ink); }
.footer-adult { text-align: left; margin: var(--s-2) 0; max-width: 70ch; }
.footer-legal { justify-content: flex-start; text-transform: none; letter-spacing: 0; }
.footer-legal a:hover { color: var(--pink); text-shadow: none; }

/* --- age gate: a printed card, not a glowing modal ----------------------- */
.age-gate { background: var(--paper-deep); -webkit-backdrop-filter: none; backdrop-filter: none; }
.age-gate .panel {
  text-align: left; border-radius: 0; border: 0; border-top: 6px solid var(--neon);
  background: var(--sheet); box-shadow: none; width: min(560px, 100%);
}
.gate-mark { font-family: var(--display); font-style: italic; font-weight: 800; font-size: 4.5rem; line-height: 0.9; color: var(--red); margin: 0 0 var(--s-4); }
.age-gate h2 { font-size: var(--t-2xl); font-style: italic; }
.age-gate .actions { justify-content: flex-start; }
.age-gate .btn.ghost { padding-left: var(--s-2); }

/* --- auth --------------------------------------------------------------- */
.choice { border-radius: 0; box-shadow: none; }
.receipt { border-radius: 0; box-shadow: none; }
.confirm-dialog { border-radius: 0; box-shadow: none; border: 1px solid var(--neon); }

@media (max-width: 960px) {
  .feed-layout { grid-template-columns: 1fr; gap: var(--s-7); }
  .feed-side { position: static; border-left: 0; padding-left: 0; border-top: 1px solid var(--rule); padding-top: var(--s-6); }
}
@media (max-width: 760px) {
  .shelf:has(.market-book) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .brand .word { font-size: 1.6rem; }
  .site-nav { gap: var(--s-4); }
}
@media (max-width: 420px) {
  .shelf:has(.market-book) > .market-book:first-child { grid-column: auto; }
}

/* v2 fixes */
.age-gate .gate-mark { color: var(--red); }
.aside-note { border-left: 0; border-top: 1px solid var(--rule-2); padding-left: 0; padding-top: var(--s-4); background: none; }
.site-nav .navlink { white-space: nowrap; }
@media (max-width: 760px) {
  .brand .word { display: inline; font-size: 1.6rem; }
}

/* The story title became an <h1> for heading structure; it keeps the look
   of the old <span>. */
.reader-bar h1.title { margin: 0; line-height: inherit; letter-spacing: inherit; display: inline; }

/* ============================================================= v3 ==== */
/* Display face: Big Shoulders Display -- tall, condensed, heavy; a late
   club poster rather than a literary imprint. Reading and UI: Archivo. */
h1, h2, h3, h4, .brand .word, .library-head h2, .side-block h3, .desk-title,
.desk-initial, .new-story strong, .gate-mark, .age-gate h2, .reader h2,
.reader-bar .title, .footer-name em, .overline, .page-head .eyebrow,
.cover .initial, .market-book .byline {
  font-family: var(--display); font-style: normal;
}
h1 { font-weight: 800; letter-spacing: -0.005em; line-height: 0.95; }
h2 { font-weight: 800; letter-spacing: 0; }
h3 { font-weight: 700; letter-spacing: 0.005em; }
.brand .word { font-weight: 900; font-size: 2.25rem; letter-spacing: 0.01em; }
.library-head h2 { font-size: clamp(2.2rem, 4vw, 3.25rem); }
.market-book h3 { font-size: 1.6rem; line-height: 1; }
.shelf:has(.market-book) > .market-book:first-child h3 { font-size: 2.6rem; }
.overline, .page-head .eyebrow { font-weight: 700; font-size: var(--t-lg); }
.reader, .reader p { font-family: var(--rd-family); font-size: var(--rd-size); line-height: var(--rd-lh); }
.reader p:first-of-type::first-line, .reader h2 + p::first-line, .reader h2 + figure + p::first-line { font-variant: normal; text-transform: none; letter-spacing: 0; }
.reader h2 + p::first-letter, .reader h2 + figure + p::first-letter { font-family: var(--display); font-weight: 900; }

/* --- chips --------------------------------------------------------------- */
.chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip {
  display: inline-flex; align-items: center; gap: var(--s-2); white-space: nowrap;
  padding: var(--s-1) var(--s-3); border: 1px solid var(--rule-2); border-radius: var(--r-full);
  background: var(--sheet); color: var(--ink-2); font-size: var(--t-sm); font-weight: 600; line-height: 1.4;
  text-decoration: none;
}
.chip:hover { color: var(--ink); border-color: var(--pink); background: var(--sheet-2); }
.chip.is-on { background: var(--neon); border-color: var(--neon); color: var(--on-accent); }
.chip.sm { font-size: var(--t-xs); padding: 0 var(--s-2); }
.chip-n { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-4); font-weight: 500; }
.chip.is-on .chip-n { color: var(--on-accent); opacity: 0.8; }
.chip-more { border-style: dashed; color: var(--neon-ink); }
.chips-lg .chip { font-size: var(--t-md); padding: var(--s-2) var(--s-4); }

.kink-bar { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: 0 0 var(--s-6); }
@media (max-width: 760px) {
  .kink-bar {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    margin-left: calc(var(--s-4) * -1); margin-right: calc(var(--s-4) * -1); padding: 0 var(--s-4) var(--s-1);
    -webkit-mask-image: linear-gradient(90deg, transparent 0, rgb(0 0 0) 16px, rgb(0 0 0) calc(100% - 24px), transparent);
            mask-image: linear-gradient(90deg, transparent 0, rgb(0 0 0) 16px, rgb(0 0 0) calc(100% - 24px), transparent);
  }
  .kink-bar::-webkit-scrollbar { display: none; }
}

.crumbs { font-size: var(--t-xs); color: var(--ink-3); margin-bottom: var(--s-4); }
.crumbs a { color: var(--ink-3); }
.tag-head { display: flex; align-items: baseline; gap: var(--s-5); flex-wrap: wrap; border-bottom: 3px solid var(--neon); padding-bottom: var(--s-3); margin-bottom: var(--s-3); }
.tag-head h1 { margin: 0; font-size: clamp(2.75rem, 7vw, 5rem); }
.tag-count { margin: 0; font-family: var(--mono); color: var(--pink); font-size: var(--t-sm); }
.tag-group { font-size: var(--t-xl); margin: var(--s-8) 0 var(--s-4); }
.feed-page > .library-note { margin-bottom: var(--s-5); }

/* --- muted credit ---------------------------------------------------------- */
.market-book .chips { margin-top: var(--s-2); }
.market-book .facts { font-size: var(--t-xs); color: var(--ink-4); }
.market-book .facts a { color: var(--ink-3); }
.story-head { margin: var(--s-6) 0 var(--s-8); }
.story-title { font-size: clamp(2.75rem, 7vw, 4.5rem); margin: var(--s-5) 0 var(--s-4); }
.story-head .chips { margin-bottom: var(--s-3); }
.story-byline { font-size: var(--t-xs); color: var(--ink-4); margin: 0; }
.story-byline a { color: var(--ink-3); }
.reader-source { margin-top: var(--s-8); padding-top: var(--s-3); border-top: 1px solid var(--rule); font-size: var(--t-xs); color: var(--ink-4); }
.reader-source p { margin: 0; }
.reader-source a { color: var(--ink-3); }

/* --- glass cover: text-only stories ------------------------------------------
   Three blurred colour shapes behind a frosted panel carrying the title.
   Palette (gc-p0..5) and layout (gc-l0..3) are classes; the exact shape
   positions come in as custom properties seeded from the story id. */
.market-book .cover:has(.glass-cover) { padding: 0; background: var(--paper-deep); }
.glass-cover {
  --c1: var(--neon);
  --c2: var(--pink);
  --c3: var(--plum-deep);
  --gx1: 20%;
  --gy1: 20%;
  --gx2: 60%;
  --gy2: 60%;
  --gr: 0deg;
  position: relative; display: block; width: 100%; height: 100%; min-height: 100%;
  overflow: hidden; background: var(--paper-deep); isolation: isolate;
}
.gc-p0 { --c1: var(--neon);      --c2: var(--pink);      --c3: var(--plum-deep); }
.gc-p1 { --c1: var(--red);       --c2: var(--neon);      --c3: var(--navy); }
.gc-p2 { --c1: var(--pink);      --c2: var(--plum-deep); --c3: var(--neon); }
.gc-p3 { --c1: var(--plum-deep); --c2: var(--red);       --c3: var(--pink); }
.gc-p4 { --c1: var(--neon);      --c2: var(--red);       --c3: var(--olive); }
.gc-p5 { --c1: var(--accent-2);  --c2: var(--navy);      --c3: var(--pink); }
.gc-blob { position: absolute; border-radius: 50%; filter: blur(28px); opacity: 0.9; z-index: 0; }
.gc-b1 { width: 70%; aspect-ratio: 1; left: calc(var(--gx1) - 25%); top: calc(var(--gy1) - 20%); background: var(--c1); }
.gc-b2 { width: 60%; aspect-ratio: 1; left: calc(var(--gx2) - 20%); top: calc(var(--gy2) - 15%); background: var(--c2); }
.gc-b3 { width: 90%; height: 40%; left: -10%; bottom: -15%; background: var(--c3); border-radius: 40%; transform: rotate(var(--gr)); }
.gc-l1 .gc-b3 { top: -15%; bottom: auto; }
.gc-l2 .gc-b1 { border-radius: 30% 70% 60% 40%; }
.gc-l3 .gc-b2 { width: 45%; border-radius: 20%; transform: rotate(var(--gr)); }
.gc-panel {
  position: absolute; z-index: 1; left: 10%; right: 10%; bottom: 10%;
  display: flex; flex-direction: column; gap: var(--s-2);
  padding: var(--s-4); border: 1px solid rgb(255 255 255 / 22%);
  background: rgb(18 10 24 / 38%);
  -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
}
.gc-l1 .gc-panel { top: 10%; bottom: auto; }
.gc-l2 .gc-panel { left: 8%; right: 22%; }
.gc-l3 .gc-panel { left: 18%; right: 8%; }
.gc-title {
  font-family: var(--display); font-weight: 900; color: var(--ink);
  font-size: clamp(1.4rem, 2.4vw, 2rem); line-height: 0.95; letter-spacing: 0.005em;
  overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}
.gc-kicker { font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-2); }
.shelf:has(.market-book) > .market-book:first-child .gc-title { font-size: clamp(2rem, 4vw, 3.25rem); }
.gc-hero { aspect-ratio: 21 / 8; height: auto; }
.gc-hero .gc-title { font-size: clamp(2rem, 5vw, 3.5rem); }
.gc-hero .gc-panel { left: 6%; right: auto; max-width: 70%; }
/* The title already sits on the hero cover; the h1 stays for structure but the
   cover carries it visually. */
.story-head:has(.gc-hero) .story-title { font-size: clamp(1.75rem, 3.5vw, 2.5rem); color: var(--ink-2); }
@media (max-width: 760px) {
  .gc-hero { aspect-ratio: 4 / 3; }
  .gc-hero .gc-panel { max-width: none; right: 6%; }
}
html.age-locked .glass-cover { filter: none; }

/* v3 fixes: the cover carries the title, so the text title under it is
   kept for structure but hidden from sight; grid items may shrink. */
.market-book:has(.glass-cover) h3 a { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.market-book:has(.glass-cover) h3 { min-height: 0; margin: 0; }
.story-head:has(.gc-hero) .story-title { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; margin: 0; }
.story-head:has(.gc-hero) .chips { margin-top: var(--s-4); }
.feed-main, .feed-layout > *, .library, .market-book { min-width: 0; }
.kink-bar { max-width: 100%; }
@media (max-width: 560px) {
  .shelf:has(.market-book) { grid-template-columns: minmax(0, 1fr); }
  .shelf:has(.market-book) > .market-book:first-child { grid-column: auto; }
  .shelf:has(.market-book) > .market-book:first-child .cover { aspect-ratio: 4 / 5; }
}

/* ======================================================= reader v4 == */
/* A wider reader: the text column follows the reader's own width setting
   (default ~860px), the cast sits beside it on wide screens. */

@font-face {
  font-family: "Fraunces";
  src: url("fonts/fraunces-latin-full-normal.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Fraunces";
  src: url("fonts/fraunces-latin-full-italic.woff2") format("woff2");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Atkinson Hyperlegible";
  src: url("fonts/atkinson-hyperlegible-latin-400-normal.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Atkinson Hyperlegible";
  src: url("fonts/atkinson-hyperlegible-latin-700-normal.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

:root {
  --rd-size: 19px;
  --rd-lh: 1.7;
  --rd-width: 1180px;
  --rd-cast: 300px;
  --rd-family: "Archivo", ui-sans-serif, system-ui, sans-serif;
}

.reader-layout { display: block; }
.reader-col { min-width: 0; }
.reader-layout .reader,
main.reading > .reader {
  max-width: var(--rd-width);
  font-size: var(--rd-size);
  line-height: var(--rd-lh);
  padding: var(--s-8) clamp(var(--s-5), 6vw, 96px) var(--s-10);
}
[data-rd-font="fraunces"] { --rd-family: "Fraunces", Georgia, "Times New Roman", serif; }
[data-rd-font="fraunces"] .reader { font-weight: 400; }
[data-rd-font="atkinson"] { --rd-family: "Atkinson Hyperlegible", Verdana, sans-serif; }
[data-rd-font="atkinson"] .reader { font-weight: 400; letter-spacing: 0.01em; word-spacing: 0.06em; }
[data-rd-font="atkinson"] .reader p { hyphens: manual; text-indent: 0; margin-bottom: 0.8em; }

/* The cast is a drawer on every screen, opened from the top bar. */
.reader-cast {
  position: fixed; top: 52px; right: 0; bottom: 0; z-index: 45;
  width: min(400px, 100vw); overflow: auto;
  transform: translateX(104%); transition: transform 180ms var(--ease), visibility 180ms;
  box-shadow: -16px 0 48px rgb(0 0 0 / 65%);
}
.reader-cast.open { transform: none; }
.reader-cast .cast-panel { min-height: 100%; border-top-width: 3px; }
.cast-panel .cast-more { display: none; }
.cast-panel.show-all .cast-more { display: block; }
.cast-show-more { margin-top: var(--s-3); width: 100%; }
@media (max-width: 640px) {
  main.reading { padding-left: 0; padding-right: 0; padding-top: 0; }
  main.reading .story-head, main.reading .chapter-nav,
  main.reading .donate-end, main.reading .comments { margin-left: var(--s-4); margin-right: var(--s-4); }
  .reader-layout .reader { border-left: 0; border-right: 0; box-shadow: none; padding: var(--s-6) var(--s-4) var(--s-9); }
  .reader-cast { top: 48px; }
  .rd-settings { top: 52px; right: 8px; left: 8px; width: auto; }
}

/* slim sticky top bar */
main.reading .reader-bar { padding: 6px 0; margin-bottom: var(--s-5); min-height: 0; }
main.reading .reader-bar .btn.sm { padding: 4px 10px; }
@media (max-width: 640px) {
  main.reading .reader-bar { padding: 6px var(--s-3); }
  main.reading .reader-bar .grow { display: none; }
  main.reading .reader-bar > .row { width: 100%; justify-content: space-between; }
}
.rd-chapters { position: relative; }
.rd-chapters summary { list-style: none; cursor: pointer; }
.rd-chapters summary::-webkit-details-marker { display: none; }
.rd-chapters-list {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 50;
  min-width: 240px; max-height: 60vh; overflow: auto; list-style: none; margin: 0; padding: 6px 0;
  background: var(--rd-c-120a1a); border: 1px solid var(--neon); box-shadow: 6px 6px 0 0 var(--crimson);
}
.rd-chapters-list a { display: flex; gap: 10px; padding: 8px 14px; color: var(--ink-2); text-decoration: none; white-space: nowrap; }
.rd-chapters-list a:hover { background: var(--rd-c-110919); color: var(--pink); }
.rd-chapters-list a[aria-current="page"] { color: var(--neon); font-weight: 700; }
.chapter-nav.top { margin: 0 auto var(--s-5); }
.chapter-nav .btn { font-size: 0.85rem; }

/* progress */
.rd-progress { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 60; background: transparent; pointer-events: none; }
.rd-progress span { display: block; height: 100%; width: 0; background: linear-gradient(90deg, var(--neon), var(--pink)); box-shadow: 0 0 10px var(--neon); transition: width 200ms linear; }

/* headings used in panels */
.rd-h {
  font-family: var(--display); font-weight: 700; font-size: 1.35rem;
  letter-spacing: 0.06em; text-transform: uppercase; margin: 0; color: var(--ink);
}

/* settings popover */
.rd-settings {
  position: fixed; top: 52px; right: max(12px, calc((100vw - 1260px) / 2 + 12px)); z-index: 50;
  width: min(320px, calc(100vw - 24px));
  background: var(--rd-c-120a1a); border: 1px solid var(--neon);
  box-shadow: 6px 6px 0 0 var(--crimson), 0 20px 60px rgb(0 0 0 / 70%);
  padding: var(--s-5); border-radius: 0;
}
.rd-settings-head { margin-bottom: var(--s-4); }
.rd-group { border: 0; padding: 0; margin: 0 0 var(--s-5); }
.rd-group legend, .rd-label {
  display: flex; justify-content: space-between; width: 100%;
  font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: var(--s-2);
}
.rd-label output { color: var(--pink); }
.rd-seg { display: flex; gap: 0; border: 1px solid var(--rule-2); }
.rd-opt { flex: 1; position: relative; cursor: pointer; }
.rd-opt + .rd-opt { border-left: 1px solid var(--rule-2); }
.rd-opt input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.rd-opt span { display: block; text-align: center; padding: 8px 6px; font-size: 0.82rem; color: var(--ink-2); }
.rd-opt input:checked + span { background: var(--neon); color: var(--rd-c-fff); box-shadow: inset 0 0 0 1px var(--rd-c-d48bff); }
.rd-opt input:focus-visible + span { outline: 2px solid var(--pink); outline-offset: -2px; }
.f-fraunces span { font-family: "Fraunces", Georgia, serif; }
.f-atkinson span { font-family: "Atkinson Hyperlegible", Verdana, sans-serif; }
.rd-settings input[type="range"] { width: 100%; accent-color: var(--neon); }
.rd-foot { border-top: 1px solid var(--rule-2); padding-top: var(--s-3); }

/* cast panel */
.cast-panel {
  background: var(--rd-c-110919); border: 1px solid var(--rule-2); border-top: 3px solid var(--neon);
  padding: var(--s-5); border-radius: 0;
}
.cast-head { margin-bottom: var(--s-2); }
.cast-hint { margin: 0 0 var(--s-4); }
.cast-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-4); }
.cast-item { border-left: 2px solid var(--crimson); padding-left: var(--s-3); }
.cast-name { font-family: var(--display); font-weight: 700; font-size: 1.15rem; letter-spacing: 0.02em; display: flex; gap: 8px; align-items: center; }
.cast-tag { font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--rd-c-fff); background: var(--crimson); padding: 1px 5px; }
.cast-role { font-size: 0.86rem; color: var(--ink-2); margin: 2px 0 4px; line-height: 1.45; }
.cast-ch { color: var(--pink); margin: 0 0 2px; }
.cast-aka { margin: 0 0 6px; }
.cast-rename input {
  width: 100%; background: var(--rd-c-0a0610); border: 1px solid var(--rule-2); color: var(--ink);
  padding: 6px 8px; font: inherit; font-size: 0.9rem; border-radius: 0;
}
.cast-rename input:focus { outline: none; border-color: var(--neon); box-shadow: 0 0 0 1px var(--neon); }
.cast-rename input:not(:placeholder-shown) { border-color: var(--pink); color: var(--pink); }
.cast-actions { margin-top: var(--s-4); }
.cast-status { margin: var(--s-3) 0 0; color: var(--ink-2); }
.cast-wait { display: flex; gap: 8px; align-items: center; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* chapters */
.story-chapter-now { font-family: var(--display); font-size: 1.2rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--pink); margin: var(--s-2) 0 0; }
.toc { max-width: var(--rd-width); margin: 0 auto var(--s-6); border: 1px solid var(--rule-2); background: var(--rd-c-110919); }
.toc summary { cursor: pointer; padding: var(--s-3) var(--s-4); font-family: var(--display); font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; }
.toc-list { margin: 0; padding: 0 var(--s-4) var(--s-4) calc(var(--s-4) + 1.2em); columns: 2 180px; }
.toc-list a { color: var(--ink-2); text-decoration: none; }
.toc-list a:hover { color: var(--pink); }
.toc-list a[aria-current="page"] { color: var(--neon); font-weight: 700; }
.chapter-nav { max-width: var(--rd-width); margin: var(--s-7) auto 0; gap: var(--s-4); }
.continue-banner { display: flex; gap: var(--s-3); align-items: center; margin-top: var(--s-4); }
.continue-banner[hidden] { display: none; }

/* donate at the end */
.donate-end {
  max-width: var(--rd-width); margin: var(--s-7) auto 0;
  display: flex; gap: var(--s-5); align-items: center;
  border: 1px solid var(--crimson); background: linear-gradient(120deg, var(--rd-c-1d0a22), var(--rd-c-2a0a1a));
  padding: var(--s-5) var(--s-6); box-shadow: 6px 6px 0 0 var(--neon);
}
.donate-end-mark { font-size: 2rem; color: var(--pink); text-shadow: 0 0 18px var(--pink); }
.donate-end-kicker { font-family: var(--display); font-weight: 800; font-size: 1.4rem; letter-spacing: 0.05em; text-transform: uppercase; margin: 0; }
.donate-end-line { margin: 2px 0 0; color: var(--ink-2); font-size: 0.95rem; }
@media (max-width: 560px) { .donate-end { flex-wrap: wrap; } }

/* continue reading row / saved versions */
.continue-row, .versions { margin: 0 0 var(--s-8); }
.continue-h { font-family: var(--display); font-weight: 800; font-size: 1.5rem; letter-spacing: 0.04em; text-transform: uppercase; margin: 0 0 var(--s-3); }
.continue-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--s-3); }
.continue-item { display: grid; gap: 4px; padding: var(--s-3) var(--s-4); border: 1px solid var(--rule-2); background: var(--rd-c-110919); text-decoration: none; color: var(--ink); }
.continue-item:hover { border-color: var(--neon); }
.continue-title { font-family: var(--display); font-weight: 700; font-size: 1.1rem; }
.continue-meta { font-size: 0.8rem; color: var(--pink); }
.continue-bar { height: 3px; background: var(--rule-2); }
.continue-bar span { display: block; height: 100%; background: var(--neon); }
.versions-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.version-item { display: flex; gap: var(--s-4); align-items: center; flex-wrap: wrap; padding: var(--s-3) var(--s-4); border: 1px solid var(--rule-2); background: var(--rd-c-110919); }
.version-title { font-family: var(--display); font-weight: 700; font-size: 1.1rem; color: var(--ink); }
.version-map { flex: 1; color: var(--ink-2); }

/* donate page */
.donate-shell { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--s-8); align-items: start; }
@media (max-width: 860px) { .donate-shell { grid-template-columns: 1fr; } }
.donate-card { border: 1px solid var(--neon); background: var(--rd-c-110919); padding: var(--s-6); box-shadow: 8px 8px 0 0 var(--crimson); }
.amounts { display: grid; grid-template-columns: repeat(5, 1fr); gap: 0; border: 1px solid var(--rule-2); margin-bottom: var(--s-5); }
@media (max-width: 560px) { .amounts { grid-template-columns: repeat(3, 1fr); } }
.amount { position: relative; }
.amount + .amount { border-left: 1px solid var(--rule-2); }
.amount input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.amount span { display: block; text-align: center; padding: 16px 4px; font-family: var(--display); font-weight: 800; font-size: 1.7rem; }
.amount input:checked + span { background: var(--neon); color: var(--rd-c-fff); }
.amount input:focus-visible + span { outline: 2px solid var(--pink); outline-offset: -2px; }
.donate-card label.field { display: block; margin-bottom: var(--s-4); }
.donate-card .field span { display: block; font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 6px; }
.donate-card input[type="number"], .donate-card textarea, .wallet-form input {
  width: 100%; background: var(--rd-c-0a0610); border: 1px solid var(--rule-2); color: var(--ink); padding: 10px 12px; font: inherit; border-radius: 0;
}
.donate-card textarea { min-height: 84px; resize: vertical; }
.donate-split { font-family: var(--mono); font-size: 0.8rem; color: var(--ink-2); }
.donate-aside { border-left: 3px solid var(--pink); padding-left: var(--s-5); }
.donate-aside h2 { font-family: var(--display); font-weight: 800; font-size: 2.2rem; line-height: 0.95; text-transform: uppercase; margin: 0 0 var(--s-3); }
.soon { display: inline-block; font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; background: var(--crimson); color: var(--rd-c-fff); padding: 4px 8px; }
.donate-card fieldset[disabled] { opacity: 0.55; }
.earn-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; border: 1px solid var(--rule-2); margin-bottom: var(--s-6); }
.earn-stats > div { padding: var(--s-4) var(--s-5); }
.earn-stats > div + div { border-left: 1px solid var(--rule-2); }
.earn-stats b { display: block; font-family: var(--display); font-size: 2.2rem; font-weight: 800; color: var(--ink); }
.earn-stats span { font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); }
.earn-stats .hot b { color: var(--pink); text-shadow: 0 0 16px rgb(255 95 174 / 40%); }
table.ledger { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
table.ledger th, table.ledger td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--rule-2); vertical-align: top; }
table.ledger th { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); }
.btn-donate { border-color: var(--pink) !important; color: var(--pink) !important; }

/* ---- light page: inside the reader only; the site stays dark ---- */
[data-rd-theme="light"] .reader-layout .reader,
[data-rd-theme="light"] main.reading > .reader {
  --ink: var(--rd-c-1d1420); --ink-2: var(--rd-c-4a3d4f); --ink-3: var(--rd-c-6f6274); --ink-4: var(--rd-c-b8acbb);
  background: var(--rd-c-f6f0e8); color: var(--rd-c-1d1420); border-color: var(--rd-c-d9cfc4);
  box-shadow: 8px 8px 0 -1px var(--rd-c-2a0c45), 8px 8px 0 0 var(--neon);
}
[data-rd-theme="light"] .reader h2 { color: var(--rd-c-4a3d4f); }
[data-rd-theme="light"] .reader h2 + p::first-letter { color: var(--rd-c-a01570); }
[data-rd-theme="light"] .reader figcaption { color: var(--rd-c-4a3d4f); }
[data-rd-theme="light"] .reader figcaption::before { color: var(--rd-c-8b1fd6); }
[data-rd-theme="light"] .reader::after { color: var(--rd-c-b8acbb); }
[data-rd-theme="light"] .reader figure img { background: var(--rd-c-fff); border-color: var(--rd-c-1d1420); }

/* v5 fixes: a closed drawer casts no shadow; the chapter menu fits a phone. */
.reader-cast:not(.open) { visibility: hidden; box-shadow: none; }
@media (max-width: 640px) {
  .rd-chapters { position: static; }
  .rd-chapters-list { position: fixed; left: 8px; right: 8px; top: 48px; min-width: 0; }
}

/* QA3: the story's cover is a banner, never the whole first screen. */
.story-head .glass-cover.gc-hero { aspect-ratio: auto; height: clamp(140px, 22vh, 220px); max-height: 220px; }
.reader-cast.open { visibility: visible; transition: transform 160ms var(--ease); touch-action: pan-y; }
