/* The recap page. Inherits every colour and font token from /style.css.
 *
 * ⭐ `content-visibility: auto` on each section is what makes 227 of them
 * cheap: the browser skips layout and paint for anything off screen. It needs
 * `contain-intrinsic-size` alongside it, or the scrollbar jumps as sections
 * render -- the value is a rough guess at a section's height, and being
 * approximately right is enough.
 */

/* ⛔ THE SAME BOX AS `.wrap`, and that is the whole point of the number.
   Measured at 1280px before this: the wordmark started at 164, the hub's
   heading at 250 and an episode's heading at 330 -- three left edges in one
   section, and only /account lined up with the masthead. `.wrap` is
   `max-width: 1000px` with `padding: 0 clamp(14px, 4vw, 24px)`, restated here
   rather than shared, because these pages carry `main` and not `.wrap`.

   ⚠️ The COLUMN is 1000px; the PROSE is not. Everything a reader reads is
   capped at --measure below, so the page is ranged left against the wordmark
   with reading-width text inside it -- which is what /account does, and what a
   newspaper column does. Widening the prose to 1000px would be unreadable. */
.recaps main {
  /* ⚠️ SET BY MEASUREMENT. At 655px the beats measured 73-77 characters a
     line -- inside the 45-75 band but sitting on its ceiling, which is where
     the previous cap had also landed. 620px with an 18px body measures 68. */
  --measure: 620px;
  max-width: 1000px;
  margin: 0 auto;
  padding: 2rem clamp(14px, 4vw, 24px) 4.5rem;
}

.recaps h1 {
  font-family: var(--display);
  font-size: clamp(27px, 5vw, 34px);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -.02em;
  margin: 0 0 0.5rem;
}

.recaps .standfirst {
  font-family: var(--serif);
  font-size: 18.5px;
  line-height: 1.55;
  color: var(--muted);
  margin: 0 0 2rem;
  max-width: var(--measure);
}

/* --- controls ---------------------------------------------------------- */

/* ⚠️ The BAR is sticky, not this. `.controls` used to stick on its own, which
   left the season and episode chips to scroll away above it. */
.recaps .hubbar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  /* ⛔ The chips can outgrow a phone screen once a season is picked, so the bar
     scrolls inside itself rather than pinning the whole page open. */
  max-height: 62vh;
  overflow-y: auto;
}

.recaps .controls {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 0.75rem;
  padding: 0.875rem 0 0.5rem;
}

@media (max-width: 34rem) {
  .recaps .controls { grid-template-columns: 1fr; }
}

/* ⭐ ONE FAMILY WITH THE CHIPS BELOW IT, his note. They shared nothing: the
   input was white with a --border-idle edge, the season chips were --chip-idle
   with a --rule edge, and the input's rem padding rendered 8.8px/11.2px against
   the chips' whole 5px/10px. Now both sit on --card behind the same
   --border-idle line, and the only thing that separates them is the shape --
   which is the house rule (`style.css` line 2: square corners throughout, pills
   are the only rounded thing), and which is doing real work here: a box you
   type into and a chip you press should not be the same shape.

   ⚠️ Capped at 34em. Full width, a search field on an 820px column reads as a
   page-wide banner rather than a control. */
.recaps input[type="search"] {
  font: inherit;
  font-family: var(--sans);
  font-size: 16px;
  width: 100%;
  max-width: 34em;
  padding: 9px 12px;
  border: 1px solid var(--border-idle);
  border-radius: 0;
  background: var(--card);
  color: var(--ink);
}

.recaps input[type="search"]:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 1px;
  border-color: var(--blue);
}

/* --- season nav -------------------------------------------------------- */

.recaps .seasons {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin: 0 0 0.5rem;
}

.recaps .seasons a {
  font-family: var(--display);
  font-size: 14.5px;
  font-weight: 600;
  padding: 5px 12px;
  border: 1px solid var(--border-idle);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink);
  text-decoration: none;
}

.recaps .seasons a:hover { background: var(--btn-hover); }

/* --- results ----------------------------------------------------------- */

.recaps .results {
  margin: 1rem 0 0;
  border: 1px solid var(--rule);
  border-radius: 0;
  background: var(--card);
  overflow: hidden;
}

.recaps .results .count,
.recaps .results .empty {
  margin: 0;
  padding: 0.6rem 0.85rem;
  font-size: 13.5px;
  color: var(--muted-2);
  background: var(--dim-surface);
  border-bottom: 1px solid var(--rule-light);
}

.recaps .hit {
  display: grid;
  grid-template-columns: 5.5rem 1fr auto;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.6rem 0.85rem;
  border-bottom: 1px solid var(--rule-lighter);
  text-decoration: none;
  color: var(--ink);
}

.recaps .hit:last-child { border-bottom: 0; }
.recaps .hit:hover { background: var(--blank-fill); }

.recaps .hit-num {
  font-family: var(--display);
  font-size: 13.5px;
  color: var(--muted-2);
}

/* ⚠️ 600, not 500. It was the ONLY rule on the site asking for Fira Sans
   500, and that weight cost 49 KiB across two files for one line of text. */
.recaps .hit-title { font-weight: 600; }

.recaps .hit-count {
  font-size: 12px;
  color: var(--muted-2);
  white-space: nowrap;
}

/* --- episodes ---------------------------------------------------------- */

/* ⭐ THE ACCOUNT PAGE'S SECTION RULE, his call -- `.rows__head` is a heading
   over a 2px --ink line, and this was the one place on the site putting the
   line ABOVE the heading instead. Same weight, same colour, opposite edge. */
.recaps h2.season {
  font-family: var(--display);
  font-size: 19px;
  font-weight: 700;
  margin: 3rem 0 1.25rem;
  padding-bottom: 0.625rem;
  border-bottom: 2px solid var(--ink);
  /* ⚠️ The RULE matches the prose, not the column. `main` is 1000px so that
     the page starts where the wordmark does, but the text inside it stops at
     --measure -- a section rule drawn to 1000px would end 380px past the last
     word under it. The controls above are furniture and do run full width. */
  max-width: var(--measure);
  scroll-margin-top: 6rem;
}

/* ⚠️ SEPARATION MEASURED AGAINST THE LEADING, not against a round number.
   At 1.5rem top and bottom the gap between two episodes was 24 + 1 + 24 = 49px,
   and the beats are 18px over 1.7 -- so two whole records sat 1.6 lines of text
   apart, which is why the page read as one column of prose rather than a list.
   40 + 1 + 32 = 73px is 2.4 lines, and a record now separates from its
   neighbour more clearly than its own parts separate from each other (the
   largest gap inside an episode is 14px).

   ⭐ ASYMMETRIC, and that way round on purpose: the rule closes the episode
   ABOVE it, so it sits nearer that one -- 32px of padding-bottom under the last
   beat, 40px of padding-top before the next kicker. Even padding makes the rule
   float between two blocks belonging to neither.

   ⚠️ contain-intrinsic-size raised with it. At eight to ten beats a section is
   roughly twice the height the 320px guess assumed; `auto` means the browser
   replaces this with the real size after first render, so it only has to be
   close enough to keep the scrollbar from jumping on the first pass. */
.recaps .ep {
  content-visibility: auto;
  contain-intrinsic-size: auto 560px;
  padding: 2.5rem 0 2rem;
  border-bottom: 1px solid var(--rule-light);
  max-width: var(--measure);
  scroll-margin-top: 6rem;
}

.recaps .ep h3 {
  font-family: var(--display);
  font-size: 19px;
  font-weight: 700;
  margin: 0 0 0.2rem;
  line-height: 1.3;
  max-width: var(--measure);
}

/* ⛔ THE TITLES WERE #0000EE -- the BROWSER's default link blue, not the
   house --blue #1a5fd0. Nothing had ever styled this selector, so 227 of the
   loudest things on the page were unstyled anchors. Near-black with the
   underline on hover, which is what a headline in a list of headlines does;
   colour is kept for things you can act on now. */
.recaps .ep h3 a {
  color: var(--ink);
  text-decoration: none;
}

.recaps .ep h3 a:hover,
.recaps .ep h3 a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The kicker. Was --blue; a number is a label, not somewhere to go. */
.recaps .ep .num {
  font-family: var(--display);
  color: var(--muted-2);
  letter-spacing: .08em;
  margin-right: 0.45rem;
  white-space: nowrap;
}

.recaps .ep .meta {
  margin: 0 0 0.75rem;
  font-size: 13.5px;
  color: var(--muted-2);
}

/* ⚠️ A STEP, not a rounding difference. The hub ran the TL;DR at 18.5px over
   beats at 18px -- half a pixel, which is no hierarchy at all, the same flat
   spot the "What happens" heading had at 16px over a 15.5px body.

   ⛔ The BEATS stay 18px on both pages and the TL;DR carries the step -- 20px
   here, 21px on an episode page. Shrinking the hub's beats to 17px instead was
   tried and measured 72 characters a line against the episode page's 68,
   because --measure is a fixed width and a smaller face fits more into it.
   One reading size across the section is also the point of the exercise. */
.recaps .ep .tldr {
  font-family: var(--serif);
  font-size: 20px;
  line-height: 1.5;
  max-width: var(--measure);
  margin: 0 0 0.9rem;
}

/* ⭐ THE BEATS ARE THE PAGE, and they were the quietest thing on it: Fira Sans
   15.5px in --muted (#5c584f), sitting UNDER a TL;DR set in Literata 18.5px in
   --ink. The summary was louder than the substance it summarised.
   ⭐ Literata, his call 2026-09-09 -- the press rule is that a serif carries
   the reading and a sans carries the furniture, and the game already works
   that way: `.dialogue__text` and `.option__text` are both Literata. The study
   pages were the only reading surface on the site set in a sans, and carrying
   three families at once (Public Sans heading, Literata TL;DR, Fira Sans body)
   is what a page looks like when nobody decided which one reads.

   ⚠️ MEASURE SET BY MEASUREMENT, NOT BY THE UNIT -- and the earlier `62ch` cap
   is why this is a pixel value now. 62ch was tuned on the hub's 820px column
   and then inherited by the 660px episode column, where it could not bite: the
   beats measured 51 characters a line there, BELOW the 45-75 band, not above
   it. A fixed --measure caps both pages identically. */
.recaps .beats {
  margin: 0;
  padding-left: 1.2rem;
  max-width: var(--measure);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.7;
}

/* A quiet marker. The bullets stay -- a recap in beats IS a list, and the
   markers are what let someone scan ten of them -- but at --muted-3 they mark
   without competing with the prose. #8d8880 is under the 4.5:1 body-text
   floor, which is why it is on a MARKER and not on any text. */
.recaps .beats li::marker { color: var(--muted-3); }

/* Separate thoughts, separated. 4.8px between beats read as one grey block,
   and at eight to ten beats an episode that block is now twice as long. */
.recaps .beats li { margin-bottom: 0.8em; }
.recaps .beats li:last-child { margin-bottom: 0; }

/* ⚠️ A phone is not a narrow desktop. Measured on a Pixel 8 at 375px: 18px
   beats measure 37 characters a line and the clamped standfirst measures 38,
   so the two sizes converge and the standfirst stops leading anything. 17px
   here measures 39 -- still a comfortable phone measure, and it restores the
   step down from the standfirst that the desktop page has. */
@media (max-width: 34rem) {
  .recaps .beats { font-size: 17px; }
}

/* An anchor for the twin number of a double episode. Occupies no space; it
   exists so /recaps#s4e24 lands on the section that covers s4e23-24. */
.recaps .alias {
  display: block;
  height: 0;
  scroll-margin-top: 6rem;
}

/* Same box as `main` above it, or the footer rule starts and stops somewhere
   the column does not. */
.recaps footer {
  max-width: 1000px;
  margin: 0 auto;
  padding: 1.5rem clamp(14px, 4vw, 24px) 3rem;
  font-size: 13.5px;
  color: var(--muted-2);
  border-top: 1px solid var(--rule);
}

.recaps footer p { max-width: 70ch; margin: 0; }

@media (prefers-reduced-motion: reduce) {
  .recaps * { scroll-behavior: auto !important; }
}

/* --- spoke: one episode per URL ---------------------------------------- */

/* ⚠️ NOT 660px any more -- see `.recaps main`. The episode page and the hub
   now share one column so that clicking from one to the other does not move
   the left edge, and --measure keeps the prose inside it at reading width. */
.spoke main { max-width: 1000px; }

/* Breadcrumbs are furniture, not a destination -- was --blue on every crumb. */
.spoke .crumbs {
  font-size: 13.5px;
  color: var(--muted-2);
  margin-bottom: 2rem;
}

.spoke .crumbs a { color: var(--muted-2); text-decoration: none; }
.spoke .crumbs a:hover,
.spoke .crumbs a:focus-visible {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ⭐ --display 700, his call: every other heading on the site is Public Sans
   700 and this was the one h1 in Fira Sans at 600.
   ⭐ Sized and letter-spaced to match `.title-row h1` on /account, his call
   2026-09-09 -- an episode page is the same kind of page and was two steps
   smaller than the account screen's heading. */
.spoke h1 {
  font-family: var(--display);
  font-size: clamp(27px, 5vw, 34px);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -.02em;
  margin: 0 0 0.4rem;
  max-width: var(--measure);
}

/* ⛔ THIS SELECTOR HAD NO RULE AT ALL. The air date rendered at the inherited
   Fira Sans 16px in --ink -- the same size and the same colour as the body
   copy underneath it, so the one line of metadata on the page was set exactly
   like the prose. It carries the rule that closes the title block, which is
   `.title-row`'s shape on /account. */
.spoke .meta {
  margin: 0 0 1.5rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--rule);
  max-width: var(--measure);
  font-size: 13.5px;
  color: var(--muted-2);
}

/* `.rows__head` from /account: 19px over a 2px --ink line. Was 16px, half a
   pixel larger than the 15.5px body it introduced. */
.spoke h2 {
  font-family: var(--display);
  font-size: 19px;
  font-weight: 700;
  line-height: 1.3;
  margin: 2.5rem 0 1.25rem;
  padding-bottom: 0.625rem;
  border-bottom: 2px solid var(--ink);
  max-width: var(--measure);
}

/* The standfirst. One step above the beats, same family, so the page reads as
   one voice getting quieter rather than two typefaces taking turns. */
.spoke .tldr {
  font-family: var(--serif);
  /* ⚠️ CLAMPED, measured on a Pixel 8 at 375px: a flat 21px measured 34
     characters a line there, under the 45 floor, and turned one sentence into
     six lines. 18px measures 40. The 21px ceiling is what desktop gets. */
  font-size: clamp(19px, 4.6vw, 21px);
  line-height: 1.45;
  max-width: var(--measure);
  margin: 0;
}

.spoke .pointer {
  margin: 2rem 0 0;
  padding: 0.75rem 0.9rem;
  background: var(--dim-surface);
  border: 1px solid var(--dim-border);
  border-radius: 0;
  max-width: var(--measure);
  font-size: 14.5px;
  color: var(--muted);
}

.spoke .episode-nav {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 2.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
  max-width: var(--measure);
  font-size: 14.5px;
}

/* Near-black, not --blue. Prev and next are two links on a page that had blue
   on the title, the number, four breadcrumbs and both of these. */
.spoke .episode-nav a {
  color: var(--ink);
  text-decoration: none;
  max-width: 48%;
}

.spoke .episode-nav a:hover,
.spoke .episode-nav a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 3px;
}
.spoke .episode-nav .next { margin-left: auto; text-align: right; }

.spoke .siblings { margin-top: 2.5rem; }

/* ⭐ ONE LINE PER EPISODE, his call 2026-09-09 -- this was `columns: 2`, and a
   two-column list is read down the left half and then down the right, which is
   the wrong shape for a run of consecutive episode numbers. Same treatment as
   `.saved-list` on /me: a row, a hairline, and the title clipped rather than
   wrapped, so every row is exactly one line tall. */
.spoke .siblings ul {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: var(--measure);
  line-height: 1.5;
}

/* ⚠️ 24px MINIMUM. These rows were 18px tall and Lighthouse flagged seven of
   them: a list of episode links is the most tapped thing on the page after the
   two buttons, and 18px is under the pointer-target minimum. */
.spoke .siblings li { margin: 0; }

.spoke .siblings a {
  display: block;
  padding: 9px 0;
  min-height: 24px;
  border-bottom: 1px solid var(--rule-lighter);
  font-size: 15.5px;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.spoke .siblings li:last-child a { border-bottom: 0; }
.spoke .siblings a:hover,
.spoke .siblings a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 3px;
}
.spoke .siblings .num { color: var(--muted-2); margin-right: 0.5rem; }

/* --- share and watchlist controls -------------------------------------- */

/* ⛔ `justify-content` MUST be declared, and this is the second collision of
   its kind. `style.css` has a bare `.actions` for the game's results-screen
   share row, and that one centres. This selector is more specific, but only
   for the properties it actually states -- everything it leaves out falls
   through to the game's rule. Left unstated, Share and Save sat centred in a
   column where the recap, the beats and the note beneath them are all ranged
   left. (The same trap sized the Save button to 15px through `.mark`.) */
.spoke .actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: 0.5rem;
  margin-top: 2rem;
  max-width: var(--measure);
}

/* ⚠️ 600 and whole pixels. Every other button on the site is weight 600, and
   the rem padding here rendered as 6.4px/12.8px against the 13px/26px the
   secondary button uses. */
.spoke .action {
  font: inherit;
  font-family: var(--sans);
  font-size: 14.5px;
  font-weight: 600;
  /* ⛔ THE THIRD COLLISION with style.css, after `.mark` and `.actions`.
     `.share` there is the game's results-screen panel and carries
     `margin-top: 36px`, so Share sat 36px below Save on the same row. Every
     property this component depends on is now stated, because a bare class
     name in the shared stylesheet can always reach one of these. */
  margin: 0;
  padding: 7px 14px;
  border: 1px solid var(--border-idle);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
}

.spoke .action:hover { background: var(--btn-hover); }

.spoke .action:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 1px;
}

/* Marked. Green rather than blue: blue is the link colour throughout, and a
   pressed toggle that looks like a link reads as somewhere still to go. */
.spoke .action.on {
  background: var(--green-surface);
  border-color: var(--green-border-soft);
  color: var(--green-deep);
  /* ⛔ No font-weight. `.spoke .action` is 600, so this was making a PRESSED
     toggle lighter than an unpressed one -- left over from when the base rule
     was 400. It inherits 600 now. */
}

.spoke .actions-note {
  flex-basis: 100%;
  margin: 0.15rem 0 0;
  font-size: 13.5px;
  color: var(--muted-2);
}

.spoke .actions-note a { color: var(--blue); }

/* --- your list (/me) --------------------------------------------------- */

.mine main { max-width: 660px; }

.mine .empty-state {
  margin-top: 1.5rem;
  padding: 1rem;
  background: var(--dim-surface);
  border: 1px solid var(--dim-border);
  border-radius: 0;
  font-size: 15.5px;
  color: var(--muted);
}

.mine .empty-state a { color: var(--blue); }

.mine .marked { margin-top: 2rem; }

.mine .marked h2 {
  font-family: var(--display);
  font-size: 18.5px;
  margin: 0 0 0.5rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--rule);
}

.mine .marked ul { list-style: none; margin: 0; padding: 0; }
.mine .marked li { padding: 0.35rem 0; font-size: 15.5px; }
.mine .marked a { color: var(--ink); text-decoration: none; }
.mine .marked a:hover { text-decoration: underline; }
.mine .marked .num { color: var(--muted-2); margin-right: 0.4rem; }

/* --- the season picker ------------------------------------------------- */

/* Selected season: filled, not merely outlined. A chip row where the current
   one is only a shade darker reads as five equal chips. */
.recaps .seasons a[aria-selected="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: #fff;
  font-weight: 700;
}

.recaps .episodes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin: 0 0 0.75rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--rule-light);
}

/* One level down from a season chip: same pill, smaller, on white. */
.recaps .episodes .chip {
  font-family: var(--display);
  font-size: 13px;
  font-weight: 600;
  padding: 4px 11px;
  border: 1px solid var(--border-idle);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink);
  text-decoration: none;
}

.recaps .episodes .chip:hover {
  background: var(--blank-fill);
  border-color: var(--blue);
}

.recaps .episodes .chip:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 1px;
}

/* The sticky bar on an episode page. Narrower column than the hub, and no
   season headings below it to scroll to -- a season chip here is a link back
   to the hub's anchor. */
.spoke .hubbar--spoke { margin-bottom: 1.25rem; }
