/* Latest episodes: a wrap-around coverflow of the 12 latest videos (client decision
   2026-09-29, SKILL.md 0.3; rebuilt 2026-10-06, "more 3D animated"; quieter 2026-10-07,
   second round). Five cards show at rest, two each side of the centre, the deck wrapping so
   the newest episode has neighbours on its left too. The stage sits on the deep band with
   no box; depth comes from a 1600px perspective, a page-coloured veil on the neighbours
   and one soft ambient shadow per card, strongest under the centre. No frame, no lit edge,
   no rule, no reflection: the only lines are the ones in the thumbnails themselves.
   Every moving value is a transform or an opacity (episodes.js writes them inline; GSAP
   scrubs the deck's rotateY and y with the scroll and tilts the centre card toward a fine
   pointer). Under prefers-reduced-motion or html.motion-paused the same markup becomes a
   horizontal scroll-snap row (.episodes--static): no 3D, no scrub, no tilt.

   Third pass, 2026-10-07 ("no premiumness"): the deck gets a stage. The centre card is larger
   (up to 620px), every card throws a faint reflection on the floor (a flipped copy of its
   picture), the centre card carries a soft sheen, a gold "Play" disc follows a fine pointer
   over it (episodes.js), the title is set in the poster face and changes by sliding through a
   mask, and the counter and the buttons are larger. Faces come from sections.css (--f-poster,
   --f-text through --font-body). */

.episodes {
  /* Card geometry. --ep-x1/--ep-x2 are the neighbour offsets as fractions of the card width,
     solved so each neighbour overlaps the next card in by about 12% under the 1600px
     perspective (slots at scale .78/.6, rotateY 28/40, translateZ -160/-320). The centre
     card is 12% smaller than the first build (528px, not 600: the deck is not the loudest
     thing on the page), so five cards sit inside a 1440px screen with air at its edges.
     episodes.js reads the offsets once per resize. */
  --ep-w: min(40vw, 620px, 54vh); /* capped by height so the deck and its foot fit the viewport a nav link lands on */
  --ep-h: calc(var(--ep-w) * 9 / 16);
  --ep-x1: 0.78;
  --ep-x2: 1.34;
  /* The caption and controls under the stage, reserved with it: the foot's margin (--ep-gap),
     the meta row (17), the title (16 above, two lines at its fluid size and 1.08 leading), the
     link (12 above, 44). Measured within 4px at 1440 and 1024. */
  --ep-title: clamp(30px, 3.1vw, 46px);
  --ep-gap: clamp(40px, 5vw, 72px);
  --ep-foot: calc(var(--ep-gap) + 89px + 2.16 * var(--ep-title));
  position: relative;
  /* The stage height is reserved before the feed answers (and kept if it never does), so
     the carousel mounting late moves nothing below it. */
  min-height: calc(var(--ep-h) + 2 * var(--s-3) + var(--ep-foot));
}
@supports (width: 1svh) {
  .episodes { --ep-w: min(40vw, 620px, 54svh); }
}
/* The empty state (the feed not yet answered, empty or unreachable, or JS off): one panel
   at the centre card's size with the channel link inside it, no frame. episodes.js replaces
   it when the feed arrives. */
.episodes__empty {
  display: grid; align-content: center; justify-items: start; gap: var(--s-3);
  width: var(--ep-w); height: var(--ep-h);
  margin: var(--s-3) auto 0;
  padding: var(--s-6);
  background: var(--panel);
}
.episodes__empty-title { font: 400 clamp(22px, 2vw, 28px) / 1.25 var(--font-display); letter-spacing: -0.015em; color: var(--ink); max-width: 20ch; text-wrap: balance; }

/* Stage: bleeds to the viewport edge so the outer pair is clipped by the screen, not by
   the gutter (clip on x only: the ambient shadow falls below the track uncut). The
   perspective sits here, centred; the deck (the track) is what the scroll scrubs, so it
   keeps its children in 3D. Vertical pans stay with the page (touch-action). */
.episodes__stage {
  position: relative;
  margin-inline: calc(-1 * var(--gutter));
  overflow-x: clip;
  overflow-y: visible;
  perspective: 1600px;
  perspective-origin: 50% 50%;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}
.episodes__track {
  position: relative;
  height: calc(var(--ep-h) + 2 * var(--s-3)); /* the card and 12px above and below it */
  transform-style: preserve-3d;
  /* The track's own box lies in the same plane as the centre card, and Chrome's hit test
     picked one or the other from point to point (measured: at some scroll positions a click
     in the middle of the centre card landed on the <ul> and opened nothing). The track
     takes no pointer events; its cards do, and the gaps fall through to the stage. */
  pointer-events: none;
}
.episodes__card { pointer-events: auto; }
.episodes__card {
  position: absolute;
  top: var(--s-3);
  left: calc(50% - var(--ep-w) / 2);
  width: var(--ep-w);
  height: var(--ep-h);
  backface-visibility: hidden;
}
/* Promoted only while a drag or a snap runs (episodes.js toggles .is-moving). The dim and
   the shadow get their own layers too: their opacity is written from JS every frame, and
   without a layer each write would repaint the whole card. */
.episodes__track.is-moving .episodes__card { will-change: transform; }
.episodes__track.is-moving :is(.episodes__dim, .episodes__shadow, .episodes__mirror) { will-change: opacity; }
/* No frame: the thumbnails are loud YouTube artwork and the serif title under the stage
   carries the typography. Depth is the ambient shadow and the veil on the neighbours. */
.episodes__card a {
  display: block;
  position: relative;
  width: 100%;
  height: 100%;
  background: var(--panel);
  -webkit-user-drag: none;
}
.episodes__card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;
}
/* The floor: each card throws a short reflection. It is a second, flipped copy of the
   thumbnail under the card (episodes.js builds it, hidden from assistive tech), faded out by
   a mask and dimmed with its card by episodes.js. A copy rather than -webkit-box-reflect, so
   Firefox shows it too and it is painted into the card's own layer. The centre card carries
   a soft diagonal sheen. */
.episodes__mirror {
  position: absolute; top: calc(100% + 10px); left: 0; z-index: -1;
  display: block; width: 100%; height: 42%; overflow: hidden;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,.26) 0%, rgba(0,0,0,.08) 55%, rgba(0,0,0,0) 100%);
  mask-image: linear-gradient(to bottom, rgba(0,0,0,.26) 0%, rgba(0,0,0,.08) 55%, rgba(0,0,0,0) 100%);
}
.episodes__mirror img { position: absolute; left: 0; bottom: 0; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; transform: scaleY(-1); transform-origin: 50% 50%; }
.episodes__mirror img:not([src]) { visibility: hidden; }
.episodes--static .episodes__mirror { display: none; }
.episodes__card a::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(118deg, rgba(255,246,222,.2) 0%, rgba(255,246,222,0) 34%, rgba(255,246,222,0) 70%, rgba(255,246,222,.08) 100%);
  opacity: 0; transition: opacity var(--t-layout) var(--ease-out);
}
.episodes__card.is-active a::after { opacity: 1; }
/* "Watch" on the centre card: a round play mark and the word on an espresso band, so the
   action is on the picture itself (the audience is mostly 55+). It fades in with the active
   state; episodes.js builds it. */
.episodes__play {
  position: absolute; left: var(--s-4); bottom: var(--s-4); z-index: 1;
  display: inline-flex; align-items: center; gap: 10px;
  padding: 5px 16px 5px 5px; border-radius: 999px;
  background: rgba(18,13,9,.58);
  -webkit-backdrop-filter: blur(12px) saturate(1.25); backdrop-filter: blur(12px) saturate(1.25);
  box-shadow: inset 0 0 0 1px rgba(224,168,62,.6), inset 0 1px 0 rgba(255,246,228,.14), 0 12px 26px -14px rgba(0,0,0,.75);
  color: var(--ink);
  opacity: 0; transition: opacity var(--t-state) var(--ease-out);
  pointer-events: none;
}
/* A gold coin with the play mark struck into it; the word in tracked capitals beside it. */
.episodes__play-mark {
  display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%;
  background: radial-gradient(120% 120% at 30% 22%, #F4CF7A 0%, var(--gold) 46%, #B98322 100%);
  color: var(--gold-ink);
  box-shadow: inset 0 1px 0 rgba(255,244,214,.55), inset 0 -2px 3px rgba(96,60,8,.45);
}
.episodes__play-mark svg { display: block; margin-left: 2px; } /* the triangle's optical centre */
.episodes__play-label { font: 600 11px/1 var(--f-text, var(--font-body)); letter-spacing: 0.18em; text-transform: uppercase; }
.episodes__card.is-active .episodes__play { opacity: 1; }
/* Neighbours dim through a page-coloured veil (0.45 then 0.65, which reads as opacity .55
   and .35 without showing the card behind through a transparent one). */
.episodes__dim {
  position: absolute;
  inset: 0;
  background: var(--bg);
  opacity: 0;
  pointer-events: none;
}
/* The ambient shadow: two soft, offset layers deeper than the band, full under the centre
   card and fading with distance from it (episodes.js writes the opacity). */
.episodes__shadow {
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Same reach as before (48 and 96px of blur): a wider second layer made the neighbours'
     layers cross the centre card's plane in Chrome's 3D sort, and a 5px strip of the
     neighbour showed through the centre card. Deeper by alpha instead. */
  box-shadow: 0 24px 48px -12px rgba(8,5,3,.78), 0 56px 96px -32px rgba(8,5,3,.72);
  pointer-events: none;
}
/* Keyboard focus: the region's ring sits on the centre card (the stage bleeds past the
   viewport, so a ring on the stage itself would read as two stray lines), and a card
   link keeps its own ring. */
.episodes__stage:focus-visible { outline: none; }
.episodes__stage:focus-visible .episodes__card.is-active a,
.episodes__card a:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

/* The "Play" disc: a gold coin of a cursor that follows a fine pointer over the centre card
   (episodes.js moves it with a transform and sets .is-on). Decoration only: the card is still
   a link with its own name, and touch and keyboard keep the "Watch" plate. */
.episodes__cursor { position: absolute; top: 0; left: 0; z-index: 200; width: 0; height: 0; pointer-events: none; }
/* The disc is a child, so its own scale and opacity stay with CSS while GSAP owns the
   parent's transform. */
.episodes__cursor-disc {
  position: absolute; top: -54px; left: -54px;
  display: grid; place-items: center;
  width: 108px; height: 108px;
  border-radius: 50%;
  /* smoked glass with a gold rim: the picture stays visible through it */
  background: radial-gradient(90% 90% at 32% 24%, rgba(255,240,210,.16) 0%, rgba(255,240,210,0) 46%), rgba(16,11,7,.52);
  -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
  box-shadow: inset 0 0 0 1px rgba(224,168,62,.9), inset 0 0 0 5px rgba(16,11,7,.22), inset 0 1px 0 rgba(255,246,228,.22), 0 22px 44px -16px rgba(0,0,0,.8);
  color: var(--ink);
  opacity: 0; scale: 0.5;
  transition: opacity var(--t-state) var(--ease-out), scale 420ms cubic-bezier(0.16, 1, 0.3, 1);
}
.episodes__cursor-ring { position: absolute; inset: 0; width: 100%; height: 100%; }
.episodes__cursor-ring text { font: 600 10.5px / 1 var(--f-text, var(--font-body)); letter-spacing: 0.02em; text-transform: uppercase; fill: rgba(246,238,222,.86); }
/* the coin at the centre: struck gold, the play mark in espresso */
.episodes__cursor-coin {
  position: relative; display: grid; place-items: center; width: 42px; height: 42px; border-radius: 50%;
  background: radial-gradient(120% 120% at 30% 22%, #F6D484 0%, var(--gold) 46%, #B47F20 100%);
  color: var(--gold-ink);
  box-shadow: inset 0 1px 0 rgba(255,244,214,.6), inset 0 -3px 5px rgba(96,60,8,.45), 0 6px 14px -6px rgba(0,0,0,.6);
}
.episodes__cursor-coin svg { width: 15px; height: 15px; margin-left: 2px; }
.episodes__cursor.is-on .episodes__cursor-disc { opacity: 1; scale: 1; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .episodes__cursor-disc { background: rgba(16,11,7,.82); }
  .episodes__play { background: rgba(18,13,9,.84); }
}
.episodes__cursor svg { display: block; }
.episodes--static .episodes__cursor { display: none; }

/* Under the stage (client, 2026-10-07: "episode 12 and there's a line on the side ... not
   premium"): one small-caps line, "Episode 12 · 3 October 2026" (the date when the feed
   carries one), the serif title and a quiet underlined link on the left (1-7); on the right
   (9-12) a thin tabular counter, "12 / 12", beside the two round buttons. No rule, no dashes. */
.episodes__foot {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--s-5);
  row-gap: var(--s-5);
  align-items: start;
  margin-top: var(--ep-gap); /* clear of the reflection on the floor */
}
.episodes__caption { grid-column: 1 / 8; }
.episodes__meta {
  font: 500 17px/1 var(--font-body);
  font-variant-caps: all-small-caps; letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.episodes__sep { margin-inline: 0.35em; }
.episodes__title-clip { display: block; overflow: clip; margin-top: var(--s-4); padding-bottom: 0.1em; margin-bottom: -0.1em; }
.episodes__title {
  margin: 0;
  font: 400 var(--ep-title) / 1.08 var(--f-poster, var(--font-display)); letter-spacing: -0.018em;
  max-width: 24ch;
  min-height: 2.16em; /* two lines: the row below does not move when a title is shorter */
  color: var(--ink);
  text-wrap: balance;
}
/* Title change: the old title leaves upward through the mask (180ms), the new one rises in
   from below (560ms on the page's expo curve). episodes.js sets .is-swapping, swaps the text
   under .is-below, then lets it go. With motion off the text simply changes. */
.episodes__title.is-swapping { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  html:not(.motion-paused) .episodes__title { transition: translate 560ms cubic-bezier(0.16, 1, 0.3, 1), opacity 320ms var(--ease-out); }
  html:not(.motion-paused) .episodes__title.is-swapping { translate: 0 -45%; transition: translate 180ms cubic-bezier(0.5, 0, 0.9, 0.4), opacity 180ms linear; }
  html:not(.motion-paused) .episodes__title.is-below { translate: 0 60%; opacity: 0; transition: none; }
}
.episodes__watch { display: inline-block; margin-top: var(--s-3); color: var(--ink-85); }

.episodes__controls {
  grid-column: 9 / -1;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--s-5);
}
/* The counter: light, tabular, the same height as the buttons so the row never shifts. */
.episodes__counter { font: 400 30px/56px var(--f-poster, var(--font-display)); letter-spacing: 0; font-variant-numeric: lining-nums tabular-nums; color: var(--ink-85); white-space: nowrap; }
.episodes__buttons { display: flex; gap: var(--s-3); }
/* Round, like the scroll cue's arrow: the page's one shape for "a step". */
.episodes__button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px;
  border: 1.5px solid var(--line-strong); border-radius: 50%;
  color: var(--ink);
  transition: color var(--t-state) var(--ease-out), border-color var(--t-state) var(--ease-out), background-color var(--t-state) var(--ease-out), scale var(--t-press) var(--ease-out);
}
.episodes__button:active { scale: 0.96; }
.motion-paused .episodes__button:active { scale: 1; }
.episodes__button[aria-disabled="true"] { color: var(--line-strong); border-color: var(--line); cursor: default; }
.episodes__button svg { display: block; }

@media (hover: hover) {
  .episodes__button:not([aria-disabled="true"]):hover { color: var(--gold-ink); border-color: var(--gold); background: var(--gold); }
  .episodes__watch:hover { text-decoration-color: currentColor; }
}
@media (hover: hover) and (pointer: fine) {
  /* The disc is the affordance under a fine pointer; the plate returns for keyboard focus. */
  .episodes:not(.episodes--static) .episodes__card.is-active .episodes__play { opacity: 0; }
  .episodes:not(.episodes--static) .episodes__stage:focus-visible .episodes__card.is-active .episodes__play,
  .episodes:not(.episodes--static) .episodes__card.is-active a:focus-visible .episodes__play { opacity: 1; }
  .episodes__stage { cursor: grab; }
  .episodes__stage.is-dragging { cursor: grabbing; }
  .episodes__card:not(.is-active) a { cursor: pointer; }
}

/* Static mode (reduced motion, the footer toggle, or a device that asks): a
   scroll-snap row, three frames per view on desktop, 1.2 on phones. The inline
   transforms are cleared by episodes.js when this class lands. */
.episodes--static .episodes__stage { margin-inline: 0; overflow: visible; perspective: none; touch-action: auto; user-select: auto; cursor: auto; }
.episodes--static .episodes__track {
  pointer-events: auto;
  display: flex;
  gap: var(--s-4);
  height: auto;
  transform: none !important;
  transform-style: flat;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  padding-bottom: var(--s-2);
  /* The row bleeds to the gutter edge; the first card still lines up with the heading. */
  margin-inline: calc(-1 * var(--gutter));
  padding-inline: var(--gutter);
  scroll-padding-inline: var(--gutter);
}
.episodes--static .episodes__track::-webkit-scrollbar { display: none; }
.episodes--static .episodes__card {
  position: static;
  flex: 0 0 calc((100% - 2 * var(--s-4)) / 3);
  width: auto;
  height: auto;
  aspect-ratio: 16 / 9;
  scroll-snap-align: start;
  transform: none !important;
  opacity: 1 !important;
  visibility: visible !important;
}
.episodes--static .episodes__dim { opacity: 0 !important; }
.episodes--static .episodes__shadow { display: none; }
.episodes--static .episodes__title { transition: none; translate: none; }
.episodes--static .episodes__play { opacity: 1; }
/* The row is shorter than the stage: nothing is reserved under reduced motion or the toggle,
   and the empty frame spans the row instead. */
.episodes--static, .motion-paused .episodes { min-height: 0; }
@media (prefers-reduced-motion: reduce) {
  .episodes { min-height: 0; }
  .episodes__empty { width: auto; height: auto; margin-inline: 0; }
}
.motion-paused .episodes__empty { width: auto; height: auto; margin-inline: 0; }

@media (max-width: 900px) {
  /* The controls drop under the caption: their row (56) and the gap (24) join the reserved foot. */
  .episodes { --ep-foot: calc(var(--ep-gap) + 169px + 2.16 * var(--ep-title)); }
  .episodes__caption, .episodes__controls { grid-column: 1 / -1; }
  .episodes__controls { justify-content: space-between; }
}
@media (min-width: 601px) and (max-width: 900px) {
  /* Tablets in portrait: the deck is the hero there too (the card was 40vw). */
  .episodes { --ep-w: min(56vw, 620px, 50vh); }
  @supports (width: 1svh) { .episodes { --ep-w: min(56vw, 620px, 50svh); } }
}
@media (max-width: 600px) {
  /* The deck is the hero on a phone (2026-10-08): the centre card is 78% of the screen (it
     was 66%), with about 11vw of angled neighbour each side at rest (the peek is bounded by
     (100vw - card) / 2). The arrows are 60px and the counter grows with them; their row (60)
     and the gap (24) join the reserved foot. A title takes three lines at most here (measured
     on the 12 latest at 360-430px), and three are reserved, so the counter and the arrows
     never move under the thumb as the deck turns (with two they stepped down 32px whenever a
     long title came round). */
  .episodes { --ep-w: 78vw; --ep-x1: 0.8; --ep-x2: 1.38; --ep-title: 30px; --ep-gap: 40px; --ep-foot: calc(var(--ep-gap) + 177px + 3.24 * var(--ep-title)); }
  .episodes__title { min-height: 3.24em; }
  .episodes__play { left: var(--s-3); bottom: var(--s-3); }
  .episodes__play-mark { width: 30px; height: 30px; }
  .episodes__empty { width: auto; margin-inline: 0; padding: var(--s-5); }
  .episodes--static .episodes__card { flex-basis: calc((100% - var(--s-4)) / 1.2); }
  .episodes__button { width: 60px; height: 60px; }
  .episodes__counter { font-size: 34px; line-height: 60px; }
  .episodes__watch { min-height: 48px; padding-block: 12px; }
}
/* Touch: the stage and its cards take no tap highlight (the deck answers itself), and the
   counter ticks as the episode changes (episodes.js swaps the two classes). */
@media (hover: none), (pointer: coarse) {
  .episodes__stage, .episodes__card a { -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; }
  /* the centre card under a thumb: the play coin sinks (the plate itself is unchanged) */
  .episodes__play-mark { transition: scale var(--t-press) var(--ease-out); }
  .episodes__card.is-active a:active .episodes__play-mark { scale: 0.9; }
  .motion-paused .episodes__card.is-active a:active .episodes__play-mark { scale: 1; }
}
@media (hover: none) and (prefers-reduced-motion: no-preference), (pointer: coarse) and (prefers-reduced-motion: no-preference) {
  html:not(.motion-paused) .episodes__counter.is-tick-a { animation: ep-tick-a 380ms cubic-bezier(0.16, 1, 0.3, 1) both; }
  html:not(.motion-paused) .episodes__counter.is-tick-b { animation: ep-tick-b 380ms cubic-bezier(0.16, 1, 0.3, 1) both; }
  @keyframes ep-tick-a { from { opacity: 0.3; transform: translateY(9px); } to { opacity: 1; transform: none; } }
  @keyframes ep-tick-b { from { opacity: 0.3; transform: translateY(9px); } to { opacity: 1; transform: none; } }
}
