/* ============================================================
   AGAIN — styles.css
   Register: restrained newsprint editorial. One accent colour
   (--breach) reserved exclusively for compromised material.
   ============================================================ */

:root {
  --paper: #f6f2ea;
  --paper-deep: #efe9dc;
  --ink: #1c1813;
  --ink-soft: #6b6257;
  --ink-faint: #a39a8c;
  --rule: #d9d1c0;
  --breach: #b23a1e;        /* THE accent. Leak / breach material only. */
  --breach-deep: #8f2e17;
  --breach-tint: #f3e0d8;
  --sent-bg: #ece5d6;       /* sentiment layer — visibly different register */
  --sent-ink: #57503f;
  --mark-bg: rgba(176, 137, 74, 0.5);  /* highlighter band — a warm brown,
                                          distinct from the reserved --breach */
  --serif: "Newsreader", Georgia, "Times New Roman", serif;
  --sans: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --measure: 40rem;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 1.14rem;
  line-height: 1.72;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Fine paper grain, barely there. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 40;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

h1, h2, h3 { font-weight: 500; line-height: 1.15; margin: 0; }

p { margin: 0 0 1.2em; }

em { font-style: italic; }
strong { font-weight: 600; }

/* Highlighter mark for load-bearing numbers and lines — a warm brown band
   sitting behind the lower part of the text, like a marker stroke. Used
   sparingly (roughly one per act); never on the reserved --breach material. */
.mark {
  background-image: linear-gradient(to top, var(--mark-bg) 0.44em, transparent 0.44em);
  background-repeat: no-repeat;
  padding: 0 0.06em 0.02em;
}

/* ------------------------------------------------------------- title card */
.titlecard {
  min-height: 92vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: 4rem 1.5rem 2rem;
  border-bottom: 1px solid var(--rule);
}

/* Sits under the masthead, and carries the rule that used to hang off it. */
.subhead {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0;
}

.subhead::after {
  content: "";
  display: block;
  width: 3.2rem;
  height: 3px;
  background: var(--ink);
  margin: 2.2rem auto 2.4rem;
}

.masthead {
  font-family: var(--serif);
  font-optical-sizing: auto;
  font-size: clamp(3rem, 11vw, 7rem);
  line-height: 1.02;
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.02em;
  margin: 0 0 1.1rem;
  max-width: 14ch;
}

.deck {
  max-width: 34rem;
  font-size: clamp(1.12rem, 2.6vw, 1.3rem);
  line-height: 1.6;
  margin-bottom: 0.9rem;
}

.deck--quiet {
  font-size: 0.98rem;
  color: var(--ink-soft);
  font-style: italic;
}

.scroll-cue {
  font-family: var(--sans);
  font-size: 0.75rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: auto;
  padding-top: 3rem;
  animation: cue 2.4s ease-in-out infinite;
}

@keyframes cue {
  0%, 100% { transform: translateY(0); opacity: 0.7; }
  50% { transform: translateY(6px); opacity: 1; }
}

/* ------------------------------------------------------------------- acts */
/* flow-root contains the floated margin notes. Without it a note that starts
   near the end of a section overflows the section box, and the next act's
   border-top is then drawn straight through the note. The section already
   has padding on both ends, so no margin was collapsing through it and
   establishing the context costs nothing else. */
.act { padding: 6rem 0 4rem; display: flow-root; }

.act + .act { border-top: 1px solid var(--rule); }

.act-head {
  max-width: var(--measure);
  margin: 0 auto 3rem;
  padding: 0 1.4rem;
}

.act-num {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.7rem;
}

.act-num::after {
  content: "";
  display: inline-block;
  vertical-align: middle;
  width: 2.6rem;
  height: 1px;
  background: var(--ink-soft);
  margin-left: 0.8rem;
}

.act-head h2 {
  font-size: clamp(2.3rem, 7vw, 3.6rem);
  font-weight: 500;
  letter-spacing: -0.015em;
}

/* Act IV gets a whisper of the breach register — a top rule, nothing more. */
.act--breach { border-top: 3px solid var(--breach) !important; }
.act--breach .act-num { color: var(--breach-deep); }
.act--breach .act-num::after { background: var(--breach-deep); }

.prose {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 1.4rem;
}

.act-open {
  font-size: clamp(1.3rem, 3.4vw, 1.55rem);
  line-height: 1.5;
  font-weight: 500;
}

.beat {
  font-size: clamp(1.18rem, 3vw, 1.4rem);
  line-height: 1.55;
  font-style: italic;
  font-weight: 500;
  margin: 2rem 0;
}

/* --------------------------------------------------------------- scrolly */
.scrolly { position: relative; margin: 3rem 0; }

.scrolly__figure {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

.js .scrolly__figure {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
}

.fig { margin: 0; width: 100%; max-width: 640px; }

.fig-fallback {
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--ink-soft);
  text-align: center;
  border: 1px solid var(--rule);
  padding: 1.4rem;
}

/* When JS builds the figure it removes .fig-fallback. */

.scrolly__steps { position: relative; z-index: 2; }

.js .scrolly__steps {
  margin-top: -88vh;          /* first step card rises over the figure */
  padding-bottom: 12vh;
}

.step {
  max-width: 30rem;
  margin: 0 auto 2rem;
  padding: 1.3rem 1.5rem;
  /* Fully opaque: at 0.94 the dot field and question grid showed through the
     type, which was hard to read on a texture. A card floating over a figure
     has to be a solid surface. */
  background: var(--paper);
  border: 1px solid var(--rule);
  box-shadow: 0 4px 20px rgba(28, 24, 19, 0.12);
  transition: opacity 0.4s ease;
}

.js .step { margin-bottom: 85vh; opacity: 0.3; }

.js .step:last-child { margin-bottom: 30vh; }

.js .step.is-active { opacity: 1; }

.step p { margin: 0; font-size: 1.05rem; line-height: 1.6; }

/* ------------------------------------------------------------- prologue */
.prologue { padding-top: 4.5rem; }

.photo-row {
  max-width: 46rem;
  margin: 2.5rem auto;
  padding: 0 1.4rem;
  display: grid;
  gap: 1.2rem;
  grid-template-columns: 1fr;
}

@media (min-width: 700px) {
  .photo-row { grid-template-columns: 1fr 1fr; }
}

.photo { margin: 0; }

.photo img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--rule);
}

.photo__frame {
  aspect-ratio: 3 / 2;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border: 1px dashed var(--ink-faint);
  background:
    repeating-linear-gradient(45deg, transparent 0 14px, rgba(28, 24, 19, 0.03) 14px 28px),
    var(--paper-deep);
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.photo__caption {
  font-family: var(--sans);
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--ink-soft);
  margin-top: 0.5rem;
}

.photo__credit {
  display: block;
  color: var(--ink-faint);
  font-size: 0.72rem;
  margin-top: 0.15rem;
}

/* ---------------------------------------- prologue: protest dated strip */
.strip {
  list-style: none;
  max-width: 42rem;
  margin: 1.6rem auto 2.5rem;
  padding: 0 1.4rem;
}

.strip__beat {
  display: grid;
  grid-template-columns: 4rem 1fr;
  gap: 0.9rem;
  padding: 0.85rem 0;
  border-top: 1px solid var(--rule);
}

.strip__beat:last-child { border-bottom: 1px solid var(--rule); }

.strip__date {
  font-family: var(--sans);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding-top: 0.2rem;
}

.strip__text { font-size: 0.98rem; line-height: 1.5; }

.strip__beat--now .strip__date { color: var(--ink); }
.strip__beat--now { background: var(--paper-deep); }

/* ------------------------------------------------- act 1: voices aside */
.voices {
  max-width: 46rem;
  margin: 2.5rem auto;
  padding: 0 1.4rem;
}

.voices__label {
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.9rem;
}

.voices__row {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}

@media (min-width: 700px) {
  .voices__row { grid-template-columns: 1fr 1fr; }
}

.voice {
  border: 1px solid var(--rule);
  border-top: 3px solid var(--ink-soft);
  background: #fbf8f2;
  padding: 1.1rem 1.25rem;
}

.voice__stance {
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.6rem;
}

.voice__quote {
  font-size: 1.02rem;
  line-height: 1.55;
  font-style: italic;
  margin-bottom: 0.7rem;
}

.voice__attr {
  font-family: var(--sans);
  font-size: 0.76rem;
  color: var(--ink-soft);
  margin: 0;
}

.voice__attr a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* three-up reactions (Act V); paraphrased reactions are not italic quotes */
@media (min-width: 860px) {
  .voices__row--three { grid-template-columns: 1fr 1fr 1fr; }
}

.voice--react .voice__quote { font-style: normal; }

/* ------------------------------------------------ act 4: accused by role */
.roles {
  max-width: 46rem;
  margin: 2rem auto 2.5rem;
  padding: 0 1.4rem;
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}

@media (min-width: 700px) {
  .roles { grid-template-columns: 1fr 1fr; }
}

.role {
  border: 1px solid var(--rule);
  border-left: 3px solid var(--breach);
  background: #fbf8f2;
  padding: 1rem 1.2rem;
}

.role__who {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--breach-deep);
  margin: 0 0 0.5rem;
}

.role__what { margin: 0; font-size: 0.96rem; line-height: 1.55; }

.role-caveat { font-size: 0.9rem; color: var(--ink-soft); }

/* ------------------------------------ act 2: a labelled off-the-line detour */
/* Real reporting that sits beside the main narrative rather than on it. Boxed
   so the reader recognises it as optional. Deliberately uses neither the
   reserved --breach accent nor the sentiment register. */
.detour {
  max-width: 34rem;
  margin: 3rem auto;
  padding: 1.3rem 1.5rem;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: rgba(217, 209, 192, 0.16);
}

.detour__label {
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.85rem;
}

.detour p:not(.detour__label) {
  margin: 0;
  font-size: 0.97rem;
  line-height: 1.65;
  color: var(--ink-soft);
}

/* -------------------------------------------- act 1: the case for/against */
.cases {
  max-width: 48rem;
  margin: 2.5rem auto;
  padding: 0 1.4rem;
}

.cases__disclaimer {
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1.1rem;
}

.cases__grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 0 1.4rem;
  align-items: start;
}

.cases__side { border-top: 3px solid var(--ink); padding-top: 1rem; }

.cases__label {
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.55rem;
}

.cases__thesis {
  font-size: 1.22rem;
  font-weight: 600;
  line-height: 1.3;
  margin: 0 0 0.85rem;
}

.cases__quote {
  margin: 0 0 0.8rem;
  font-style: italic;
  font-size: 1.02rem;
  line-height: 1.55;
}

.cases__body { margin: 0 0 0.8rem; font-size: 1rem; line-height: 1.6; }

.cases__attr {
  font-family: var(--sans);
  font-size: 0.76rem;
  color: var(--ink-soft);
  margin: 0;
}

.cases__attr a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

.cases__vs { align-self: center; justify-self: center; }

.cases__vs span {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--paper);
  font-style: italic;
  font-size: 1.05rem;
  color: var(--ink-faint);
}

@media (max-width: 640px) {
  .cases__grid { grid-template-columns: 1fr; gap: 0.4rem; }
  .cases__vs { justify-self: start; margin: 0.4rem 0; }
}

/* ------------------------------------------------------ act 1: dot field */
.funnel-flex {
  display: flex;
  align-items: center;
  gap: 1.6rem;
}

.funnel-flex svg { flex: 1 1 auto; min-width: 0; }

.funnel-stat {
  flex: 0 0 11.5rem;
  opacity: 0.25;
  transition: opacity 0.5s ease;
}

.funnel-stat.is-on { opacity: 1; }

.funnel-stat__headline {
  font-family: var(--sans);
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 0 0.2rem;
}

.funnel-stat__sub {
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.45;
  color: var(--ink);
  margin: 0 0 0.7rem;
}

.funnel-stat__rest {
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--ink-soft);
  margin: 0 0 0.6rem;
}

.funnel-stat__hedge {
  font-family: var(--sans);
  font-size: 0.7rem;
  line-height: 1.45;
  color: var(--ink-faint);
  margin: 0;
}

@media (max-width: 620px) {
  .funnel-flex { flex-direction: column; align-items: stretch; gap: 0.9rem; }
  .funnel-stat {
    flex: none;
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.15rem 0.9rem;
    align-items: baseline;
  }
  .funnel-stat__rest, .funnel-stat__hedge { grid-column: 1 / -1; }
}

/* While the dot field and the ratio panel sit side by side, a centred step
   card drifts right and lands on top of the panel. Pin the step column to the
   same 40rem band as the figure and left-align the cards over the dots, so a
   card overlays the field it narrates and never covers the panel. Below 620px
   the panel drops beneath the field, so there is nothing to clear. */
@media (min-width: 621px) {
  #scrolly-1 .scrolly__steps { max-width: 40rem; margin-left: auto; margin-right: auto; }
  #scrolly-1 .step { margin-left: 0; margin-right: auto; max-width: 22rem; }
}

/* Wide screens: the dot field and the question grid are the two strongest
   visuals, and a card travelling over their centre covers the very dots,
   caption, and grid rows the card is describing. Above 1160px both figures
   (640px) and a card fit side by side, so push the figure to the right and
   drop the cards into the left gutter beside it, never over it. This is the
   same treatment the map uses. Below 1160px the cards fall back to opaque
   overlays, which at least read cleanly now that they are solid. */
@media (min-width: 1160px) {
  #scrolly-1 .scrolly__figure,
  #scrolly-4 .scrolly__figure {
    justify-content: flex-end;
    padding-right: 4vw;
  }
  #scrolly-1 .scrolly__steps,
  #scrolly-4 .scrolly__steps {
    max-width: none;
    margin: 0;
  }
  #scrolly-1 .step,
  #scrolly-4 .step {
    margin-left: max(3vw, 1.4rem);
    margin-right: auto;
    max-width: 21rem;
  }
}

.funnel-svg { width: 100%; height: auto; display: block; }

.funnel-svg .dot {
  fill: var(--ink);
  opacity: 0.3;
  transition: opacity 0.6s ease, fill 0.6s ease;
}

.funnel-svg.stage-0 .dot { opacity: 0.34; }

.funnel-svg.stage-1 .dot,
.funnel-svg.stage-2 .dot { opacity: 0.13; }

.funnel-svg.stage-1 .dot--seat,
.funnel-svg.stage-2 .dot--seat { opacity: 1; }

.funnel-svg .seat-bracket { opacity: 0; transition: opacity 0.5s ease 0.3s; }
.funnel-svg.stage-1 .seat-bracket,
.funnel-svg.stage-2 .seat-bracket { opacity: 1; }

.fig-caption {
  font-family: var(--sans);
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--ink-soft);
  text-align: center;
  margin-top: 1rem;
  min-height: 3.6em;
}

/* ------------------------------------------------------ act 2: timeline */
.timeline-svg { width: 100%; height: auto; display: block; overflow: visible; }

.timeline-svg .tl-track { stroke: var(--rule); stroke-width: 2; }
.timeline-svg .tl-progress {
  stroke: var(--ink);
  stroke-width: 2;
  transition: stroke-dashoffset 0.5s ease;
}

.timeline-svg .tl-node {
  fill: var(--paper);
  stroke: var(--ink-soft);
  stroke-width: 2;
  transition: all 0.4s ease;
}

.timeline-svg .tl-node.is-past { fill: var(--ink); stroke: var(--ink); }

.timeline-svg .tl-node.is-active {
  fill: var(--ink);
  stroke: var(--ink);
  stroke-width: 2;
}

/* the leak node — the one place Act II touches the accent */
.timeline-svg .tl-node--breach.is-past,
.timeline-svg .tl-node--breach.is-active { fill: var(--breach); stroke: var(--breach); }

.tl-detail {
  text-align: center;
  margin-top: 1.2rem;
  min-height: 4.5em;
}

.tl-detail__date {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 0.3rem;
}

.tl-detail__label {
  font-family: var(--serif);
  font-size: 1.25rem;
  font-weight: 500;
  font-style: italic;
  margin: 0;
}

/* ------------------------------------------------- act 2: the heist clock */
.heist {
  max-width: 40rem;
  margin: 2.5rem auto 3rem;
  padding: 1.7rem 1.6rem 1.6rem;
  border: 1px solid var(--rule);
  background: #fbf8f2;
}

.heist__label {
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1.5rem;
}

.heist__clock { list-style: none; margin: 0; padding: 0; }

.heist__step {
  display: grid;
  grid-template-columns: 4.4rem 1fr;
  gap: 0 1rem;
}

.heist__time {
  font-family: var(--sans);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 1.02rem;
  color: var(--breach-deep);
  white-space: nowrap;
  text-align: right;
  line-height: 1.5;
}

.heist__body {
  position: relative;
  border-left: 2px solid var(--rule);
  padding: 0 0 1.5rem 1.2rem;
  font-size: 0.98rem;
  line-height: 1.55;
}

.heist__body::before {
  content: "";
  position: absolute;
  left: -6px;
  top: 0.5em;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--breach);
}

.heist__step:last-child .heist__body { padding-bottom: 0.2rem; }

.heist__punch {
  margin: 1.3rem 0 0;
  padding-top: 1.1rem;
  padding-left: 5.4rem;
  border-top: 1px solid var(--rule);
  font-size: 1.2rem;
  font-style: italic;
  font-weight: 600;
}

@media (max-width: 480px) {
  .heist__step { grid-template-columns: 3.6rem 1fr; gap: 0 0.7rem; }
  .heist__time { font-size: 0.92rem; }
  .heist__punch { padding-left: 0; }
}

/* ----------------------------------------------------------- act 2: fork */
.fork {
  max-width: var(--measure);
  margin: 4rem auto;
  padding: 2rem 1.4rem;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  background: var(--paper-deep);
}

.fork__label {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1rem;
}

.fork__question {
  font-size: 1.22rem;
  line-height: 1.55;
  font-weight: 500;
}

.fork__options {
  display: grid;
  gap: 0.9rem;
  grid-template-columns: 1fr;
  margin: 1.6rem 0;
}

@media (min-width: 640px) {
  .fork__options { grid-template-columns: 1fr 1fr; }
}

.fork__btn {
  font-family: var(--serif);
  text-align: left;
  padding: 1.1rem 1.2rem;
  background: var(--paper);
  border: 1px solid var(--ink-soft);
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}

.fork__btn:hover, .fork__btn:focus-visible { border-color: var(--ink); background: #fffdf8; }

.fork__btn[aria-pressed="true"] {
  border: 2px solid var(--ink);
  background: #fffdf8;
}

.fork__btn-title {
  display: block;
  font-size: 1.08rem;
  font-weight: 600;
  margin-bottom: 0.35rem;
}

.fork__btn-note {
  display: block;
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.fork__reveal { padding-top: 0.4rem; }
.fork__reveal[hidden] { display: none; }

/* The reader's own choice, read back to them before the Court's reasoning.
   Serif and larger than the body so it lands as the personal beat it is;
   --breach is reserved for compromised material, so this stays in ink. */
.fork__verdict {
  font-family: var(--serif);
  font-size: 1.16rem;
  line-height: 1.45;
  font-weight: 500;
  color: var(--ink);
  border-left: 3px solid var(--ink);
  padding-left: 1rem;
  margin: 0 0 1.4rem;
}
.fork__verdict[hidden] { display: none; }

.fork__reveal-label {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.8rem;
}

/* ---------------------------------------------------- act 3: reform cards */
.reforms {
  max-width: 46rem;
  margin: 3rem auto;
  padding: 0 1.4rem;
  display: grid;
  gap: 0.8rem;
  grid-template-columns: 1fr;
}

@media (min-width: 700px) {
  .reforms { grid-template-columns: 1fr 1fr; }
}

.card {
  border: 1px solid var(--rule);
  background: #fbf8f2;
  transition: border-color 0.2s ease;
}

.card[open] { border-color: var(--ink-soft); }

.card summary {
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  padding: 1.05rem 1.2rem;
  cursor: pointer;
  user-select: none;
}

.card summary::-webkit-details-marker { display: none; }

.card summary:hover .card__title,
.card summary:focus-visible .card__title { text-decoration: underline; text-underline-offset: 3px; }

.card__num {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--ink-faint);
  letter-spacing: 0.08em;
}

.card__title {
  font-family: var(--serif);
  font-size: 1.12rem;
  font-weight: 600;
}

.card summary::after {
  content: "+";
  margin-left: auto;
  font-family: var(--sans);
  font-weight: 500;
  font-size: 1.1rem;
  color: var(--ink-soft);
  transition: transform 0.2s ease;
}

.card[open] summary::after { content: "−"; }

.card p {
  margin: 0;
  padding: 0 1.2rem 1.15rem 3.15rem;
  font-size: 0.98rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

/* --------------------------------------------------------- act 4: grid */
.qgrid-wrap { text-align: center; }

.qgrid-counter {
  font-family: var(--sans);
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.6rem, 5vw, 2.2rem);
  font-weight: 700;
  letter-spacing: 0.01em;
  margin-bottom: 0.9rem;
}

.qgrid-counter .n-matched { color: var(--breach); }
.qgrid-counter.is-zero .n-matched { color: var(--ink); }

.qgrid-counter__sub {
  display: block;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-top: 0.2rem;
}

.qgrid {
  display: grid;
  grid-template-columns: repeat(15, 1fr);
  gap: 3px;
  max-width: 480px;
  margin: 0 auto;
}

.qgrid__cell {
  aspect-ratio: 1;
  border: 1px solid var(--ink-faint);
  background: transparent;
  transition: background 0.25s ease, border-color 0.25s ease;
}

/* matched = filled + accent: colour AND fill-state carry the meaning */
.qgrid__cell.is-lit {
  background: var(--breach);
  border-color: var(--breach-deep);
}

.qgrid__band {
  grid-column: 1 / -1;
  text-align: left;
  font-family: var(--sans);
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding: 0.5em 0 0.1em;
}

.qgrid-legend {
  display: flex;
  justify-content: center;
  gap: 1.4rem;
  margin-top: 0.9rem;
  font-family: var(--sans);
  font-size: 0.74rem;
  color: var(--ink-soft);
}

.qgrid-legend .swatch {
  display: inline-block;
  width: 0.7em;
  height: 0.7em;
  border: 1px solid var(--ink-faint);
  margin-right: 0.45em;
}

.qgrid-legend .swatch--lit { background: var(--breach); border-color: var(--breach-deep); }

/* --------------------------------------------------- act 5: promise ledger */
.ledger {
  max-width: 54rem;
  margin: 3rem auto 4rem;
  padding: 0 1.4rem;
}

.ledger__row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 0.9rem;
  align-items: stretch;
}

.ledger__row + .ledger__row { margin-top: 1rem; }

.ledger__cell {
  padding: 1.1rem 1.3rem;
  border: 1px solid var(--rule);
}

.ledger__promise { background: #fbf8f2; }

.ledger__reality {
  border-color: var(--breach);
  background: var(--breach-tint);
}

.ledger__tag {
  display: block;
  font-family: var(--sans);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin-bottom: 0.5rem;
}

.ledger__promise .ledger__tag { color: var(--ink-soft); }
.ledger__reality .ledger__tag { color: var(--breach-deep); }

.ledger__cell p { margin: 0; font-size: 1rem; line-height: 1.55; }
.ledger__reality p { color: var(--breach-deep); }

.ledger__arrow {
  align-self: center;
  font-family: var(--sans);
  font-size: 1.3rem;
  color: var(--ink-faint);
}

/* With JS the reality column stamps in as each row scrolls into view; the
   promise column is untouched, so both are always laid out side by side and
   nothing overwrites anything. Without JS everything is simply shown. */
.js .ledger__reality {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.js .ledger__row.is-shown .ledger__reality {
  opacity: 1;
  transform: none;
}

.js .ledger__arrow {
  opacity: 0.2;
  transition: opacity 0.5s ease;
}

.js .ledger__row.is-shown .ledger__arrow { opacity: 1; }

@media (max-width: 640px) {
  .ledger__row { grid-template-columns: 1fr; gap: 0.5rem; }
  .ledger__arrow { transform: rotate(90deg); justify-self: center; }
  .js .ledger__row.is-shown .ledger__arrow { transform: rotate(90deg); }
}

/* --------------------------------------------------------- coda */
.coda-fig {
  max-width: 30rem;
  margin: 2.5rem auto 3rem;
}

.coda-svg { width: 100%; height: auto; display: block; }

.coda-svg .dot { fill: var(--ink); opacity: 0.12; }
.coda-svg .dot--seat { opacity: 0.85; }

/* --------------------------------------------------------- sentiment */
/* The piece's last line. Bold and italic together, at 600 rather than the
   browser's 700, so it reads as the sentence being held up rather than as
   shouting on the way out. */
.coda-q {
  font-style: italic;
  font-weight: 600;
  color: var(--ink);
}

/* The decade the leak sits inside. Ink, never --breach: the reserved colour
   marks compromised exam material and these deaths are not that. Two bars on
   one scale, no gridlines, no animation. The numbers do the work. */
.toll {
  max-width: 26rem;
  margin: 2.6rem auto 3rem;
  padding: 0 1.4rem;
}

.toll__cap {
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 1.5rem;
}

.toll__svg { width: 100%; height: auto; display: block; overflow: visible; }
.toll__axis { stroke: var(--rule); stroke-width: 1; }
.toll__bar { fill: var(--ink); }
/* The earlier year sits back so the decade's direction reads at a glance. */
.toll__bar--then { fill: var(--ink-faint); }

.toll__val {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 700;
  fill: var(--ink);
}

.toll__yr {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.1em;
  fill: var(--ink-soft);
}

.toll__note {
  font-family: var(--sans);
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--ink-soft);
  margin-top: 1.1rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--rule);
}

.sentiment {
  max-width: 34rem;
  margin: 3.5rem auto;
  padding: 1.5rem 1.7rem;
  background: var(--sent-bg);
  border-left: 4px double var(--sent-ink);
  color: var(--sent-ink);
  font-family: var(--serif);
}

.sentiment__label {
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-bottom: 1rem;
  color: var(--sent-ink);
}

.sentiment__quote {
  font-size: 1.35rem;
  font-style: italic;
  line-height: 1.45;
  margin-bottom: 0.7rem;
}

.sentiment__context { font-size: 0.92rem; line-height: 1.55; margin: 0; opacity: 0.85; }

/* --------------------------------------------------------- epilogue */
.epilogue {
  max-width: var(--measure);
  margin: 3.5rem auto 1rem;
  padding: 1.6rem 1.4rem;
  border: 1px solid var(--rule);
  background: var(--paper-deep);
}

.epilogue__label {
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.9rem;
}

.epilogue ul { margin: 0; padding-left: 1.2rem; }
.epilogue li { margin-bottom: 0.55rem; font-size: 0.98rem; line-height: 1.55; }

/* ----------------------------------------------------------- update
   A dated addendum, added after the coda. It is part of the story, not
   apparatus, so it keeps the prose typography; the dateline and a heavier
   top rule mark it as arriving later than everything above it. No --breach:
   a resignation is not compromised exam material. */
/* .act.update, not .update: it has to out-specify `.act + .act`, which sets
   the 1px section rule. Same specificity, declared later, so it wins. */
.act.update { border-top-width: 3px; padding-top: 4.5rem; }
.update .act-num { color: var(--ink); }
.update .act-num::after { background: var(--ink); }

/* The closing paragraph carries the gap between what was promised and what
   the piece documented. Given a little more room and weight than body copy,
   without the highlighter, which belongs to the acts. */
.update .prose p:last-child {
  margin-top: 1.6rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
  font-size: 1.08rem;
}

/* --------------------------------------------------------- appendix
   Reference apparatus rather than narrative, so it reads a step quieter
   than an act: sans-serif entries, softer ink, a hairline ruled list. It
   sits between the coda and the sources and should feel closer to the
   sources than to the story. No --breach here; these are leaks, but the
   appendix is furniture, and the reserved colour stays with the material
   the piece actually reports on. */
.appendix { background: var(--paper-deep); padding-top: 3.5rem; padding-bottom: 3.5rem; }

.record {
  list-style: none;
  max-width: 44rem;
  margin: 1.8rem auto 0;
  padding: 0 1.4rem;
}

.record__row {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 1.1rem;
  padding: 1.05rem 0;
  border-top: 1px solid var(--rule);
}

.record__row:last-child { border-bottom: 1px solid var(--rule); }

.record__year {
  font-family: var(--sans);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  padding-top: 0.25rem;
  font-variant-numeric: tabular-nums;
}

/* A span of years stacks, so the column stays the same width as a single one. */
.record__to {
  display: block;
  font-weight: 400;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-faint);
  line-height: 1.5;
}

.record__text {
  font-size: 0.95rem;
  line-height: 1.62;
  color: var(--ink-soft);
}

.record__text strong { color: var(--ink); font-weight: 600; }

@media (max-width: 560px) {
  .record__row { grid-template-columns: 1fr; gap: 0.35rem; }
  .record__to { display: inline; font-size: 0.82rem; letter-spacing: 0.04em; }
}

/* --------------------------------------------------------- colophon */
.colophon {
  border-top: 1px solid var(--rule);
  margin-top: 4rem;
  padding: 3.5rem 1.4rem 4rem;
  background: var(--paper-deep);
}

.colophon__inner { max-width: var(--measure); margin: 0 auto; }

.colophon__head {
  font-size: 1.5rem;
  font-weight: 500;
  margin-bottom: 1rem;
}

.colophon__note, .colophon__help, .colophon__fine {
  font-size: 0.95rem;
  color: var(--ink-soft);
  line-height: 1.6;
}

.colophon__sources {
  margin: 1.2rem 0;
  padding-left: 1.2rem;
  font-family: var(--sans);
  font-size: 0.82rem;
  line-height: 1.7;
  color: var(--ink-soft);
}

.colophon__help { margin-top: 1.8rem; }

.colophon__fine {
  margin-top: 1.6rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
  font-size: 0.82rem;
  font-family: var(--sans);
}

/* ---------------------------------------------- margin notes (explainers)
   A term's first use gets a dotted underline (<dfn class="term">) and a
   companion note (<span class="mn" role="note">, placed at the end of the
   paragraph). Narrow screens: a small boxed insert in the column. Wide
   screens (≥1200px): the note floats out into the right margin, NYT/Tufte
   style. Pure CSS — readable without JS. */
dfn.term {
  font-style: normal;
  text-decoration: underline dotted;
  text-decoration-color: var(--ink-faint);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

.mn {
  display: block;
  font-family: var(--sans);
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--ink-soft);
  border-left: 2px solid var(--rule);
  padding: 0.45rem 0 0.45rem 0.9rem;
  margin: 1.1rem 0 0.4rem;
}

.mn__term {
  display: block;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 0.25rem;
}

@media (min-width: 1200px) {
  .mn {
    float: right;
    clear: right;
    width: 14rem;
    margin: 0.2rem -16.4rem 1rem 1.5rem;
  }
}

/* Links inside running text and margin notes. There is no blue anywhere else
   in the piece, so a default browser link reads as debris on this paper.
   The child combinator keeps this off sup.ref anchors, which are nested one
   level deeper and carry their own treatment below. */
.prose p > a,
.mn a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--ink-faint);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-color 160ms ease;
}

.prose p > a:hover,
.mn a:hover { text-decoration-color: var(--ink); }

/* ------------------------------------------------------ inline citations
   Wikipedia-style [n] markers pointing at the numbered source list. */
sup.ref {
  font-family: var(--sans);
  font-size: 0.68em;
  line-height: 0;
}

sup.ref a {
  color: var(--ink-soft);
  text-decoration: none;
  padding: 0 0.06em;
}

sup.ref a:hover,
sup.ref a:focus-visible {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.colophon__sources li {
  scroll-margin-top: 2.5rem;
}

.colophon__sources li:target {
  color: var(--ink);
  background: #fffdf5;
  outline: 1px solid var(--rule);
  outline-offset: 4px;
}

.colophon__sources a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* --------------------------------------------------------- motion prefs */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  .scroll-cue { animation: none; }
}

/* --------------------------------------------------------- small screens */
@media (max-width: 420px) {
  body { font-size: 1.06rem; }
  .step { margin-left: 0.8rem; margin-right: 0.8rem; }
  .qgrid { gap: 2px; }
}

/* ============================================================= THE MAP ===
   Where the two leaks happened. The country is drawn whole; one state,
   Haryana, is shaded, because that is the level the source names it at.
   Leak and arrest sites use the reserved --breach accent because that is
   exactly what they are: compromised material. The Delhi reference marker
   and the land itself deliberately do NOT use it. */

/* On one column the map holds the top of the screen and the step cards
   travel below it, so a card is never sitting on the towns it names. */
.scrolly--map .scrolly__figure { align-items: flex-start; }

/* The map's cards live inside .step-wrap wrappers (see the HTML comment).
   Wrapping made every card a :last-child, which would hand step 0 the
   short 30vh tail meant for the end of a scrolly - so the margins are
   restated here at wrapper level for wide screens. */
.js .scrolly--map .step { margin-bottom: 85vh; }
.js .scrolly--map .step-wrap:last-child .step { margin-bottom: 30vh; }

.map-wrap { width: 100%; display: flex; justify-content: center; }

.map-svg {
  height: min(68vh, 660px);
  width: auto;
  max-width: 100%;
  display: block;
  overflow: visible;
}

.map-land {
  fill: var(--paper-deep);
  stroke: var(--rule);
  stroke-width: 1.2;
  stroke-linejoin: round;
}

/* Haryana: a shaded state, not a pin — the source names no town. The group
   holds the shape and its label, so paint goes on the shape only; putting
   fill on the group would tint the label too. */
.map-region { cursor: pointer; transition: opacity 420ms ease; }

.map-region__shape {
  fill: var(--breach-tint);
  stroke: var(--rule);
  stroke-width: 1;
  transition: fill 200ms ease;
}
.map-region.is-active .map-region__shape { fill: var(--breach); fill-opacity: 0.42; }
.map-region:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Haryana is a 2024 site. Once 2026 is showing it recedes to the same weight
   as the 2024 pins, rather than sitting at full strength beside them. */
.map-svg.show-2026 .map-region { opacity: 0.38; }
.map-svg.show-2026 .map-region.is-active { opacity: 1; }

.map-ref circle { fill: var(--ink-faint); }
.map-ref__label {
  font-family: var(--sans);
  font-size: 13px;
  fill: var(--ink-soft);
  letter-spacing: 0.04em;
}

.map-pin__label {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 600;
  fill: var(--ink);
  paint-order: stroke;
  stroke: var(--paper);
  stroke-width: 3.5px;
  stroke-linejoin: round;
  pointer-events: none;
}

/* Shape carries the year as well as colour: 2024 round, 2026 square. */
.map-pin__mark {
  fill: var(--breach);
  stroke: var(--paper);
  stroke-width: 1.6;
  transition: transform 160ms ease;
  transform-box: fill-box;
  transform-origin: center;
}

/* An invisible disc wide enough for a thumb, which fills faintly on hover so
   the marker answers the pointer before the panel does. */
.map-pin__hit { fill: transparent; transition: fill 160ms ease; }
.map-pin.is-active .map-pin__hit { fill: var(--breach); fill-opacity: 0.13; }

/* The step column sits over the figure at z-index 2, and used to swallow
   every pointer event before it reached a marker. Let the gaps between the
   cards fall through to the map. */
.scrolly--map .scrolly__steps { pointer-events: none; }
.scrolly--map .step { pointer-events: auto; }

.map-pin {
  opacity: 0;
  pointer-events: none;
  cursor: pointer;
  transition: opacity 420ms ease;
}
.map-svg .map-pin--2024,
.map-svg.show-2026 .map-pin--2026 { opacity: 1; pointer-events: auto; }
/* Once 2026 is showing, the 2024 sites stay but recede. */
.map-svg.show-2026 .map-pin--2024 { opacity: 0.38; }

.map-pin.is-active { opacity: 1; }
/* Hovering a 2026 row in the key must not conjure its marker onto a map
   that is still showing 2024. */
.map-svg:not(.show-2026) .map-pin--2026.is-active { opacity: 0; }
.map-pin.is-active .map-pin__mark { transform: scale(1.45); }
.map-pin.is-active .map-pin__label { fill: var(--breach); }
.map-pin:focus-visible { outline: none; }
.map-pin:focus-visible .map-pin__mark { stroke: var(--ink); stroke-width: 2.4; }

.map-svg.is-static .map-pin,
.map-svg.is-static .map-pin__mark,
.map-svg.is-static .map-region { transition: none; }

/* The panel under the map: what a marker says when you touch it. */
.map-detail {
  min-height: 5.4rem;
  max-width: 30rem;
  margin: 0.9rem auto 0;
  padding: 0.7rem 0 0;
  border-top: 1px solid var(--rule);
  font-size: 0.9rem;
  line-height: 1.55;
}
.map-detail__hint {
  margin: 0;
  font-family: var(--sans);
  font-size: 0.76rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.map-detail__place {
  margin: 0 0 0.25rem;
  font-family: var(--sans);
  font-weight: 700;
  font-size: 0.95rem;
}
.map-detail__state, .map-detail__year {
  font-weight: 400;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-left: 0.6rem;
}
.map-detail__why { margin: 0; color: var(--ink-soft); }

/* The key: why each place matters. Lives outside the sticky figure. */
.map-key {
  max-width: 46rem;
  margin: 1.5rem auto 0;
  padding: 0 1.4rem;
}

.map-key__groups, .map-key__list { list-style: none; margin: 0; padding: 0; }
.map-key__group + .map-key__group { margin-top: 1.6rem; }

.map-key__year {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0 0 0.5rem;
}

.map-key__row {
  display: grid;
  grid-template-columns: 9rem 1fr;
  gap: 1rem;
  padding: 0.55rem 0 0.55rem 0.8rem;
  border-top: 1px solid var(--rule);
  /* The reserved accent, on the sites themselves: this is leak material. */
  border-left: 3px solid var(--breach);
  font-size: 0.9rem;
  line-height: 1.55;
  cursor: pointer;
  transition: background-color 180ms ease;
}
.map-key__row.is-active { background: var(--breach-tint); }
.map-key__row:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

.map-key__place {
  font-family: var(--sans);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
}
.map-key__state {
  display: block;
  font-weight: 400;
  font-size: 0.7rem;
  color: var(--ink-faint);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.map-key__text { color: var(--ink-soft); }

/* Keep the step cards clear of the map: on wide screens the cards sit left
   and the map sits right, so a card never covers the places it discusses. */
@media (min-width: 1000px) {
  .scrolly--map .step { margin-left: max(3vw, 1.4rem); margin-right: auto; max-width: 24rem; }
  .scrolly--map .scrolly__figure {
    align-items: center;
    justify-content: flex-end;
    padding-right: 4vw;
  }
  .scrolly--map .map-fig { max-width: 34rem; }
}

@media (max-width: 999px) {
  .map-svg { height: min(42vh, 420px); }

  /* On one column a step card normally travels the full height of the
     screen, which sooner or later parks it on top of the map - and the
     2026 view held for barely a third of a screen before the map unpinned,
     which on a phone read as the map rushing away. Here the card arrives
     from the bottom, pins below the map and its detail panel (48vh map +
     figure padding + 6.4rem panel), and the next card fades in over it in
     place. Travel comes from the wrapper heights: a sticky element's own
     margins count against its pin range, which is why the cards cannot
     simply keep their 85vh margins. */
  .js .scrolly--map .scrolly__steps { margin-top: -45vh; }
  .js .scrolly--map .step {
    position: sticky;
    top: calc(48vh + 8.6rem);
    margin-bottom: 0;
  }
  /* Both the map and the card are pinned here, so any scroll beyond what it
     takes to read the card is dead travel: the screen holds still while the
     thumb keeps moving. At 115vh that dead stretch measured 0.71 of a screen,
     which reads as the piece being stuck. Trimmed to leave enough dwell to
     read the card and register the map, and no more. */
  .js .scrolly--map .step-wrap { min-height: 70vh; }
  .js .scrolly--map .step-wrap:last-child { min-height: 76vh; }
  .js .scrolly--map .step-wrap:last-child .step { margin-bottom: 0; }

  /* Only the current card exists. The off-duty card would otherwise sit
     dimmed in the same pinned spot, and an invisible card must not
     swallow taps meant for the key below it. */
  .js .scrolly--map .step:not(.is-active) { opacity: 0; pointer-events: none; }

}

/* No-JS fallback: the same facts as a plain list. */
.map-fallback { margin: 0; padding-left: 1.1rem; font-size: 0.92rem; line-height: 1.6; }
.map-fallback li { margin-bottom: 0.45rem; }
.js .map-fallback { display: none; }

@media (max-width: 640px) {
  .map-key__row { grid-template-columns: 1fr; gap: 0.15rem; }

  /* On a phone the SVG scales to roughly half its drawn size, so everything
     below is set in inflated user units to land at a readable screen size.
     Sizing by width instead of height first: the height cap left 70-odd px
     of a 390px screen unused, and on a small map every pixel is a city. */
  .map-svg { width: 100%; height: auto; max-height: 48vh; transform: translateX(4vw); }

  /* The nudge right exists for one label: Ahmedabad sits on the west coast
     with its name reading out to sea, and at phone scale it cleared the
     viewport's left edge by -12px. India's own shape leans the other way,
     so the drawing has ~80px of slack on the right and none on the left.
     A translation moves everything as one; it cannot create overlaps. */

  /* 26 user units x ~0.55 scale ≈ 14px on screen. */
  .map-pin__label { font-size: 26px; }
  .map-ref__label { font-size: 22px; }

  /* Grow the marks in place. transform scales the circle and the 2026
     square alike, which per-shape geometry properties cannot. */
  .map-pin__mark {
    transform-box: fill-box;
    transform-origin: center;
    transform: scale(1.5);
  }

  /* The invisible disc under each mark is the touch target: 15 user units
     rendered at ~0.55 scale was a 16px target. 40 units ≈ 44px, the floor
     for a fingertip. r is a geometry property, so CSS can set it; browsers
     that do not support that keep the smaller attribute value and lose
     nothing else. */
  .map-pin__hit { r: 40px; }

  .map-detail { min-height: 6.4rem; }

  /* Short phones (SE-class): map + panel + a pinned card do not all fit in
     667px with the map at 48vh, and a pinned card cannot be scrolled to,
     so its clipped last lines would simply be unreadable. Trade map size
     for the card's room. This must sit after the 48vh rule above to win
     the cascade. */
  @media (max-height: 700px) {
    .map-svg { max-height: 42vh; }
    .js .scrolly--map .step { top: calc(42vh + 8.4rem); }
  }
}

/* ======================================================= WHERE YOU ARE ===
   A long scroll-driven piece loses people. Three things say where they are:
   a rail on wide screens, a hairline across the top, and on narrow screens a
   button carrying the current section's name. None of them use --breach;
   this is furniture, not evidence. */

/* With JS off, this is simply a contents list at the head of the piece. */
.toc {
  max-width: var(--measure);
  margin: 0 auto 3.5rem;
  padding: 0 1.4rem;
}

.toc__head {
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 0.6rem;
}

.toc__list { list-style: none; margin: 0; padding: 0; }

.toc a {
  display: block;
  text-decoration: none;
  color: var(--ink-faint);
  padding: 0.3rem 0 0.3rem 0.75rem;
  border-left: 2px solid transparent;
  transition: color 200ms ease, border-color 200ms ease;
}
.toc a:hover, .toc a:focus-visible { color: var(--ink); }
.toc a.is-current { color: var(--ink); border-left-color: var(--ink); }

.toc__num {
  display: block;
  font-family: var(--sans);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.toc__name { font-size: 0.92rem; line-height: 1.35; }

.reading-bar, .toc__toggle { display: none; }

/* --- with JS: the rail, the bar, the button --- */
.js .reading-bar {
  display: block;
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2px;
  z-index: 45;
  pointer-events: none;
}
.reading-bar__fill {
  display: block;
  height: 100%;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

.js .toc {
  display: none;
  position: fixed;
  z-index: 50;
  left: 1.6rem;
  top: 50%;
  transform: translateY(-50%);
  width: 10.5rem;
  max-width: none;
  margin: 0;
  padding: 0;
}
.js .toc__head { display: none; }

/* Narrow screens: the rail is a panel the button opens. */
.js .toc.is-open {
  display: block;
  left: auto;
  right: 1rem;
  top: auto;
  bottom: 4.3rem;
  transform: none;
  width: min(17rem, calc(100vw - 2rem));
  max-height: 70vh;
  overflow-y: auto;
  background: var(--paper);
  border: 1px solid var(--rule);
  box-shadow: 0 10px 30px rgba(28, 24, 19, 0.14);
  padding: 0.9rem 1.1rem;
}

.js .toc__toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 51;
  max-width: 66vw;
  padding: 0.5rem 0.95rem;
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 999px;
  box-shadow: 0 2px 12px rgba(28, 24, 19, 0.1);
  cursor: pointer;
}
.js .toc__toggle::before {
  content: '';
  width: 12px;
  height: 8px;
  flex: none;
  border-top: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
}
.toc__toggle-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Wide enough for the rail to sit clear of the text column. */
@media (min-width: 1240px) {
  .js .toc { display: block; }
  .js .toc__toggle { display: none; }
  /* The map's step cards are the one block that hugs the left edge. Move
     them clear of the rail rather than letting the two overlap. */
  .js .scrolly--map .step { margin-left: 13.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .toc a { transition: none; }
}

/* The cover carries no chrome. The rail, the button and the reading bar stay
   out of the way until the prologue has actually begun; js/lib/toc.js puts
   .is-reading on <html> at that point. visibility, not just opacity, so a
   hidden toggle is not in the tab order. */
.js .toc,
.js .toc__toggle,
.js .reading-bar {
  opacity: 0;
  visibility: hidden;
  transition: opacity 320ms ease, visibility 0s linear 320ms;
}

.js.is-reading .toc,
.js.is-reading .toc__toggle,
.js.is-reading .reading-bar {
  opacity: 1;
  visibility: visible;
  transition: opacity 320ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .js .toc, .js .toc__toggle, .js .reading-bar,
  .js.is-reading .toc, .js.is-reading .toc__toggle, .js.is-reading .reading-bar {
    transition: none;
  }
}
