/* Messages site - public sermon archive (ADR-0006).
 *
 * Light-default with a dark toggle (the operator app is the opposite,
 * dark-first). Brand palette is the official Unity Worldwide / Unity of
 * Walnut Creek system (#160): primary teal + gold accent for
 * active/selected/focus states. Fonts (Montserrat headings, Poppins body)
 * are self-hosted from assets/fonts/ - no Google Fonts CDN, to keep the site
 * cookieless / free of third-party requests.
 */

/* Self-hosted fonts (OFL). Montserrat latin is the variable subset and covers
 * both heading weights; Poppins ships a static instance per weight. */
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 600 700;
  font-display: swap;
  src: url("/assets/fonts/montserrat-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/poppins-400.woff2") format("woff2");
}
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/poppins-600.woff2") format("woff2");
}

:root {
  --brand: #008ea9;          /* Unity teal - primary */
  --brand-contrast: #ffffff;
  --accent: #ffbb33;         /* Unity gold - active/selected/focus */
  --accent-contrast: #1c2421;
  --bg: #ffffff;
  --surface: #f3f7f8;
  --text: #1c2421;
  --muted: #5b6b6e;
  --border: #dde6e8;
  --link: #007089;
  --radius: 10px;
  --maxw: 64rem;
  --font-heading: "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body: "Poppins", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

[data-theme="dark"] {
  --brand: #0c2a30;          /* dark-mode header */
  --brand-contrast: #e7ecee;
  --accent: #ffbb33;
  --accent-contrast: #11181a;
  --bg: #0d1416;
  --surface: #16201f;
  --text: #e7ecee;
  --muted: #9bafb2;
  --border: #28363a;
  --link: #3fb6c9;           /* dark-mode accent */
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-body);
  line-height: 1.55;
  color: var(--text);
  background: var(--bg);
}

h1, h2, h3, h4, .year-heading, .brand {
  font-family: var(--font-heading);
}

a { color: var(--link); }

/* Visible gold focus ring on every interactive control (#165). Buttons, links,
   inputs, and the iframe all share the brand-gold outline; the offset keeps it
   clear of the control even on the teal header. */
:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.site-header {
  background: var(--brand);
  color: var(--brand-contrast);
}
.site-header__inner,
.site-main,
.site-footer__inner {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 1rem 1.25rem;
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.site-header a { color: var(--brand-contrast); text-decoration: none; }
.brand { display: inline-flex; align-items: center; }
.brand__logo { display: block; height: 2.4rem; width: auto; }

/* Primary header nav (#164). Sits between the wordmark and the theme toggle. */
.site-nav {
  display: flex;
  gap: 1.1rem;
  margin-left: auto;
  margin-right: 0.5rem;
  font-weight: 600;
}
.site-nav a { padding: 0.2rem 0; border-bottom: 2px solid transparent; }
.site-nav a:hover { border-bottom-color: var(--accent); }

/* Theme toggle (#165): icon-only square button. Shows the moon in light mode
   (tap → dark) and the sun in dark mode (tap → light); CSS picks the icon from
   the <html data-theme> attribute so it works with no-JS first paint too. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--brand-contrast);
  color: var(--brand-contrast);
  border-radius: var(--radius);
  padding: 0.35rem;
  width: 2.2rem;
  height: 2.2rem;
  cursor: pointer;
  font: inherit;
}
.theme-toggle__icon { display: none; }
.theme-toggle__moon { display: block; }            /* light default → offer dark */
[data-theme="dark"] .theme-toggle__moon { display: none; }
[data-theme="dark"] .theme-toggle__sun { display: block; }

.search {
  width: 100%;
  padding: 0.7rem 0.9rem;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}
.search:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}

/* --- Year jump (dropdown + prev/next, scales past the old pill row) ------ */
.year-jump {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 1rem 0 0.5rem;
}
.year-jump__field { display: inline-flex; align-items: center; gap: 0.5rem; }
.year-jump__label { font-weight: 600; color: var(--muted); }
.year-select {
  font: inherit;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  border-radius: var(--radius);
  padding: 0.35rem 0.6rem;
}
.year-step {
  font: inherit;
  line-height: 1;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  border-radius: 999px;
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.year-step:hover { border-color: var(--accent); }

/* --- View switcher (small + discreet, sits below the year jump) ---------- */
.view-switcher {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
  margin: 0 0 1rem;
}
.view-switcher button {
  font: inherit;
  cursor: pointer;
  border: 0;
  background: var(--surface);
  color: var(--text);
  padding: 0.25rem 0.8rem;
}
.view-switcher button + button { border-left: 1px solid var(--border); }
.view-switcher button[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-contrast);
}

/* Compact modifier: smaller and muted until a layout is selected. */
.view-switcher--compact { font-size: 0.78rem; }
.view-switcher--compact button { padding: 0.2rem 0.65rem; color: var(--muted); }
.view-switcher--compact button[aria-pressed="true"] { color: var(--accent-contrast); }

/* List controls row: view switcher + page-size selector (#180). */
.list-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  margin: 0 0 1rem;
}
.page-size {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.78rem;
}
.page-size__label { font-weight: 600; color: var(--muted); }
.page-size-select {
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  border-radius: var(--radius);
  padding: 0.2rem 0.5rem;
}

.result-count {
  margin: 0.75rem 0 0.25rem;
  font-weight: 600;
  color: var(--muted);
}

/* Bottom controls bar (#345): client pagination + page-size selector. The
   pager sits left, the page-size selector right. */
.list-controls--bottom {
  margin: 1.5rem 0 0;
}
.pagination--client {
  margin: 0;
  align-items: center;
  gap: 0.75rem;
}
.pager__btn {
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  border-radius: var(--radius);
  padding: 0.3rem 0.7rem;
}
.pager__btn:disabled { opacity: 0.45; cursor: default; }
.page-summary {
  margin: 0.5rem 0 0;
  font-size: 0.8rem;
  color: var(--muted);
}

/* --- Episode list + view modes ------------------------------------------ */
/* ONE DOM, three layouts. CSS alone shows/hides the thumbnail + summary and
   switches between a flex list and a card grid (issue #161). */
.episode-list { list-style: none; padding: 0; margin: 0; }
/* Stretched-link card (#180): the cover anchor lays out the row; its ::after
   spans the whole .episode so the entire card is clickable. The consumption
   icons sit OUTSIDE the anchor (no nested <a>) and float above the cover via
   z-index so their own links remain clickable. */
.episode { position: relative; }
.episode-list .episode .episode__cover {
  display: flex;
  gap: 0.9rem;
  text-decoration: none;
  color: var(--text);
}
.episode__cover::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
}
.episode-list .episode .episode__cover:hover { background: var(--surface); }
/* Icon row floats above the cover link so each icon link is clickable. */
.episode-list .episode .cmi-row--sm { position: relative; z-index: 1; }
.episode-list .meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.9rem;
  align-items: baseline;
  min-width: 0;
}
.episode-list .date { color: var(--muted); font-variant-numeric: tabular-nums; }
.episode-list .title { font-weight: 600; color: var(--link); }
.episode-list .speaker { color: var(--muted); }
.episode-list .summary-line {
  flex-basis: 100%;
  color: var(--text);
  /* Clamp to two lines with a trailing ellipsis; the whole row is a link to
     the detail page, so tapping the truncated "…" opens the full message. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* Thumbnail + branded fallback tile (no-thumbnail episodes). The width is
   driven by --thumb-w so the enriched icon row can anchor under the thumbnail
   at the same width across breakpoints. */
.thumb { display: block; flex: none; }
.thumb__img,
.thumb__fallback {
  display: block;
  width: var(--thumb-w, 160px);
  aspect-ratio: 16 / 9;
  height: auto;
  object-fit: cover;
  border-radius: var(--radius);
  background: var(--surface);
}
.thumb__fallback {
  background: #008ea9; /* brand teal, fixed regardless of theme */
  display: flex;
  align-items: center;
  justify-content: center;
}
.thumb__wordmark { width: 70%; height: auto; }

/* Enriched (default): thumbnail + stacked meta with summary line. The icon row
   is positioned just below the thumbnail in the thumb column (#180), so the
   .episode lays the cover + icon row out as a flex row and the cover keeps the
   meta/thumb side-by-side inside it. */
.episode-list.view-enriched .episode { border-bottom: 1px solid var(--border); }
.episode-list.view-enriched .episode {
  --thumb-w: 160px;
  display: flex;
  align-items: flex-start;
  padding: 0.8rem 0.2rem;
  /* Reserve a left gutter the full width of the thumbnail so the absolutely
     positioned icon row (which sits under the thumb) never overlaps the meta
     text, and keep enough min-height to hold the thumb + the icon row beneath
     it even when the meta block is short. */
  padding-left: calc(var(--thumb-w) + 0.2rem + 0.9rem);
  min-height: calc(var(--thumb-w) * 9 / 16 + 1.6rem + 0.8rem);
}
.episode-list.view-enriched .episode .episode__cover {
  flex: 1 1 auto;
  align-items: flex-start;
  padding: 0.2rem;
  margin: -0.2rem;
  /* Pull the thumbnail back into the reserved left gutter so it sits at the
     row's left edge; the meta flows in the remaining width. */
  margin-left: calc(-1 * (var(--thumb-w) + 0.9rem) - 0.2rem);
}
.episode-list.view-enriched .episode .episode__cover::after {
  /* Cover the whole .episode card (it is now position:relative on .episode). */
  inset: -0.8rem -0.2rem;
}
.episode-list.view-enriched .meta { flex-direction: column; }
/* Icon row sits in the thumb column, snug under the thumbnail. It is absolutely
   positioned within the position:relative .episode so its vertical placement is
   pinned just below the (fixed aspect-ratio) thumbnail rather than wrapping to a
   flex line below the full-height meta block (#180 gap fix). */
.episode-list.view-enriched .episode .cmi-row--sm {
  position: absolute;
  left: 0.2rem;
  top: calc(0.8rem + var(--thumb-w) * 9 / 16 + 0.5rem);
  width: var(--thumb-w);
  margin: 0;
}

/* Compact: plain text rows, no thumbnail, no summary; icons inline at row end. */
.episode-list.view-compact .episode { border-bottom: 1px solid var(--border); }
.episode-list.view-compact .episode {
  display: flex;
  align-items: baseline;
  gap: 0.5rem 0.9rem;
  padding: 0.6rem 0.2rem;
}
.episode-list.view-compact .episode .episode__cover {
  flex: 1 1 auto;
  align-items: baseline;
  padding: 0;
}
.episode-list.view-compact .thumb,
.episode-list.view-compact .summary-line { display: none; }
/* Icons inline at the end of the line (override the sm "own line" basis). */
.episode-list.view-compact .episode .cmi-row--sm {
  flex: 0 0 auto;
  flex-basis: auto;
  margin: 0;
  align-self: center;
}

/* Grid: thumbnail cards. The in-list year heading spans the row. */
.episode-list.view-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1rem;
}
.episode-list.view-grid .year-heading {
  grid-column: 1 / -1;
  margin: 1rem 0 0;
}
.episode-list.view-grid .episode {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  /* Was overflow:hidden, which clipped the consumption-icon row on any card
     whose thumbnail + title (when the title wrapped to extra lines) filled the
     card height, since the icon row was the last item in a fixed-height column
     and overflowed the content box -> "some cards show no icons" (#180). Clip
     only the thumbnail corners (below) instead of the whole card. */
  display: flex;
  flex-direction: column;
}
.episode-list.view-grid .episode .episode__cover {
  flex-direction: column;
  gap: 0;
  flex: 1 1 auto;
}
.episode-list.view-grid .thumb { width: 100%; overflow: hidden; }
.episode-list.view-grid .thumb__img,
.episode-list.view-grid .thumb__fallback {
  width: 100%;
  border-radius: var(--radius) var(--radius) 0 0;
}
.episode-list.view-grid .meta {
  flex-direction: column;
  padding: 0.7rem 0.8rem 0.9rem;
}
.episode-list.view-grid .summary-line { display: none; }
/* Icon row pinned to the bottom of the card, always visible (no overflow clip),
   spanning the card's padded gutter. */
.episode-list.view-grid .episode .cmi-row--sm {
  margin: 0 0 0.9rem;
  padding: 0 0.8rem;
  flex: none;
}

.year-heading {
  margin: 1.4rem 0 0.4rem;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

.pagination {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin: 1.5rem 0;
}

.video-frame {
  position: relative;
  width: 100%;
  /* Cap at ~two-thirds of the content column so the embed reads as one
     artifact among several, not the whole page. Full width on narrow screens. */
  max-width: 42rem;
  aspect-ratio: 16 / 9;
  background: var(--surface);
  border-radius: var(--radius);
  overflow: hidden;
}
.video-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.summary { background: var(--surface); padding: 1rem; border-radius: var(--radius); }

/* --- Consumption indicators (#168, Version C: mono glyph + gold underline) -
   Per-format availability badges. The glyph is drawn in currentColor, so it
   auto-reverses with the theme (dark glyph on light, light glyph on dark);
   lit uses the foreground color with a gold underline, muted dims to --muted
   with a faint underline. */
.cmi-row {
  display: flex;
  flex-basis: 100%;        /* own line within the flex .meta block */
  align-items: flex-start;
  gap: 0.55rem;
}
.cmi {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  line-height: 1;
}
.cmi__glyph {
  width: var(--cmi-size, 18px);
  height: var(--cmi-size, 18px);
  display: block;
  border-bottom: 2px solid transparent;
  padding-bottom: 2px;
}
.cmi--on { color: var(--text); }
.cmi--on .cmi__glyph { border-bottom-color: var(--accent); }
.cmi--off { color: var(--muted); opacity: 0.4; }
.cmi--off .cmi__glyph { border-bottom-color: var(--border); }
.cmi__label { font-size: 0.72rem; color: var(--muted); }

/* List rows: small, label-free, sits beneath the meta block. */
.cmi-row--sm { --cmi-size: 18px; gap: 0.5rem; margin-top: 0.45rem; }
.cmi-row--sm .cmi__label { display: none; }

/* Detail page: larger, labeled. Sits beside the date+speaker line (#180). */
.cmi-row--lg { --cmi-size: 30px; gap: 1.4rem; margin: 0; flex-basis: auto; flex-wrap: wrap; }

/* Detail meta row: date+speaker line with the lg icon group beside it (#180).
   Wraps below the line on narrow screens. */
.episode-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.5rem;
  margin: 0 0 1rem;
}
.episode-meta-row .episode-meta { margin: 0; }

/* Raw billing string stays muted; each resolvable co-speaker links out as a
   chip (ADR-0009). Chips wrap and read as links beside the verbatim credit. */
.episode-meta .speaker { color: var(--muted); }
.speaker-links {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
}
.speaker-chip { color: var(--link); }
/* --- Series chip (list rows + episode meta line) -------------------------
   ONE component on both surfaces. Speaker chips stay bare links, so "another
   person" and "the container this message belongs to" are visibly different
   kinds of object; the pill shape, the stacked-cards glyph and (on the detail
   page) "Part N of M" carry that difference on four channels, none of them hue.
   On a LIST ROW the series is a second destination, so it CANNOT live inside
   the row's stretched cover anchor (no nested <a>). It sits OUTSIDE the anchor
   as a sibling of .episode__cover / .cmi-row--sm, and each view mode places it.
   .episode__series is layout only and is intentionally NOT z-indexed: the empty
   space beside the pill stays under the cover's ::after, so it still navigates
   to the message. Only the pill is lifted, so its own click wins. */
.episode__series { display: flex; align-items: center; min-width: 0; --chip-size: 0.78rem; }

.series-chip {
  position: relative;
  z-index: 1;                       /* above .episode__cover::after (z-index: 0) */
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;                  /* long names wrap INSIDE the pill on detail */
  vertical-align: middle;
  gap: 0.15rem 0.45rem;
  max-width: 100%;
  min-width: 0;
  padding: 0.28rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  /* --bg, not --surface: a hovered list row paints the card --surface, and the
     pill must stay legible as a cut-out on top of it. */
  background: var(--bg);
  color: var(--link);
  font-size: var(--chip-size, 0.92rem);
  font-weight: 600;
  font-style: normal;
  line-height: 1.35;
  text-decoration: none;
}
.series-chip__mark { width: 1em; height: 1em; flex: none; display: block; }
.series-chip__count {
  color: var(--muted);
  font-weight: 400;
  font-size: 0.85em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.series-chip:hover { border-color: var(--accent); }
.series-chip:hover .series-chip__name { text-decoration: underline; }
/* The global :focus-visible ring forces border-radius: 4px; restate it so the
   gold outline traces the pill instead of squaring it off. */
.series-chip:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
  border-radius: 999px;
}
/* List rows only: one line, ellipsised. `title` carries the full name. On the
   detail page the name wraps normally - a truncated series name is worse than
   a two-line pill, because the name is what a returning listener recognises. */
.episode__series .series-chip__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Enriched (default): own flex line under the meta column, flush with the
   title's left edge (the row already reserves a left gutter the width of the
   thumbnail). `flex: 0 0 100%` can never share a line with the cover, so it
   always wraps; the cover keeps `flex: 1 1 auto` and its negative left margin
   untouched. The absolutely positioned .cmi-row--sm lives in the left thumb
   gutter, so the two never collide and no calc() changes. */
.episode-list.view-enriched .episode { flex-wrap: wrap; }
.episode-list.view-enriched .episode__series {
  flex: 0 0 100%;
  margin-top: 0.45rem;
}

/* Compact: inline at the end of the row, just before the icon cluster. */
.episode-list.view-compact .episode__series {
  flex: 0 0 auto;
  align-self: center;
}
.episode-list.view-compact .series-chip { max-width: 12rem; }

/* Grid: its own band in the card gutter, between the meta block and the pinned
   icon row. The card is a flex column, so `flex: none` keeps natural height. */
.episode-list.view-grid .episode__series {
  flex: none;
  padding: 0 0.8rem;
  margin: -0.15rem 0 0.7rem;
}

/* Touch: a real 40px box rather than an invisible hit-area halo, which would
   sit above the cover link and silently steal clicks from the row around it. */
@media (pointer: coarse) {
  .series-chip { min-height: 40px; padding-block: 0.45rem; }
}

/* Icons are links/spans; keep glyphs un-decorated and color-stable. */
.cmi { text-decoration: none; }
a.cmi.cmi--on { color: var(--text); }
a.cmi.cmi--on:hover .cmi__glyph { border-bottom-color: var(--accent); }
a.cmi.cmi--on:hover { color: var(--link); }

.site-footer {
  border-top: 1px solid var(--border);
  margin-top: 3rem;
  color: var(--muted);
}
.site-footer a { color: var(--link); }
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  list-style: none;
  padding: 0;
}

.muted { color: var(--muted); }
.hidden { display: none !important; }
/* Screen-reader-only text. Used for the "Series:" chip qualifier and the
   arc-card heading qualifier - kept in the accessible name, off the screen.
   Never display:none, which would remove it from the accessible name. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Episode share button (native share sheet / copy-link fallback). */
.share-row { display: flex; align-items: center; gap: 0.75rem; }
.btn-share {
  font: inherit;
  cursor: pointer;
  color: var(--brand-contrast);
  background: var(--brand);
  border: 1px solid var(--brand);
  border-radius: var(--radius);
  padding: 0.45rem 1.1rem;
}
.btn-share:hover { background: var(--link); border-color: var(--link); }
.btn-share:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
.share-status { color: var(--muted); font-size: 0.9rem; }

/* Chronological neighbor links at the foot of an episode page. */
.episode-neighbors {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 2.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}
.episode-neighbors a {
  display: flex;
  flex-direction: column;
  max-width: 48%;
}
.episode-neighbors__newer { text-align: right; margin-left: auto; }
.episode-neighbors .neighbor-title {
  color: var(--muted);
  font-size: 0.9rem;
}

/* --- Speaker directory (#164) ------------------------------------------- */
.speaker-directory { list-style: none; padding: 0; margin: 1.25rem 0 0; }
.speaker-directory__item { border-bottom: 1px solid var(--border); }
.speaker-directory__item a {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding: 0.7rem 0.25rem;
  text-decoration: none;
  color: var(--text);
}
.speaker-directory__item a:hover { background: var(--surface); }
.speaker-directory__name { font-weight: 600; color: var(--link); }
.speaker-directory__count {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* --- Series directory: first-aired date + client-side sort --------------- */
/* Borrows .speaker-directory wholesale; __meta is the right-hand column that
   now carries two facts (first aired + message count) instead of one. */
.speaker-directory__meta {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
}
.series-directory__first-aired {
  color: var(--muted);
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Sort switcher reuses .view-switcher; the arrow is the only extra. */
.sort-switcher { margin-top: 1rem; }
.sort-switcher__arrow { font-size: 0.85em; }

/* --- Series arc card (episode detail) ------------------------------------
   The series' installments in watch order with the current one marked. Sits
   below the player/summary so it never displaces them on mobile. Long series
   collapse to a window around the current part (class toggled by app.js); with
   JS off every item is visible.
   DO NOT make a row a stretched-link card: the rows sit in a block that also
   contains the series-page link in the heading, so an ::after overlay would
   swallow it and re-create exactly the nested-affordance hazard the list
   partial has to work around. Each row is its own bounded, padded anchor. */
.series-contents {
  margin: 2.25rem 0 0;
  padding: 0.9rem 1rem 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.series-contents__title { margin: 0 0 0.7rem; font-size: 1.05rem; line-height: 1.3; }
.series-contents__title a { text-decoration: none; }
.series-contents__title a:hover { text-decoration: underline; }
.series-contents__position {
  display: block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 0.85rem;
  color: var(--muted);
}
.series-contents__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.15rem;
}
/* Wide enough for two (or three) columns: a 14-part arc reads in one glance
   instead of a scroll. Row-major flow keeps 1,2 / 3,4 reading order. */
@media (min-width: 48rem) {
  .series-contents__list {
    grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
    gap: 0.15rem 1rem;
  }
}
/* One row. min-height keeps every tap target well over the 40px floor. */
.series-contents__link {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  min-height: 44px;
  padding: 0.5rem 0.6rem;
  border-left: 3px solid transparent;
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--text);
}
a.series-contents__link:hover { background: var(--bg); }
.series-contents__num {
  flex: none;
  min-width: 1.4em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.series-contents__label { flex: 1 1 auto; min-width: 0; color: var(--link); }
.series-contents__date {
  flex: none;
  margin-left: auto;
  padding-left: 0.4rem;
  color: var(--muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Current installment: gold bar + raised surface, not a link. */
.series-contents__item.is-current .series-contents__link {
  border-left-color: var(--accent);
  background: var(--bg);
  font-weight: 600;
  cursor: default;
}
.series-contents__item.is-current .series-contents__label { color: var(--text); }
.series-contents__you {
  flex: none;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
/* Collapsed (long series, JS on): only the +/-2 window stays in flow. */
.series-contents--collapsed .series-contents__item:not(.is-near) { display: none; }
.series-contents__foot { margin: 0.8rem 0 0; }
/* Reuses .pager__btn for the chrome; [hidden] must win over its display rule. */
.series-contents__toggle[hidden] { display: none; }

/* Windows High Contrast drops author backgrounds, so the current row's gold bar
   and raised fill would both vanish and "you are here" would rest on text
   alone. Restore the position cue with system colors. */
@media (forced-colors: active) {
  .series-contents__item.is-current .series-contents__link {
    border-left-color: Highlight;
    forced-color-adjust: none;
    border-left-style: solid;
  }
}

/* --- Motion (#165) ------------------------------------------------------- */
/* Subtle hover/active transitions on interactive controls, gated entirely
   behind a motion preference so reduced-motion users get instant state
   changes. Nothing animates unless the user is OK with motion. */
@media (prefers-reduced-motion: no-preference) {
  .theme-toggle,
  .view-switcher button,
  .year-step,
  .year-select,
  .btn-share,
  .site-nav a,
  a {
    transition: background-color 0.15s ease, color 0.15s ease,
                border-color 0.15s ease, box-shadow 0.15s ease;
  }
}

/* --- Responsive (#165) --------------------------------------------------- */
/* Stack the controls row on narrow screens; switcher above year pills, each
   full-width so taps are easy. The card grid already auto-fills; on the
   smallest screens drop it to a single column so cards never get cramped. */
@media (max-width: 36rem) {
  .site-header__inner { gap: 0.5rem; }
  .site-nav { gap: 0.75rem; margin-right: 0.25rem; }

  .year-jump { flex-wrap: wrap; }
  .view-switcher--compact { width: 100%; }
  .view-switcher--compact button { flex: 1; text-align: center; }

  .series-directory .speaker-directory__item a {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.2rem;
  }
  .series-directory .speaker-directory__meta { justify-content: flex-start; }

  /* Enriched rows: shrink the thumbnail so the title has room. Driving
     --thumb-w keeps the left gutter, min-height, and the icon row's offset (all
     calc'd off --thumb-w) in sync with the smaller thumbnail. */
  .episode-list.view-enriched .episode { --thumb-w: 116px; }

  /* Compact rows are already tight; a chip plus five icons will not share a
     line with the title on a phone. Let the row wrap: cover on line 1, chip +
     icons on line 2. */
  .episode-list.view-compact .episode { flex-wrap: wrap; }
  .episode-list.view-compact .episode .episode__cover { flex: 1 1 100%; }
  .episode-list.view-compact .series-chip { max-width: 60vw; }

  .series-contents { padding: 0.8rem 0.8rem 0.9rem; }
  /* Order is what matters inside a series; the date is on the destination
     page. Dropping it keeps title + "You are here" on one line at 320px. */
  .series-contents__date { display: none; }

  .episode-list.view-grid {
    grid-template-columns: 1fr;
  }

  .episode-neighbors { flex-direction: column; }
  .episode-neighbors a { max-width: 100%; }
  .episode-neighbors__newer { text-align: left; margin-left: 0; }

  .pagination { flex-wrap: wrap; }
}
