/* ============================================================
   The Left Is Dead, Long Live the Left - scrollytelling lander.
   Radical-editorial identity, echoing research/_styles/theme.css:
     · POSTER RED  - the glossy brand, the campaign claim.
     · LEDGER INK  - the receipt, the number, the cost.
   Riso / Jacobin / bell-hooks-book-cover. Warm near-black "ink"
   ground by default, warm-newsprint "paper" under light. Serif
   display + read stacks, mono for stamps. Self-contained, offline.
   All figures are hardcoded from ../data/canonical-dataset.md.
   ============================================================ */

:root {
  /* Type - system stacks only, no external fetch. */
  --display: "Iowan Old Style", "Palatino Linotype", "Book Antiqua",
    Palatino, "Hoefler Text", Georgia, "Times New Roman", serif;
  --read: Charter, "Iowan Old Style", "Palatino Linotype", Cambria,
    Georgia, "Times New Roman", serif;
  --stamp: ui-monospace, "SFMono-Regular", "Cascadia Mono", "Roboto Mono",
    Menlo, Consolas, "DejaVu Sans Mono", monospace;

  /* INK scheme (default): warm near-black ground, cream type. */
  --paper: #17130e;
  --surface: #211b14;
  --surface-2: #2a2218;
  --ink: #f0e8d7;
  --ink-strong: #fff7e9;
  --muted: #a99e8b;
  --faint: #6a6152;
  --rule: #3a3125;
  --poster: #ff5a45;
  --red: #ff6f5e;
  --red-deep: #c8331f;
  /* warm gold for the money-payoff line, legible on the dark ground */
  --gold: #e8b923;

  --gutter: clamp(1.25rem, 5vw, 4rem);
  --measure: 40rem;
}

/* PAPER scheme under light preference: warm newsprint, ink type. */
@media (prefers-color-scheme: light) {
  :root {
    --paper: #f4eee1;
    --surface: #faf5ea;
    --surface-2: #efe7d6;
    --ink: #1c1712;
    --ink-strong: #000;
    --muted: #6d6456;
    --faint: #a99d86;
    --rule: #d8cdb8;
    --poster: #e12d1b;
    --red: #c31f12;
    --red-deep: #9a1608;
    /* deeper gold so the money-payoff line stays legible on newsprint */
    --gold: #9a7407;
  }
}

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--read);
  font-size: clamp(16px, 1rem + 0.25vw, 19px);
  line-height: 1.62;
  overflow-x: hidden;
  background-image:
    radial-gradient(circle at 15% 8%, rgba(255,90,69,0.05), transparent 55%),
    radial-gradient(circle at 85% 92%, rgba(255,90,69,0.04), transparent 50%);
  background-attachment: fixed;
}

a {
  color: var(--red);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}
a:hover { color: var(--poster); }

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

h1, h2 {
  font-family: var(--display);
  font-weight: 800;
  letter-spacing: -0.015em;
}

/* ---------- scroll progress rail ---------- */
.rail {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  height: 3px;
  background: transparent;
  z-index: 100;
}
.rail__fill {
  display: block;
  height: 100%;
  width: 0%;
  background: var(--poster);
  box-shadow: 0 0 10px rgba(255,90,69,0.6);
  transition: width 0.05s linear;
}

/* ---------- shared stamp (mono, poster-red) ---------- */
.stamp {
  display: inline-block;
  font-family: var(--stamp);
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #fff;
  background: var(--poster);
  padding: 0.3em 0.7em;
}
.stamp--small {
  font-size: 0.62rem;
  vertical-align: middle;
  margin-right: 0.4rem;
}

/* ============================= HERO ============================= */
.hero {
  min-height: 100vh;
  min-height: 100svh;          /* full-screen opener; svh avoids mobile URL-bar jump */
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(3rem, 8vh, 6rem) var(--gutter);
  max-width: 1080px;
  margin: 0 auto;
  position: relative;
  border-bottom: 3px solid var(--poster);
}
.hero__stamp {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 2.5rem;
}
.hero__file {
  font-family: var(--stamp);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.hero__kicker {
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(1.15rem, 3vw, 1.7rem);
  color: var(--ink);
  margin: 0 0 0.4rem;
  max-width: 34rem;
  line-height: 1.3;
}
.hero__figure {
  font-size: clamp(4.5rem, 19vw, 12rem);
  line-height: 0.9;
  margin: 0.15em 0;
  color: var(--poster);
}
.hero__sub {
  font-size: clamp(1.02rem, 2vw, 1.2rem);
  color: var(--ink);
  max-width: var(--measure);
  margin: 1.4rem 0 0;
}
.scroll-cue {
  position: absolute;
  bottom: 1.8rem;
  left: var(--gutter);
  font-family: var(--stamp);
  font-size: 0.72rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
  animation: bob 2.4s ease-in-out infinite;
}
@keyframes bob {
  0%, 100% { transform: translateY(0); opacity: 0.55; }
  50%      { transform: translateY(6px); opacity: 1; }
}

/* ============================= IMAGERY ============================= */
/* Full-bleed photo openers. Real <img> elements so they render with JS off
   (graceful degradation); a dark gradient overlay keeps poster-red + cream
   type legible; text over photos is force-lit in BOTH themes so the light
   "paper" scheme never puts dark ink on a dark-overlaid image. */

/* ---- hero background photo ---- */
/* NOT overflow:hidden — that would clip the 100vw bg/scrim/fence back to the
   hero's max-width:1080 column (the "square photo" look). body{overflow-x:hidden}
   is the horizontal-scroll backstop, so the openers can breach to full-bleed. */
.hero.has-img { overflow: visible; }
.hero__bg {
  position: absolute;
  top: 0; height: 100%;
  left: 50%; transform: translateX(-50%);
  width: 100vw;
  object-fit: cover;
  object-position: 50% 30%;      /* pull the bright sky + glass pyramid into the visible band */
  z-index: 0;
  filter: saturate(0.9) contrast(1.04);
}
/* Two crossed scrims darken the bright City Hall photo enough for cream type:
   a LEFT→right wash builds a dark reading column where the copy sits while the
   pyramid stays visible on the right, and a TOP/bottom wash anchors the stamp
   row and the closing scroll-cue. Tuned darker than the old dusk-skyline scrim
   because this opener is a sunny, high-key image. */
.hero.has-img::after {
  content: "";
  position: absolute;
  top: 0; height: 100%;
  left: 50%; transform: translateX(-50%);
  width: 100vw;
  z-index: 1;
  /* Gentle, even darkening now that the card carries text legibility — this
     only sets mood and anchors the top stamp-less corners and bottom cue,
     letting the caged City Hall photo read much fuller than before. */
  background:
    linear-gradient(180deg,
      rgba(10,8,5,0.5) 0%,
      rgba(10,8,5,0.3) 40%,
      rgba(10,8,5,0.36) 66%,
      rgba(10,8,5,0.72) 100%);
}
.hero.has-img > *:not(.hero__bg) { position: relative; z-index: 3; }

/* ---- hero message card ----
   The whole opening message rides a dark, semi-opaque card so the type is
   legible no matter how bright the photo behind it gets. It stays translucent
   and softly blurs what's under it, so the fence and skyline still ghost through
   (the message is still "behind the wire") while the busy diagonal mesh under
   the words is muted. Matches the site card language: surface + hairline rule +
   poster-red left edge, echoing the pulls and the closing. */
.hero.has-img .hero__card {
  align-self: flex-start;
  max-width: min(58rem, 94vw);
  background: rgba(14,11,7,0.82);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--poster);
  border-radius: 3px;
  padding: clamp(1.5rem, 3.5vw, 2.5rem) clamp(1.4rem, 3.5vw, 2.7rem);
  -webkit-backdrop-filter: blur(3px) saturate(0.92);
          backdrop-filter: blur(3px) saturate(0.92);
}
.hero.has-img .hero__card .hero__stamp { margin-bottom: 1.5rem; }
.hero.has-img .hero__card .hero__kicker { margin-top: 0; }
.hero.has-img .hero__card .hero__sub { margin-bottom: 0; }
/* the big figure stays on one line inside the card; the vw-clamp handles narrow */
.hero.has-img .hero__figure { white-space: nowrap; }

/* ---- chain-link cage over the hero ----
   The reader reads the city from the wrong side of the fence: a galvanized
   diamond mesh laid over the darkened skyline, so the whole page opens from
   behind the $225,000 of wire it is about. Two crossing repeating-linear
   gradients fake round wire (dark edge → bright core → dark edge) for a
   woven-steel sheen rather than a flat grid; a drop-shadow lifts it off the
   photo; a mask fades it out over the lower third so the body copy under it
   stays legible. Sits above the darkening ::after (z2) and below the type (z3).
   Pure CSS, no asset, renders with JS off. */
.hero.has-img::before {
  content: "";
  position: absolute;
  top: 0; height: 100%;
  left: 50%; transform: translateX(-50%);
  width: 100vw;
  z-index: 2;
  --pitch: 34px;                 /* diamond pitch; smaller = tighter, more caged */
  background-image:
    repeating-linear-gradient(45deg,
      transparent 0 calc(var(--pitch) - 4px),
      rgba(6,7,9,0.7)       calc(var(--pitch) - 4px) calc(var(--pitch) - 3px),
      rgba(228,233,238,0.7) calc(var(--pitch) - 3px) calc(var(--pitch) - 1px),
      rgba(6,7,9,0.7)       calc(var(--pitch) - 1px) var(--pitch)),
    repeating-linear-gradient(-45deg,
      transparent 0 calc(var(--pitch) - 4px),
      rgba(6,7,9,0.7)       calc(var(--pitch) - 4px) calc(var(--pitch) - 3px),
      rgba(228,233,238,0.7) calc(var(--pitch) - 3px) calc(var(--pitch) - 1px),
      rgba(6,7,9,0.7)       calc(var(--pitch) - 1px) var(--pitch));
  filter: drop-shadow(0 1px 1px rgba(0,0,0,0.55));
  opacity: 0.7;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 42%, rgba(0,0,0,0.4) 74%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 0%, #000 42%, rgba(0,0,0,0.4) 74%, transparent 100%);
  pointer-events: none;
}
/* the reveal fade never touches ::before, so the cage is present from first paint */
/* force-lit hero type over the photo, theme-independent. Each line also carries
   its own text-shadow so it stays legible where it drifts over the brighter,
   less-scrimmed right side of the image — local per-glyph contrast, so we don't
   have to crush the whole photo darker to make the words read. */
.hero.has-img .hero__kicker,
.hero.has-img .hero__sub { color: #f2ead9; text-shadow: 0 1px 3px rgba(0,0,0,0.9), 0 0 20px rgba(0,0,0,0.55); }
.hero.has-img .hero__file { color: #d8ccb6; text-shadow: 0 1px 2px rgba(0,0,0,0.9); }
.hero.has-img .hero__figure { color: #ff5a45; text-shadow: 0 2px 26px rgba(0,0,0,0.55), 0 1px 2px rgba(0,0,0,0.65); }
.hero.has-img .scroll-cue { color: #d8ccb6; text-shadow: 0 1px 2px rgba(0,0,0,0.9); }
.hero__credit {
  align-self: flex-start;
  margin-top: 1.6rem;
  font-family: var(--stamp);
  font-size: 0.6rem;
  letter-spacing: 0.03em;
  color: #d8ccb6;
  text-shadow: 0 1px 3px rgba(0,0,0,0.8);
}

/* ---- per-act full-bleed opener ---- */
.act-hero {
  position: relative;
  left: 50%; transform: translateX(-50%);
  width: 100vw;
  margin: -5.5rem 0 2.6rem;
  height: clamp(220px, 38vh, 400px);
  overflow: hidden;
  border-bottom: 3px solid var(--poster);
}
.act-hero img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center;
  filter: saturate(0.8) contrast(1.04);
}
.act-hero::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(
    180deg,
    rgba(18,14,10,0.34) 0%,
    rgba(18,14,10,0.12) 46%,
    rgba(18,14,10,0.86) 100%
  );
}
.act-hero figcaption {
  position: absolute;
  right: 0.85rem; bottom: 0.7rem;
  z-index: 2;
  max-width: min(92vw, 34rem);
  font-family: var(--stamp);
  font-size: 0.58rem;
  letter-spacing: 0.02em;
  line-height: 1.35;
  text-align: right;
  color: #ece3d1;
  background: rgba(18,14,10,0.5);
  padding: 0.3em 0.6em;
  border-radius: 2px;
}
/* the reveal transform would clip a 100vw breakout element sideways;
   over-ride it to a plain fade so the full-bleed openers never shift x. */
.js-reveal .act-hero[data-reveal],
.js-reveal .hero__bg[data-reveal] { transform: translateX(-50%); }
.js-reveal .act-hero[data-reveal].is-visible { transform: translateX(-50%); }

/* ============================= ACTS ============================= */
.act {
  max-width: 1080px;
  margin: 0 auto;
  padding: 5.5rem var(--gutter) 3rem;
}
.act__marker {
  display: flex;
  align-items: baseline;
  gap: 1.1rem;
  margin-bottom: 2rem;
  border-top: 3px solid var(--poster);
  padding-top: 1rem;
}
.act__num {
  font-family: var(--stamp);
  font-size: clamp(1.4rem, 4vw, 2.2rem);
  font-weight: 700;
  color: var(--poster);
  line-height: 1;
}
.act__title {
  font-size: clamp(1.9rem, 5.5vw, 3.2rem);
  line-height: 1.02;
  margin: 0;
}

/* ---------- prose panels ---------- */
.panel {
  max-width: var(--measure);
  margin: 2.2rem 0;
}
.panel p { margin: 0 0 1.1rem; }
.panel p:last-child { margin-bottom: 0; }

.panel--pull {
  max-width: 32rem;
  border-left: 4px solid var(--poster);
  padding-left: 1.4rem;
}
.pull {
  font-family: var(--display);
  font-size: clamp(1.35rem, 3.6vw, 2rem);
  line-height: 1.24;
  color: var(--ink);
}

/* honest-complication note card */
.panel--note {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--ink);
  padding: 1.5rem 1.6rem;
  border-radius: 2px;
}
.note__head {
  font-family: var(--stamp);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.7rem !important;
}
/* the pulled climax of a note beat - lands last and big (Act II homicide) */
.note__climax {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(1.35rem, 3.3vw, 2.15rem);
  line-height: 1.14;
  letter-spacing: -0.01em;
  color: var(--ink-strong);
  text-wrap: balance;
  margin: 1.2rem 0 0 !important;
}

/* ---------- two-up quote cards ---------- */
.twoup {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin: 2.5rem 0;
}
.quote {
  margin: 0;
  padding: 1.7rem 1.4rem 1.3rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--poster);
  position: relative;
}
.quote--dissenter { border-top-color: var(--ink); }
.quote__tag {
  font-family: var(--stamp);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--poster);
}
.quote--dissenter .quote__tag { color: var(--muted); }
.quote blockquote {
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(1.05rem, 2.4vw, 1.3rem);
  line-height: 1.3;
  color: var(--ink);
  margin: 0.7rem 0 0.8rem;
}
.quote figcaption {
  font-family: var(--stamp);
  font-size: 0.72rem;
  color: var(--muted);
}

/* ============================= CHARTS ============================= */
.chart-block { margin: 2.8rem 0; }
.chart-block__cap {
  font-size: 0.95rem;
  color: var(--ink);
  margin-bottom: 0.9rem;
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  flex-wrap: wrap;
}
.chart-block__label {
  font-family: var(--stamp);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #fff;
  background: var(--poster);
  padding: 0.2em 0.55em;
}
.chart-block__note {
  font-size: 0.82rem;
  color: var(--muted);
  max-width: var(--measure);
  margin: 0.9rem 0 0;
}
.chart {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 1.2rem;
  overflow: hidden;
}
.chart svg { display: block; width: 100%; height: auto; }

/* SVG element styling (charts drawn by main.js) */
.grid-line { stroke: var(--rule); stroke-width: 1; stroke-dasharray: 2 4; }
.axis-label { fill: var(--muted); font-family: var(--stamp); font-size: 12px; }
.series-area { fill: rgba(255,90,69,0.12); }
.series-line { fill: none; stroke: var(--poster); stroke-width: 3; }
.series-dot { fill: var(--paper); stroke: var(--poster); stroke-width: 2.5; }
.series-dot--anchor { fill: var(--poster); stroke: var(--poster); }
.point-label { fill: var(--ink); font-family: var(--stamp); font-size: 12px; font-weight: 700; }
.callout-line { stroke: var(--poster); stroke-width: 1; stroke-dasharray: 3 3; }

.bar-track { fill: rgba(255,255,255,0.04); }
.bar-fill { fill: var(--faint); }
.bar-fill--hot { fill: var(--poster); }
.bar-fill--ink { fill: var(--ink); }
.bar-label { fill: var(--ink); font-family: var(--stamp); font-size: 13px; }
.bar-value { fill: var(--muted); font-family: var(--stamp); font-size: 12px; }
.bar-value--in { fill: #fff; font-family: var(--stamp); font-size: 13px; font-weight: 700; }
.bar-mult { fill: var(--poster); font-family: var(--stamp); font-size: 13px; font-weight: 700; }

/* animated draw-in - armed only when JS is present (progressive enhancement) */
.js-reveal .chart .draw { opacity: 0; transition: opacity 0.7s ease; }
.js-reveal .chart.is-drawn .draw { opacity: 1; }

/* ---------- five small multiples ---------- */
.smalls {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem;
  margin: 2.4rem 0;
}
.sm {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--poster);
  border-radius: 3px;
  padding: 1.1rem 1.2rem 1.2rem;
}
.sm__title {
  font-family: var(--display);
  font-weight: 800;
  font-size: 1.1rem;
  color: var(--ink-strong);
  margin: 0 0 0.15rem;
}
.sm__delta {
  font-family: var(--stamp);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  color: var(--poster);
  margin: 0 0 0.8rem;
}
.sm .chart {
  padding: 0.4rem 0.2rem 0;
  background: none;
  border: none;
}
.sm__note {
  font-size: 0.72rem;
  color: var(--muted);
  margin: 0.7rem 0 0;
  line-height: 1.45;
}
.sm-line { fill: none; stroke: var(--poster); stroke-width: 2.5; }
.sm-dot { fill: var(--poster); }
.sm-bar { fill: var(--poster); }
.sm-bar--zero { fill: var(--faint); }
.sm-lab { fill: var(--muted); font-family: var(--stamp); font-size: 11px; }
.sm-val { fill: var(--ink); font-family: var(--stamp); font-size: 11px; font-weight: 700; }

/* ============================= ROLL-CALL ============================= */
.rollcall {
  list-style: none;
  counter-reset: rc;
  margin: 2.2rem 0;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 3px;
  overflow: hidden;
  background: var(--surface);
}
.rc {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 1rem 1.4rem;
  align-items: center;
  padding: 1.1rem 1.4rem;
  border-bottom: 1px solid var(--rule);
}
.rc:last-child { border-bottom: 0; }
.rc__motion {
  margin: 0 0 0.25rem;
  font-weight: 700;
  color: var(--ink-strong);
  font-size: 1.02rem;
}
.rc__date {
  margin: 0;
  font-family: var(--stamp);
  font-size: 0.72rem;
  color: var(--muted);
}
.rc__result {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.35rem;
  white-space: nowrap;
}
.rc__vote {
  font-family: var(--stamp);
  font-size: 0.78rem;
  color: var(--ink);
}
.rc__outcome {
  font-family: var(--stamp);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.2em 0.6em;
  border: 1px solid currentColor;
  border-radius: 2px;
}
.rc__outcome--lose { color: var(--red); }

/* the hinge - the YES that funds what he opposed */
.panel--hinge {
  max-width: var(--measure);
  background: var(--surface-2);
  border: 2px solid var(--poster);
  border-radius: 3px;
  padding: 1.6rem 1.7rem;
  margin: 1.6rem 0 2.4rem;
}
.hinge__label {
  font-family: var(--stamp);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--poster);
  margin: 0 0 0.7rem !important;
}
.hinge__motion {
  font-family: var(--display);
  font-size: clamp(1.15rem, 2.6vw, 1.5rem);
  line-height: 1.25;
  color: var(--ink-strong);
  margin: 0 0 0.7rem !important;
}
.hinge__vote {
  font-family: var(--stamp);
  font-size: 1rem;
  color: var(--ink);
  margin: 0 !important;
}
.hinge__vote strong { color: var(--poster); }

/* ============================= BODY COUNT (Act V) ============================= */
.bodycount {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 1rem;
  margin: 2.4rem 0;
}
.bc {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--poster);
  border-radius: 3px;
  padding: 1.3rem 1.25rem 1.35rem;
}
.bc__num {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(2.2rem, 6vw, 3.1rem);
  line-height: 0.92;
  letter-spacing: -0.02em;
  color: var(--poster);
  margin: 0;
}
.bc__label {
  font-size: 0.9rem;
  color: var(--ink);
  margin: 0.6rem 0 0;
  line-height: 1.35;
}
.bc__sub {
  font-family: var(--stamp);
  font-size: 0.62rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0.45rem 0 0;
  line-height: 1.4;
}

/* the promoted thesis - Act V emotional climax, biggest text on the page */
.panel--thesis {
  max-width: 56rem;
  margin: 3.4rem auto 3.2rem;
}
.thesis {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(2rem, 6.6vw, 4.3rem);
  line-height: 1.04;
  letter-spacing: -0.022em;
  color: var(--poster);
  margin: 0;
  text-wrap: balance;
}

/* ============================= CLOSING ============================= */
/* One continuous poster-red rule carries the whole closing, so the pulled
   "Conviction wins. The costume loses." punch reads as part of it. */
.panel--closing {
  max-width: var(--measure);
  margin-top: 3rem;
  border-left: 4px solid var(--poster);
  padding-left: 1.5rem;
}
.closing {
  font-family: var(--display);
  font-size: clamp(1.4rem, 3.6vw, 2.1rem);
  line-height: 1.28;
  color: var(--ink-strong);
}
/* the pulled close - the biggest beat of the whole page after the thesis */
.closing__punch {
  font-weight: 800;
  font-size: clamp(1.9rem, 5.6vw, 3.3rem);
  line-height: 1.06;
  letter-spacing: -0.02em;
  color: var(--poster);
  text-wrap: balance;
}
.closing__coda { color: var(--poster); font-style: italic; }

/* ============================= SOURCES ============================= */
.sources {
  max-width: 1080px;
  margin: 4rem auto 0;
  padding: 3rem var(--gutter) 5rem;
  border-top: 1px solid var(--rule);
}
.sources__head {
  font-size: 1rem;
  margin: 0 0 1rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.sources__body { color: var(--muted); max-width: var(--measure); margin: 0 0 1.4rem; font-size: 0.92rem; }
.sources__cta { margin: 0 0 1.75rem; font-size: 1.02rem; }
.sources__cta a { font-weight: 700; }
.sources__group-h {
  font-family: var(--stamp);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--poster);
  font-weight: 700;
  margin: 1.2rem 0 0.5rem;
}
.sources__list {
  list-style: none;
  padding: 0;
  margin: 0 0 1.5rem;
  font-size: 0.9rem;
}
.sources__list li {
  padding: 0.4rem 0;
  border-bottom: 1px dashed var(--rule);
  color: var(--muted);
}
.sources__list a { font-weight: 700; }
.sources__caveat {
  font-size: 0.8rem;
  color: var(--faint);
  max-width: var(--measure);
  margin: 0;
  line-height: 1.55;
}

/* ============================= SCROLL REVEAL ============================= */
/* Progressive enhancement: content is fully visible by default. The reveal
   start-state (opacity:0) is armed only once JS adds `.js-reveal` to <html>,
   so with JS blocked or failed the entire story still renders at full opacity. */
.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.7, 0.2, 1);
  will-change: opacity, transform;
}
.js-reveal [data-reveal].is-visible { opacity: 1; transform: none; }

/* ---------- scroll-grow emphasis ---------- */
/* Designated climactic lines grow as they scroll to their hit, kept crisp.
   main.js renders each line natively at its peak font-size and writes an inline
   transform:scale() that starts DOWN near base size and animates up to 1.0 at
   the hit - downscaling stays sharp where upscaling would blur, and the real
   peak font-size makes the block wrap in-column instead of spilling. Left
   origin matches the left-aligned pulls and holds the left edge fixed; the body
   is overflow-x:hidden as a final backstop. Armed only when the engine runs
   (JS on, motion allowed); with JS off or motion reduced no inline size or
   scale is written, so the line renders at its normal base size, fully legible. */
.grow-on-scroll { transform-origin: left center; }
.js-reveal .grow-on-scroll { will-change: transform; }
/* Inline grow targets (e.g. a bold phrase mid-sentence) need display
   :inline-block for the transform to apply at all, and a centre origin so
   the phrase breathes in place instead of shoving the line leftward. */
strong.grow-on-scroll { display: inline-block; transform-origin: center; }

/* the money-payoff line, lifted to gold so it stands off the poster-red field */
.money-gold { color: var(--gold); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js-reveal [data-reveal] { opacity: 1; transform: none; transition: none; }
  .scroll-cue { animation: none; }
  .js-reveal .chart .draw { opacity: 1; transition: none; }
  .grow-on-scroll { transform: none !important; }
}

/* ============================= RESPONSIVE ============================= */
@media (max-width: 640px) {
  .twoup { grid-template-columns: 1fr; }
  .rc {
    grid-template-columns: 1fr;
    gap: 0.6rem;
  }
  .rc__result { align-items: flex-start; flex-direction: row; gap: 0.7rem; }
  .pull { font-size: 1.3rem; }
}
