/* ============================================================================
 * /watch/ — Multi-show smart-TV theme
 * ============================================================================
 * Two views, one stylesheet: a landing-page show grid and a per-show player
 * with a chunky flat-screen-TV chassis. The accent colour (LED, chip, button
 * highlights) is set via the `--tv-accent` custom property on :root from JS
 * so each show has its own visual signature.
 * ========================================================================= */

:root {
  --tv-bezel: #0e0e10;
  --tv-bezel-edge: #050507;
  --tv-bezel-trim: #1d1d22;
  --tv-screen-off: #050608;
  --tv-screen-tint: #0a0f14;
  --tv-stand: #2a2a30;
  --tv-stand-dark: #14141a;
  --tv-led: #c0ff5a;
  --tv-led-glow: rgba(192, 255, 90, 0.55);
  --tv-led-red: #ff4b3a;
  --tv-led-red-glow: rgba(255, 75, 58, 0.6);
  --tv-accent: #ffb800;
  --tv-accent-dim: #c98a00;
  --tv-accent-glow: rgba(255, 184, 0, 0.45);
  --tv-chip-bg: #1b1b22;
  --tv-chip-border: #2a2a35;
  --tv-chip-active: #ffb800;
  --tv-card-bg: #15151a;
  --tv-card-border: #24242c;
  --tv-text: #f5f5f7;
  --tv-text-dim: #9a9aa3;
  --tv-text-dimmer: #65656e;
  --font-screen: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, monospace;
}

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

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--font-screen);
  color: var(--tv-text);
  background: radial-gradient(ellipse at 50% 0%, #1a1a22 0%, #0b0b10 60%, #050507 100%);
  overflow-x: hidden;
}

.hidden {
  display: none !important;
}

.tv-room {
  min-height: 100vh;
  /* Top padding clears the fixed .tv-header (~52px tall on desktop)
     plus a small visual gap. The back button (z-index 999999) floats
     ON TOP of the header, so we don't need extra clearance for it. */
  padding: 76px 16px 96px;
  max-width: 1280px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 20px;
}

/* ------------------------------------------------------------------------ */
/* Header + breadcrumbs                                                     */
/* ------------------------------------------------------------------------ */

/* The header is fixed to the viewport top so the breadcrumb trail
   stays reachable while scrolling through long episode grids. The
   translucent backdrop + blur keep content readable as it scrolls
   under. z-index sits below the back button (999999) so the back
   pill floats over the bar rather than being clipped by it. */
.tv-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--tv-screen-off) 78%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--tv-card-border);
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

.tv-breadcrumbs {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Single-line trail. A fixed header that grows when breadcrumbs
     wrap would crowd content underneath and require the .tv-room
     top padding to be dynamic — neither is worth it. Long episode
     titles instead truncate via the .tv-crumb ellipsis below. */
  flex-wrap: nowrap;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.5px;
  color: var(--tv-text-dim);
  /* Match .tv-room's centering so the breadcrumbs visually line up
     with the page content. The left padding clears the back button
     (~100px wide at top-left); the right side gets a small breathing
     gap. Centered inner block keeps wide viewports looking tidy. */
  max-width: 1280px;
  width: 100%;
  margin: 0 auto;
  padding: 14px 18px 14px 130px;
  box-sizing: border-box;
}

.tv-crumb {
  color: var(--tv-text-dim);
  text-decoration: none;
  border-bottom: 1px dotted transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
  /* Truncate gracefully when the trail outgrows the available width
     (mostly the current-episode crumb on narrow viewports). */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.tv-crumb-sep {
  flex: 0 0 auto;
}

/* The trailing "current" crumb soaks up remaining space so the
   ellipsis lands on it (the episode title) instead of the show
   name, which is shorter and useful as a backup to read. */
.tv-crumb--current {
  flex: 1 1 auto;
}

a.tv-crumb:hover {
  color: var(--tv-text);
  border-bottom-color: var(--tv-accent-dim);
}

.tv-crumb--current {
  color: var(--tv-text);
  font-weight: 600;
}

.tv-crumb-sep {
  color: var(--tv-text-dimmer);
  font-size: 14px;
}

.tv-crumb-back {
  appearance: none;
  background: transparent;
  border: 1px solid var(--tv-card-border);
  color: var(--tv-text-dim);
  font-family: var(--font-mono);
  font-size: 14px;
  text-decoration: none;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  /* Don't let flex shrink the back arrow — it's a fixed-size pill. */
  flex: 0 0 auto;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.tv-crumb-back:hover {
  color: var(--tv-text);
  border-color: var(--tv-accent-dim);
  background: color-mix(in srgb, var(--tv-accent) 8%, transparent);
}

/* The mounted view fills whatever's left of the room. Views own their own
   spacing so the slot itself stays minimal. */
.tv-view {
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-height: clamp(320px, 50vh, 640px);
}

/* ------------------------------------------------------------------------ */
/* Landing view — show grid                                                 */
/* ------------------------------------------------------------------------ */

.tv-landing {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.tv-landing-intro {
  text-align: center;
  max-width: 68ch;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
}

.tv-landing-title {
  margin: 0;
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--tv-text);
}

.tv-landing-blurb {
  margin: 0;
  font-size: 14px;
  color: var(--tv-text-dim);
  line-height: 1.55;
}

/* Landing search input. Lives in the intro block, just below the blurb.
   The clear ✕ is absolutely positioned over the input's trailing edge —
   the input itself reserves the slot via right padding so typed text
   never collides with the button. */
.tv-search {
  position: relative;
  width: min(420px, 100%);
  margin-top: 6px;
}

.tv-search-input {
  appearance: none;
  width: 100%;
  font-family: var(--font-screen);
  font-size: 15px;
  color: var(--tv-text);
  background: var(--tv-card-bg);
  border: 1px solid var(--tv-card-border);
  border-radius: 10px;
  padding: 10px 36px 10px 14px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}

/* Kill the native WebKit/Edge search clear so we only ship one ✕. */
.tv-search-input::-webkit-search-cancel-button,
.tv-search-input::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}

.tv-search-input::placeholder {
  color: var(--tv-text-dimmer);
}

.tv-search-input:hover {
  border-color: var(--tv-accent-dim);
}

.tv-search-input:focus,
.tv-search-input:focus-visible {
  outline: none;
  border-color: var(--tv-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--tv-accent-dim) 35%, transparent);
  background: #181820;
}

.tv-search-clear {
  appearance: none;
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid transparent;
  background: transparent;
  color: var(--tv-text-dim);
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.tv-search-clear:hover,
.tv-search-clear:focus-visible {
  color: var(--tv-text);
  border-color: var(--tv-accent-dim);
  background: color-mix(in srgb, var(--tv-accent) 10%, transparent);
  outline: none;
}

/* Visually hide the live-region announcement (screen readers still see it). */
.tv-search-status {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.tv-search-empty {
  margin: 4px 2px 0;
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.5px;
  color: var(--tv-text-dim);
  text-align: center;
  padding: 22px 12px;
  border: 1px dashed var(--tv-card-border);
  border-radius: 10px;
  background: color-mix(in srgb, var(--tv-card-bg) 60%, transparent);
}

/* Tag-filter chip row — wraps under the search input. Each chip is a
   real <button>; the active state lives on .is-active so the styles
   work even if the JS hasn't set aria-pressed yet on first paint. */
.tv-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 10px;
  max-width: 720px;
}

.tv-tag-chip {
  appearance: none;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  padding: 5px 10px;
  border: 1px solid var(--tv-card-border);
  border-radius: 999px;
  background: color-mix(in srgb, var(--tv-card-bg) 80%, transparent);
  color: var(--tv-text-dim);
  cursor: pointer;
  transition: border-color 120ms ease, background-color 120ms ease, color 120ms ease;
}

.tv-tag-chip:hover {
  border-color: var(--tv-accent-dim);
  color: var(--tv-text);
}

.tv-tag-chip:focus-visible {
  outline: none;
  border-color: var(--tv-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--tv-accent-dim) 35%, transparent);
}

.tv-tag-chip.is-active {
  background: color-mix(in srgb, var(--tv-accent) 18%, var(--tv-card-bg));
  border-color: var(--tv-accent);
  color: var(--tv-text);
}

.tv-tag-reset {
  appearance: none;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.6px;
  text-transform: lowercase;
  padding: 5px 10px;
  border: 1px dashed var(--tv-card-border);
  border-radius: 999px;
  background: transparent;
  color: var(--tv-text-dimmer);
  cursor: pointer;
  margin-left: 4px;
}

.tv-tag-reset:hover,
.tv-tag-reset:focus-visible {
  outline: none;
  color: var(--tv-text);
  border-color: var(--tv-accent-dim);
  border-style: solid;
}

/* ------------------------------------------------------------------------ */
/* Episodes view — per-show banner + grid                                   */
/* ------------------------------------------------------------------------ */

.tv-episodes {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.tv-show-banner {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 20px;
  background: linear-gradient(
    100deg,
    color-mix(in srgb, var(--show-accent, var(--tv-accent)) 18%, var(--tv-card-bg)) 0%,
    var(--tv-card-bg) 70%
  );
  border: 1px solid var(--tv-card-border);
  border-left: 3px solid var(--show-accent, var(--tv-accent));
  border-radius: 14px;
}

.tv-show-banner-emoji {
  font-size: 40px;
  line-height: 1;
}

.tv-show-banner-text {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.tv-show-banner-name {
  margin: 0;
  font-size: clamp(20px, 2.6vw, 26px);
  font-weight: 700;
  letter-spacing: 0.4px;
  color: var(--tv-text);
}

.tv-show-banner-tag {
  margin: 0;
  font-size: 13px;
  color: var(--tv-text-dim);
  line-height: 1.4;
}

.tv-inline-loading {
  text-align: center;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--tv-text-dim);
  padding: 28px 12px;
  letter-spacing: 1.5px;
}

.tv-error-banner {
  background: rgba(40, 6, 6, 0.6);
  border: 1px solid #5a1d15;
  border-radius: 10px;
  padding: 14px 16px;
  color: #ffdcd2;
  font-size: 13px;
}

.tv-ep-tools {
  display: flex;
  justify-content: flex-end;
  padding: 0 2px;
}

.tv-ep-tools.hidden {
  display: none;
}

/* Plex/Netflix-style compact poster wall — small minmax + tight
   gaps so the page reads as a streaming-service landing rather than
   a blog list. Caption sits below the poster in muted weight so the
   artwork leads. Tighter than .tv-ep-grid's 220px because portrait
   posters read fine at 110–130px wide. */
.tv-show-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 14px 12px;
}

/* Invisible sliver placed right after the shows / movies grid for
   scroll-based windowing. An IntersectionObserver in shows-view.js
   watches it; when it crosses into the viewport (with rootMargin
   ahead-of-fold) the next page of cards is appended to the grid
   above. 1px tall is enough for the observer to fire, but small
   enough that it doesn't add visible whitespace between sections. */
.tv-scroll-sentinel {
  width: 100%;
  height: 1px;
  pointer-events: none;
}

/* Shared base for every grid card surface in /watch/ — landing show
   tiles, Continue Watching cards, episode cards, and the player's
   Up-next strip. Each variant below overrides border-radius,
   transition, hover lift, focus ring, and any extras (continue-card
   gets `position: relative` for its remove button etc.). Keeping the
   skeleton in one place means "what a card looks like" is a single
   concept rather than four copies that drift over time. */
.tv-show-card,
.tv-continue-card,
.tv-ep-card,
.tv-upnext-card {
  appearance: none;
  border: 1px solid var(--tv-card-border);
  background: var(--tv-card-bg);
  padding: 0;
  overflow: hidden;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  text-align: left;
  color: var(--tv-text);
  /* Skip rendering paint/layout/style for off-screen cards (Chromium 85+,
     Safari 18+). Matters most for the long-running shows that put a
     hundred+ episodes in a single season grid (X-Men '92's 76-ep season,
     Astro Boy '63's 104, Aqua Teen / Beavis specials), and for the home
     poster wall which is 85 cards today and growing. The browser still
     keeps every card in the DOM (so spatial-nav + roving-tabindex still
     find them) but it doesn't pay layout / paint cost until the card is
     within ~50vh of the viewport, after which it stays "rendered" via
     the `auto` memoization. `contain-intrinsic-size: auto 220px` reserves
     a placeholder box of that height before first render so the grid's
     row count + scroll height match the eventual layout — without it,
     the scrollbar jumps as cards hydrate on first scroll. Width is
     `auto` so the grid's `minmax(N, 1fr)` track sizing still controls
     it. Cheap, declarative, no JS — a real windowing/virtualization
     pass would be heavier than the gain. */
  content-visibility: auto;
  contain-intrinsic-size: auto 220px;
}

.tv-show-card {
  border: 0;
  background: transparent;
  border-radius: 0;
  transition: transform 0.18s ease;
  /* `--show-accent` is set inline on each card from JS so the hover
     glow tracks each show's signature colour. */
  --show-accent: var(--tv-accent);
}

/* Hover lift now lives on the poster only, so the caption underneath
   stays put while the artwork slides up — same effect Plex / Apple TV
   use to make rows feel responsive without re-flowing the labels. */
.tv-show-card:hover .tv-show-poster {
  transform: translateY(-3px);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.55), 0 0 0 1px var(--show-accent),
    0 0 24px color-mix(in srgb, var(--show-accent) 30%, transparent);
}

.tv-show-card:hover .tv-show-name {
  color: var(--tv-text);
}

.tv-show-card:focus-visible {
  outline: none;
}

.tv-show-card:focus-visible .tv-show-poster {
  outline: 2px solid var(--show-accent);
  outline-offset: 2px;
}

.tv-show-poster {
  position: relative;
  aspect-ratio: 2 / 3;
  background: #0a0a10;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-size: 80px;
  border-radius: 8px;
  transition: transform 0.18s ease, box-shadow 0.18s ease, outline 0.18s ease;
}

.tv-show-poster.is-empty {
  color: rgba(255, 255, 255, 0.4);
}

.tv-show-poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tv-show-meta {
  padding: 8px 2px 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.tv-show-name {
  margin: 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--tv-text-dim);
  line-height: 1.3;
  /* Single-line caption to mirror Plex / Apple-TV: long show names
     truncate with an ellipsis instead of pushing tile heights uneven.
     The full name is still available via the parent card's hover
     `title` attribute. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Legacy `.tv-show-tag` styling kept for any callers we missed; the
   shows view no longer renders this element on the home grid. */
.tv-show-tag {
  margin: 0;
  font-size: 12.5px;
  color: var(--tv-text-dim);
  line-height: 1.4;
}

/* ------------------------------------------------------------------------ */
/* Landing view — "Continue watching" row                                   */
/* ------------------------------------------------------------------------ */

.tv-continue-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.tv-continue-section.hidden {
  display: none;
}

.tv-continue-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px;
}

.tv-continue-card {
  /* Skeleton (border, padding, flex, etc.) inherited from the
     shared `.tv-show-card, .tv-continue-card, .tv-ep-card,
     .tv-upnext-card` base rule above. */
  border-radius: 12px;
  text-decoration: none;
  position: relative;
  transition: transform 0.18s ease, border 0.18s ease, box-shadow 0.18s ease;
  --show-accent: var(--tv-accent);
}

.tv-continue-card:hover {
  transform: translateY(-2px);
  border-color: var(--show-accent);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.55),
    0 0 18px color-mix(in srgb, var(--show-accent) 25%, transparent);
}

.tv-continue-card:focus-visible {
  outline: 2px solid var(--show-accent);
  outline-offset: 2px;
}

.tv-continue-thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  background: #0a0a10;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-size: 44px;
}

.tv-continue-thumb.is-empty {
  color: rgba(255, 255, 255, 0.45);
}

.tv-continue-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Play glyph overlay — visible by default at low opacity, brighter on
   hover so the user reads the card as a "resume playback" affordance. */
.tv-continue-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 38px;
  color: #fff;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.8);
  opacity: 0.25;
  background: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.55) 100%);
  transition: opacity 0.2s ease;
  pointer-events: none;
}

.tv-continue-card:hover .tv-continue-play,
.tv-continue-card:focus-visible .tv-continue-play {
  opacity: 1;
}

.tv-continue-remove {
  appearance: none;
  position: absolute;
  top: 6px;
  right: 6px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(8, 8, 12, 0.7);
  color: var(--tv-text);
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  opacity: 0;
  transform: scale(0.92);
  transition: opacity 0.15s ease, transform 0.15s ease, background 0.15s ease,
    border-color 0.15s ease, color 0.15s ease;
  z-index: 1;
}

.tv-continue-card:hover .tv-continue-remove,
.tv-continue-card:focus-within .tv-continue-remove,
.tv-continue-remove:focus-visible {
  opacity: 1;
  transform: scale(1);
}

.tv-continue-remove:hover,
.tv-continue-remove:focus-visible {
  background: var(--tv-led-red);
  border-color: var(--tv-led-red);
  color: #fff;
}

/* When the ✕ is the active focus target (user pressed ArrowUp on
   the card to "drill into" the remove action), give it a clearly
   stronger ring than the card-level :focus-within glow. Without
   this the highlight is subtle from 10ft away. */
.tv-continue-remove:focus-visible {
  outline: 3px solid var(--tv-led-red);
  outline-offset: 3px;
  box-shadow: 0 0 0 6px rgba(255, 87, 87, 0.25);
  transform: scale(1.12);
}

.tv-continue-meta {
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.tv-continue-show {
  font-size: 13px;
  font-weight: 600;
  color: var(--tv-text);
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tv-continue-tag {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 1.5px;
  color: var(--show-accent, var(--tv-accent));
  text-transform: uppercase;
}

/* Touch devices have no hover state; keep the ✕ visible at lower
   opacity so it's still discoverable. */
@media (hover: none) {
  .tv-continue-remove {
    opacity: 0.85;
    transform: scale(1);
  }
}

/* ------------------------------------------------------------------------ */
/* Landing view — "Saved offline" row                                       */
/* ------------------------------------------------------------------------ */

.tv-saved-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.tv-saved-section.hidden {
  display: none;
}

.tv-saved-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px;
}

/* Card chrome is shared with Continue Watching; the "saved" variant
   adds a corner badge and a size line below the title. */
.tv-saved-badge {
  position: absolute;
  bottom: 6px;
  left: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 1.5px;
  color: var(--tv-text);
  background: rgba(8, 8, 12, 0.78);
  border: 1px solid rgba(255, 255, 255, 0.18);
  padding: 3px 7px;
  border-radius: 4px;
  text-transform: uppercase;
  z-index: 1;
}

.tv-saved-size {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--tv-text-dim);
  letter-spacing: 0.5px;
}

/* ------------------------------------------------------------------------ */
/* Landing page — Shows + Movies section split                              */
/* ------------------------------------------------------------------------ */

/* Both grids use the same .tv-show-grid layout; the section wrapper
   just gives each its own label + spacing. The Movies section lives
   below the Shows section so the eye reads "main catalog, then
   alt-catalog" without needing tabs. */
.tv-shows-section,
.tv-movies-section {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.tv-movies-section {
  margin-top: 24px;
}

/* "Movie" pill in the top-right corner of a movie poster — the
   saved-badge styles handle font + chrome; this just relocates a
   copy of them to the opposite corner so the two badge variants
   don't collide if a movie is ever also offline-cached. */
.tv-movie-badge {
  position: absolute;
  top: 6px;
  right: 6px;
  bottom: auto;
  left: auto;
}

/* ------------------------------------------------------------------------ */
/* Quick nav — fixed left-edge rail with Shows / Movies shortcuts.          */
/* ------------------------------------------------------------------------ */

/* Mounted to <body> by `modules/quicknav.js` at first paint. The rail
   is reachable from every view (landing, episodes, watch, movie watch)
   so the user can jump between shows / movies without going through
   the breadcrumb-then-scroll path. Hidden inside iframes (the script
   guards on window.top !== window) and on narrow viewports (the media
   query at the bottom of this block) so it doesn't overlap the player
   chrome on phones. */
.tv-quicknav {
  position: fixed;
  /* Vertically-centered so it sits visually independent of the
     header and the back button. The two pills together are ~160px
     tall so transform-translateY centers them rather than the rail
     box's outer edge. */
  top: 50%;
  left: 14px;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Above the header backdrop (z-index 50) but below the back-button
     pill (z-index 999999) — back-button takes precedence if the user
     scrolls to a state where the two would visually overlap. */
  z-index: 100;
  pointer-events: none;
}

.tv-quicknav-btn {
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 60px;
  padding: 12px 6px;
  border-radius: 14px;
  text-decoration: none;
  color: var(--tv-text-bright, #f5f7fa);
  background: color-mix(in srgb, var(--tv-screen-off, #0a0c10) 78%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid var(--tv-card-border, rgba(255, 255, 255, 0.12));
  /* Subtle drop shadow so the pill reads as floating over content
     rather than sitting flat on the page background. */
  box-shadow: 0 4px 14px -6px rgba(0, 0, 0, 0.4);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease,
    box-shadow 0.15s ease;
}

.tv-quicknav-btn:hover,
.tv-quicknav-btn:focus-visible {
  background: color-mix(in srgb, var(--tv-screen-off, #0a0c10) 92%, transparent);
  border-color: color-mix(in srgb, var(--tv-accent, #6aa9ff) 60%, transparent);
  transform: translateX(2px);
  outline: none;
  box-shadow: 0 6px 18px -6px rgba(0, 0, 0, 0.55);
}

/* Active pill (URL matches the section's route). The accent variable
   is set per-show by the router on the episodes / watch views, so on
   those pages the active pill picks up the show's color. On the
   landing page the accent is unset and we fall back to a neutral
   high-contrast border. */
.tv-quicknav-btn.is-active {
  background: color-mix(
    in srgb,
    var(--tv-accent, var(--tv-text-bright, #f5f7fa)) 28%,
    var(--tv-screen-off, #0a0c10)
  );
  border-color: color-mix(in srgb, var(--tv-accent, #f5f7fa) 70%, transparent);
}

.tv-quicknav-emoji {
  font-size: 22px;
  line-height: 1;
}

.tv-quicknav-label {
  font-family: var(--font-mono, monospace);
  font-size: 10px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--tv-text-dim, #b6bcc6);
}

.tv-quicknav-btn.is-active .tv-quicknav-label {
  color: var(--tv-text-bright, #f5f7fa);
}

/* Hide on narrow viewports. The video player + episode grid get the
   full width on phones, and the breadcrumb + back-button cover
   "jump out of this view" already. The breakpoint matches the
   .tv-room one-column reflow threshold below. */
@media (max-width: 900px) {
  .tv-quicknav {
    display: none;
  }
}

/* Hide on the player view in TV mode. On the Android-TV WebView the
   rail overlaps the player chrome (left bezel label, save-offline
   button, scrub controls) and the user reported it physically gets
   in the way of remote-control navigation during playback. The
   `tv-quicknav--hidden-on-player` class is toggled from
   `quicknav.js:updateActive` whenever `isTvMode && isPlayerView(loc)`
   — desktop / mouse browsers keep the rail on the player view since
   it doesn't compete with anything there. */
.tv-quicknav.tv-quicknav--hidden-on-player {
  display: none;
}

/* ------------------------------------------------------------------------ */
/* Player view — Save-offline button states                                 */
/* ------------------------------------------------------------------------ */

.tv-save-btn.is-saved {
  color: var(--tv-led);
  border-color: color-mix(in srgb, var(--tv-led) 50%, transparent);
  background: color-mix(in srgb, var(--tv-led) 8%, transparent);
}

.tv-save-btn.is-saved:hover {
  background: color-mix(in srgb, var(--tv-led-red) 14%, transparent);
  color: var(--tv-led-red);
  border-color: color-mix(in srgb, var(--tv-led-red) 50%, transparent);
}

.tv-save-btn.is-downloading {
  color: var(--tv-accent);
  border-color: color-mix(in srgb, var(--tv-accent) 50%, transparent);
  background: color-mix(in srgb, var(--tv-accent) 8%, transparent);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  /* Subtle pulse so the user has a fidget-target while a 200 MB file
     downloads — eight seconds full-cycle reads as "active" without
     feeling impatient. */
  animation: tv-save-pulse 2s ease-in-out infinite;
}

@keyframes tv-save-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--tv-accent) 0%, transparent);
  }
  50% {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--tv-accent) 18%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .tv-save-btn.is-downloading {
    animation: none;
  }
}

.tv-back-to-shows {
  appearance: none;
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 1.5px;
  padding: 7px 12px;
  background: transparent;
  color: var(--tv-text-dim);
  border: 1px solid var(--tv-card-border);
  border-radius: 6px;
  cursor: pointer;
  transition: color 0.15s ease, border 0.15s ease, background 0.15s ease;
}

.tv-back-to-shows:hover {
  color: var(--tv-text);
  border-color: var(--tv-accent-dim);
  background: rgba(255, 184, 0, 0.06);
}

/* ------------------------------------------------------------------------ */
/* Player view                                                              */
/* ------------------------------------------------------------------------ */

.tv-player {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.tv-led {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--tv-led);
  box-shadow: 0 0 10px var(--tv-led-glow);
  animation: tv-pulse 2.4s ease-in-out infinite;
}

@keyframes tv-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.55;
  }
}

/* ------------------------------------------------------------------------ */
/* TV chassis                                                               */
/* ------------------------------------------------------------------------ */

.tv-set {
  display: flex;
  justify-content: center;
  margin: 4px 0 0;
}

.tv-bezel {
  width: min(1100px, 100%);
  background: linear-gradient(180deg, var(--tv-bezel) 0%, var(--tv-bezel-edge) 100%);
  border-radius: 22px;
  padding: 14px 14px 0;
  border: 1px solid var(--tv-bezel-trim);
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.04);
  position: relative;
}

.tv-bezel-label {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 2px 8px 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--tv-text-dimmer);
  text-transform: uppercase;
}

.tv-channel-chip {
  background: var(--tv-chip-bg);
  color: var(--tv-led);
  padding: 3px 8px;
  border-radius: 4px;
  border: 1px solid var(--tv-chip-border);
  letter-spacing: 1.5px;
  text-shadow: 0 0 6px var(--tv-led-glow);
}

.tv-screen {
  position: relative;
  background: var(--tv-screen-off);
  border-radius: 10px;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  border: 1px solid #000;
  box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.8), inset 0 0 0 3px var(--tv-bezel-edge);
}

.tv-video {
  display: block;
  width: 100%;
  height: 100%;
  background: #000;
  outline: none;
  object-fit: contain;
}

/* Fullscreen (F key / browser controls). The screen wrapper is what
   gets fullscreened so its child overlays (loading / no-signal / end
   card) stay layered correctly. Strip the chassis-only chrome since
   the bezel ring + aspect-ratio cap make no sense at 100vw/100vh. */
.tv-screen:fullscreen,
.tv-screen:-webkit-full-screen {
  width: 100vw;
  height: 100vh;
  aspect-ratio: auto;
  border-radius: 0;
  border: none;
  box-shadow: none;
  background: #000;
}

.tv-base {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 0 6px;
}

.tv-stand {
  width: 32%;
  max-width: 280px;
  height: 8px;
  border-radius: 0 0 10px 10px;
  background: linear-gradient(180deg, var(--tv-stand) 0%, var(--tv-stand-dark) 100%);
  border: 1px solid #000;
  border-top: 0;
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.55);
}

/* Loading + no-signal overlays sit ON TOP of the video element. */
.tv-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: linear-gradient(180deg, #0a0d10 0%, #050608 100%);
  color: var(--tv-text);
  text-align: center;
  padding: 24px;
}

.tv-overlay.hidden {
  display: none;
}

.tv-overlay-title {
  margin: 0;
  font-family: var(--font-mono);
  font-size: clamp(14px, 2vw, 18px);
  letter-spacing: 4px;
  color: var(--tv-led);
  text-shadow: 0 0 10px var(--tv-led-glow);
}

#tv-no-signal .tv-overlay-title {
  color: var(--tv-led-red);
  text-shadow: 0 0 10px var(--tv-led-red-glow);
}

.tv-overlay-detail {
  margin: 0;
  font-size: 14px;
  color: var(--tv-text-dim);
  max-width: 50ch;
}

/* Playback error banner (sits ABOVE the controls inside the screen). */
.tv-error {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 50px;
  z-index: 3;
  background: rgba(20, 4, 4, 0.92);
  border: 1px solid #5a1d15;
  border-radius: 8px;
  padding: 12px 14px;
  color: #ffdcd2;
  font-size: 13px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
  pointer-events: auto;
}

.tv-error.hidden {
  display: none;
}

.tv-error-link {
  color: var(--tv-accent);
  background: color-mix(in srgb, var(--tv-accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--tv-accent) 40%, transparent);
  padding: 4px 10px;
  border-radius: 4px;
  text-decoration: none;
  font-weight: 600;
}

.tv-error-link:hover {
  background: color-mix(in srgb, var(--tv-accent) 18%, transparent);
}

/* ------------------------------------------------------------------------ */
/* Marquee                                                                  */
/* ------------------------------------------------------------------------ */

.tv-marquee {
  background: var(--tv-card-bg);
  border: 1px solid var(--tv-card-border);
  border-radius: 12px;
  padding: 12px 16px;
  display: flex;
  align-items: center;
  gap: 18px;
  overflow: hidden;
}

.tv-now-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 2.5px;
  color: var(--tv-led-red);
  text-transform: uppercase;
  white-space: nowrap;
  text-shadow: 0 0 8px var(--tv-led-red-glow);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.tv-rec-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--tv-led-red);
  box-shadow: 0 0 8px var(--tv-led-red-glow);
  animation: tv-pulse 1.8s ease-in-out infinite;
}

.tv-now-stack {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

.tv-title {
  font-size: 17px;
  font-weight: 600;
  color: var(--tv-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tv-subtitle {
  font-size: 12px;
  font-family: var(--font-mono);
  color: var(--tv-text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tv-marquee.is-flashing .tv-title {
  color: var(--tv-led);
  text-shadow: 0 0 8px var(--tv-led-glow);
}

/* Synopsis card under the marquee. */
.tv-summary {
  background: var(--tv-card-bg);
  border: 1px solid var(--tv-card-border);
  border-left: 3px solid var(--tv-accent);
  border-radius: 12px;
  padding: 10px 14px;
  margin: 0;
}

.tv-summary-text {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--tv-text);
}

/* ------------------------------------------------------------------------ */
/* Remote-control button row                                                */
/* ------------------------------------------------------------------------ */

.tv-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  justify-content: center;
}

.tv-btn {
  appearance: none;
  font-family: var(--font-screen);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.3px;
  padding: 10px 16px;
  border-radius: 8px;
  border: 1px solid var(--tv-card-border);
  background: linear-gradient(180deg, #25252d 0%, #18181f 100%);
  color: var(--tv-text);
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.1s ease, filter 0.15s ease, background 0.15s ease;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.tv-btn:hover {
  filter: brightness(1.15);
}

.tv-btn:active {
  transform: translateY(1px);
}

.tv-btn.is-accent {
  background: linear-gradient(180deg, var(--tv-accent) 0%, var(--tv-accent-dim) 100%);
  color: #1b1100;
  border-color: var(--tv-accent-dim);
  box-shadow: 0 0 14px var(--tv-accent-glow);
}

.tv-btn--ghost {
  background: transparent;
  color: var(--tv-text-dim);
  border-color: #2a2a32;
  font-weight: 500;
}

.tv-btn--ghost:hover {
  color: var(--tv-text);
  background: #1c1c22;
}

/* Seek buttons (±10s) — visually paired with Prev/Next but secondary. */
.tv-btn--seek {
  padding: 10px 12px;
  font-variant-numeric: tabular-nums;
  color: var(--tv-text-dim);
}

.tv-btn--seek:hover {
  color: var(--tv-text);
}

/* Subtitle picker — anchored under the CC button. */
.tv-subs-wrap {
  position: relative;
  display: inline-flex;
}

.tv-subs-btn {
  font-variant: small-caps;
  letter-spacing: 0.05em;
}

.tv-subs-btn.is-active {
  color: var(--tv-accent);
  border-color: var(--tv-accent-dim);
  background: linear-gradient(180deg, #2a261c 0%, #1a1812 100%);
  font-weight: 700;
}

.tv-subs-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  right: 0;
  min-width: 220px;
  max-height: 320px;
  overflow-y: auto;
  padding: 6px;
  border-radius: 10px;
  border: 1px solid var(--tv-card-border);
  background: #14141a;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.55);
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.tv-subs-menu.hidden {
  display: none;
}

.tv-subs-item {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--tv-text);
  font: inherit;
  text-align: left;
  padding: 8px 12px;
  border-radius: 6px;
  cursor: pointer;
}

.tv-subs-item:hover,
.tv-subs-item:focus-visible {
  background: #20202a;
  outline: none;
}

.tv-subs-item.is-current {
  color: var(--tv-accent);
  font-weight: 600;
}

.tv-subs-empty {
  margin: 0;
  padding: 10px 12px;
  font-size: 13px;
  color: var(--tv-text-dim);
}

/* Subtitle sync slider — only visible while a track is active.
   Directional labels ("Earlier" / "Later") flank the range input so
   the user never has to guess which way fixes their drift. */
.tv-subs-sync {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  border-radius: 10px;
  background: #15151b;
  border: 1px solid var(--tv-card-border);
  flex-wrap: nowrap;
}

.tv-subs-sync.hidden {
  display: none;
}

.tv-subs-sync-hint {
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--tv-text-dim);
  white-space: nowrap;
  user-select: none;
}

.tv-subs-sync-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 140px;
  height: 22px;
  background: transparent;
  cursor: pointer;
  margin: 0;
}

.tv-subs-sync-slider::-webkit-slider-runnable-track {
  height: 4px;
  background: #2a2a32;
  border-radius: 2px;
}

.tv-subs-sync-slider::-moz-range-track {
  height: 4px;
  background: #2a2a32;
  border-radius: 2px;
}

.tv-subs-sync-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--tv-accent);
  border: 2px solid #0b0b10;
  box-shadow: 0 0 8px var(--tv-accent-glow);
  cursor: pointer;
}

.tv-subs-sync-slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--tv-accent);
  border: 2px solid #0b0b10;
  box-shadow: 0 0 8px var(--tv-accent-glow);
  cursor: pointer;
}

.tv-subs-sync-slider:focus-visible {
  outline: 2px solid var(--tv-accent);
  outline-offset: 4px;
  border-radius: 6px;
}

.tv-subs-sync-readout {
  appearance: none;
  border: 0;
  background: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px 8px;
  font-family: var(--font-screen);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--tv-text-dim);
  cursor: pointer;
  user-select: none;
  min-width: 56px;
  border-radius: 6px;
}

.tv-subs-sync-readout:hover,
.tv-subs-sync-readout:focus-visible {
  background: #20202a;
  color: var(--tv-text);
  outline: none;
}

.tv-subs-sync-readout.is-shifted {
  color: var(--tv-accent);
  font-weight: 600;
}

@media (max-width: 640px) {
  /* On narrow screens the controls row wraps — drop the verbose hints
     so the slider+readout still fit on a single line. */
  .tv-subs-sync-hint {
    display: none;
  }
  .tv-subs-sync-slider {
    width: 110px;
  }
}

.tv-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--tv-text-dim);
  font-family: var(--font-mono);
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px dashed var(--tv-card-border);
  cursor: pointer;
  user-select: none;
}

.tv-toggle input {
  width: 16px;
  height: 16px;
  accent-color: var(--tv-accent);
  cursor: pointer;
}

/* ------------------------------------------------------------------------ */
/* Section blocks (seasons + episodes)                                      */
/* ------------------------------------------------------------------------ */

.tv-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tv-section-label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 2.5px;
  color: var(--tv-text-dim);
  text-transform: uppercase;
  padding-left: 4px;
  display: flex;
  align-items: baseline;
  gap: 12px;
}

.tv-section-meta {
  font-size: 10px;
  letter-spacing: 1.5px;
  color: var(--tv-text-dimmer);
}

.tv-chip-row {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  gap: 6px;
  padding: 4px 4px 10px;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}

.tv-chip-row::-webkit-scrollbar {
  height: 8px;
}

.tv-chip-row::-webkit-scrollbar-thumb {
  background: var(--tv-card-border);
  border-radius: 4px;
}

.tv-chip {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 1px;
  padding: 8px 12px;
  background: var(--tv-chip-bg);
  color: var(--tv-text-dim);
  border: 1px solid var(--tv-chip-border);
  border-radius: 6px;
  cursor: pointer;
  scroll-snap-align: center;
  transition: color 0.15s ease, background 0.15s ease, border 0.15s ease;
}

.tv-chip:hover {
  color: var(--tv-text);
  background: #232330;
}

.tv-chip.is-active {
  background: var(--tv-accent);
  color: #1b1100;
  border-color: var(--tv-accent-dim);
  font-weight: 600;
  box-shadow: 0 0 12px var(--tv-accent-glow);
}

/* Episode grid — Netflix-style cards */
.tv-ep-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px;
}

.tv-ep-card {
  /* Skeleton inherited from the shared card-base rule. */
  border-radius: 10px;
  transition: transform 0.15s ease, border 0.15s ease, box-shadow 0.15s ease;
}

.tv-ep-card:hover {
  transform: translateY(-2px);
  border-color: var(--tv-accent-dim);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
}

.tv-ep-card.is-active {
  border-color: var(--tv-accent);
  box-shadow: 0 0 0 2px var(--tv-accent-glow), 0 8px 20px rgba(0, 0, 0, 0.5);
}

.tv-ep-thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  background: #0a0a10;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.tv-ep-thumb.is-empty {
  font-size: 36px;
  color: var(--tv-text-dimmer);
}

.tv-ep-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tv-ep-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 38px;
  color: #fff;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.8);
  opacity: 0;
  transition: opacity 0.2s ease;
  background: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.65) 100%);
}

.tv-ep-card:hover .tv-ep-play,
.tv-ep-card.is-active .tv-ep-play {
  opacity: 1;
}

.tv-ep-meta {
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.tv-ep-num {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 1.5px;
  color: var(--tv-accent);
  text-transform: uppercase;
}

.tv-ep-title {
  font-size: 13px;
  font-weight: 500;
  color: var(--tv-text);
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ------------------------------------------------------------------------ */
/* Watch view — end-of-episode "Up next" card                               */
/* ------------------------------------------------------------------------ */

.tv-endcard {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 8, 12, 0.86);
  backdrop-filter: blur(10px);
  z-index: 5;
  padding: 24px;
  animation: tv-endcard-in 0.25s ease-out;
}

.tv-endcard.hidden {
  display: none;
}

@keyframes tv-endcard-in {
  from {
    opacity: 0;
    transform: scale(0.985);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.tv-endcard-inner {
  max-width: 520px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: center;
  text-align: center;
}

.tv-endcard-eyebrow {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 3px;
  color: var(--tv-accent);
  text-transform: uppercase;
}

.tv-endcard-thumb {
  width: clamp(180px, 32vw, 280px);
  aspect-ratio: 16 / 9;
  border-radius: 10px;
  overflow: hidden;
  background: #0a0a10;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 56px;
  border: 1px solid var(--tv-card-border);
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.55);
}

.tv-endcard-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tv-endcard-thumb.is-empty {
  color: var(--tv-text-dimmer);
}

.tv-endcard-meta {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.tv-endcard-title {
  margin: 0;
  font-size: clamp(18px, 2.4vw, 22px);
  font-weight: 700;
  color: var(--tv-text);
  line-height: 1.25;
}

.tv-endcard-sub {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--tv-text-dim);
  letter-spacing: 1px;
}

.tv-endcard-countdown {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--tv-text-dim);
  letter-spacing: 0.5px;
}

.tv-endcard-countdown.hidden {
  display: none;
}

.tv-endcard-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 6px;
}

.tv-endcard-btn {
  appearance: none;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--tv-card-border);
  color: var(--tv-text);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 1px;
  padding: 10px 16px;
  border-radius: 8px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}

.tv-endcard-btn:hover {
  border-color: var(--tv-accent-dim);
  background: rgba(255, 255, 255, 0.1);
  transform: translateY(-1px);
}

.tv-endcard-btn.hidden {
  display: none;
}

.tv-endcard-btn--primary {
  background: var(--tv-accent);
  color: #0a0a10;
  border-color: var(--tv-accent);
  font-weight: 700;
}

.tv-endcard-btn--primary:hover {
  background: color-mix(in srgb, var(--tv-accent) 88%, white);
}

/* ------------------------------------------------------------------------ */
/* Watch view — resume / restart prompt                                     */
/* ------------------------------------------------------------------------ */

/* Bottom-anchored prompt that appears for ~6s when the user re-enters
   an episode they had previously paused. Unlike `.tv-endcard` (which
   covers the whole screen), this leaves the picture visible behind a
   gradient — the video has already seeked to the resume point, so the
   user sees their next frame and can confirm it's the right spot. */
.tv-resume {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 24px;
  background: linear-gradient(
    to top,
    rgba(8, 8, 12, 0.94) 0%,
    rgba(8, 8, 12, 0.6) 60%,
    transparent
  );
  z-index: 6;
  animation: tv-resume-in 0.25s ease-out;
}

.tv-resume.hidden {
  display: none;
}

@keyframes tv-resume-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.tv-resume-inner {
  max-width: 720px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-start;
}

.tv-resume-eyebrow {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 3px;
  color: var(--tv-accent);
  text-transform: uppercase;
}

.tv-resume-title {
  margin: 0;
  font-size: clamp(16px, 2.2vw, 20px);
  font-weight: 700;
  color: var(--tv-text);
  line-height: 1.2;
}

.tv-resume-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 4px;
}

.tv-resume-btn {
  appearance: none;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid var(--tv-card-border);
  color: var(--tv-text);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 1px;
  padding: 10px 18px;
  border-radius: 8px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}

.tv-resume-btn:hover,
.tv-resume-btn:focus-visible {
  border-color: var(--tv-accent);
  background: rgba(255, 255, 255, 0.14);
  outline: none;
  transform: translateY(-1px);
}

.tv-resume-btn--primary {
  background: var(--tv-accent);
  color: #0a0a10;
  border-color: var(--tv-accent);
  font-weight: 700;
}

.tv-resume-btn--primary:hover,
.tv-resume-btn--primary:focus-visible {
  background: color-mix(in srgb, var(--tv-accent) 88%, white);
}

/* Subtle styling for the secondary "← Back" button. Slightly lower
   contrast than the default tv-resume-btn so the eye still treats
   Resume as the primary action, but readable enough to be
   discoverable for users who don't remember the remote's back key. */
.tv-resume-btn--ghost {
  background: transparent;
  border-color: rgba(255, 255, 255, 0.18);
  color: var(--tv-text-muted, rgba(255, 255, 255, 0.7));
}

/* TV mode: bigger touch targets + heavier weight so the prompt reads
   from the couch. Same focus ring rules the rest of the TV chrome
   uses, so D-pad navigation between the two buttons looks consistent
   with chips / cards / controls. */
:root[data-mode='tv'] .tv-resume {
  padding: clamp(28px, 5vh, 56px) clamp(32px, 4vw, 64px);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.92) 0%, rgba(0, 0, 0, 0.55) 70%, transparent);
}

:root[data-mode='tv'] .tv-resume-eyebrow {
  font-size: 13px;
}

:root[data-mode='tv'] .tv-resume-title {
  font-size: clamp(20px, 2.6vw, 26px);
}

:root[data-mode='tv'] .tv-resume-btn {
  font-size: 14px;
  padding: 14px 22px;
}

:root[data-mode='tv'] .tv-resume-btn:focus-visible {
  box-shadow: 0 0 0 3px var(--tv-accent);
}

/* ------------------------------------------------------------------------ */
/* Watch view — up-next strip + nav links                                   */
/* ------------------------------------------------------------------------ */

.tv-upnext-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 10px;
}

.tv-upnext-card {
  /* Skeleton inherited from the shared card-base rule. */
  border-radius: 8px;
  transition: transform 0.15s ease, border 0.15s ease;
}

.tv-upnext-card:hover {
  transform: translateY(-2px);
  border-color: var(--tv-accent-dim);
}

.tv-upnext-thumb {
  aspect-ratio: 16 / 9;
  background: #0a0a10;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-size: 24px;
  color: var(--tv-text-dimmer);
}

.tv-upnext-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.tv-upnext-label {
  padding: 6px 10px 10px;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--tv-text-dim);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.tv-all-eps-link {
  display: inline-block;
  align-self: flex-start;
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.5px;
  color: var(--tv-accent);
  text-decoration: none;
  border-bottom: 1px dotted var(--tv-accent-dim);
}

.tv-all-eps-link:hover {
  color: #ffd24a;
}

/* ------------------------------------------------------------------------ */
/* Help footer                                                              */
/* ------------------------------------------------------------------------ */

.tv-help {
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 1.5px;
  color: var(--tv-text-dimmer);
  text-transform: uppercase;
  line-height: 1.9;
}

.tv-help kbd {
  display: inline-block;
  padding: 2px 6px;
  margin: 0 2px;
  background: #15151c;
  border: 1px solid #2a2a35;
  border-radius: 3px;
  color: var(--tv-text-dim);
  font-family: inherit;
  font-size: 10px;
}

.tv-about {
  width: min(760px, 100%);
  margin: 18px auto 0;
  padding-top: 24px;
  border-top: 1px solid var(--tv-card-border);
  color: var(--tv-text-dim);
  font-size: 14px;
  line-height: 1.65;
}

.tv-about h2 {
  margin: 0 0 10px;
  color: var(--tv-text);
  font-size: 20px;
}

.tv-about p {
  margin: 8px 0 0;
}

.tv-about a {
  color: var(--tv-accent);
}

.tv-fineprint {
  text-align: center;
  font-size: 11px;
  color: var(--tv-text-dimmer);
  margin: 0;
  font-style: italic;
}

/* ------------------------------------------------------------------------ */
/* Responsive                                                               */
/* ------------------------------------------------------------------------ */

@media (max-width: 768px) {
  .tv-room {
    /* Same clearance rationale as desktop, just tightened: mobile
       back button is smaller and the header reduces its padding. */
    padding: 64px 10px 80px;
    gap: 14px;
  }

  .tv-breadcrumbs {
    /* Mobile back button is ~90px wide at top:12 left:12; 110px
       leaves a small visual gap to the first breadcrumb. */
    padding: 11px 12px 11px 110px;
    font-size: 11px;
  }

  .tv-bezel {
    padding: 8px 8px 0;
    border-radius: 14px;
  }

  .tv-marquee {
    padding: 10px 12px;
    gap: 10px;
  }

  .tv-title {
    font-size: 15px;
  }

  .tv-subtitle {
    font-size: 11px;
  }

  .tv-summary {
    padding: 8px 12px;
  }

  .tv-summary-text {
    font-size: 12px;
    line-height: 1.45;
  }

  .tv-btn {
    padding: 9px 12px;
    font-size: 13px;
  }

  .tv-ep-grid,
  .tv-show-grid,
  .tv-continue-grid,
  .tv-saved-grid {
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: 10px;
  }

  .tv-ep-meta {
    padding: 8px 10px 10px;
  }

  .tv-ep-title {
    font-size: 12px;
  }
}

@media (max-width: 420px) {
  .tv-ep-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .tv-show-grid,
  .tv-continue-grid,
  .tv-saved-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }

  .tv-controls {
    gap: 6px;
  }

  .tv-btn {
    padding: 8px 10px;
    font-size: 12px;
  }

  .tv-toggle {
    padding: 6px 10px;
    font-size: 12px;
  }

  .tv-marquee {
    flex-wrap: wrap;
  }

  .tv-now-stack {
    flex-basis: 100%;
  }

  .tv-chip {
    padding: 10px 14px;
    font-size: 13px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tv-led,
  .tv-rec-dot {
    animation: none;
  }

  .tv-show-card,
  .tv-ep-card {
    transition: none;
  }

  .tv-show-card:hover,
  .tv-ep-card:hover {
    transform: none;
  }
}

/* ============================================================================
 * TV mode overlay
 * ============================================================================
 * Activates when the document root carries `data-mode="tv"`. The mode is set
 * by `modules/mode.js` based on three signals (in priority order): the
 * `?tv=1` query param (manual override, useful for desktop preview), a
 * `window.__WATCH_TV__` flag injected by the native Android-TV WebView shell,
 * and a UA fingerprint of TV-class browsers (Android TV, Tizen, WebOS, HbbTV).
 *
 * Goal of these overrides: bigger UI for ~8-foot living-room viewing, focus
 * rings strong enough to read across the room, and remove mouse-only chrome
 * the remote can't reach. Almost no behavior changes — just typography,
 * spacing, and focus-ring styling.
 *
 * Modality (data-modality) is also flipped here so a focus ring only paints
 * when the user is currently driving with the keyboard / D-pad. Pointer
 * users on a TV (rare — Bluetooth mouse plugged into the set) get the
 * same hover-friendly experience the desktop site has.
 * ========================================================================= */

:root[data-mode='tv'] {
  /* Most Android TVs expose a 960×540 CSS viewport at 2×
     dpr (1920×1080 device pixels). These knobs are sized for that
     canvas — NOT 1920 CSS px. Trying to declare a 1920 viewport
     just causes horizontal scroll on 320dpi panels because
     `loadWithOverviewMode` doesn't reliably scale the page back
     down. So we design for 960 CSS and let the OS scale up. */
  --tv-base-size: 18px;
  /* `--tv-tile-min` sizes 16:9 cards (episode grid + Up-next strip).
     140px keeps thumbnails legible — drop below that and the still
     image inside the thumb starts losing detail. The Continue-Watching
     and Saved Offline rails also use this width, so 1–2 entries don't
     bloat to half-screen. */
  --tv-tile-min: 140px;
  /* `--tv-poster-min` sizes 2:3 portrait posters (only the home-page
     show wall, currently). Posters carry the show title in artwork,
     so they stay readable at much narrower widths than 16:9 thumbs.
     110px on a 960 CSS-px TV viewport = 6–7 poster columns,
     matching Plex's TV home-page density. */
  --tv-poster-min: 110px;
  --tv-tile-gap: 14px;
}

:root[data-mode='tv'] body {
  font-size: var(--tv-base-size);
  /* Disable text smoothing tricks that look worse at TV scaling. */
  -webkit-font-smoothing: antialiased;
  /* Reserve a real left column for the quicknav rail. The rail is
     `position: fixed; left: 14px; width: 60px`, so on typical TV
     viewports it lands right where `.tv-room` (centered, ~940
     max-width) starts — making it visually float on top of the
     show / movie grid. Shifting body content right by 88px (rail
     left-inset 14 + rail width 60 + 14 gap) gives the rail its own
     column without changing its anchor or the rail's own styles.
     Desktop and mobile keep the original floating layout — mobile
     hides the rail entirely (see the .tv-quicknav @media block),
     and desktop has enough room around .tv-room that the overlap
     never happens. */
  padding-left: 88px;
}

/* On TV we open the .tv-room out from its default 1280px max-width
   to fill the full safe area. 940px CSS px still leaves a ~10px
   inset for TVs that slightly overscan the visible region. */
:root[data-mode='tv'] .tv-room {
  max-width: 940px;
  padding: 32px 24px 64px;
  gap: 20px;
}

:root[data-mode='tv'] .tv-header {
  /* Header padding scales with the larger base font; the breadcrumbs
     inside also bump font-size below. */
  padding: 8px 0;
}

:root[data-mode='tv'] .tv-breadcrumbs {
  font-size: 16px;
  padding: 0 48px;
}

/* TV-mode landing trims the marketing chrome. Streaming-app
   convention: posters dominate the home screen, the user already
   chose to open the app so re-pitching it ("Smart-TV style player
   that streams classic TV…") just delays them seeing content. The
   title shrinks to a small section header and the blurb is hidden
   entirely — Plex / Apple TV / Roku do the same thing on their
   home pages. The search input + tag chip row are kept because they
   actually filter the grid. */
:root[data-mode='tv'] .tv-landing-intro {
  max-width: none;
  padding: 0;
  margin: 0;
  text-align: left;
  align-items: stretch;
  gap: 12px;
}

:root[data-mode='tv'] .tv-landing-title {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--tv-text-dim);
}

:root[data-mode='tv'] .tv-landing-blurb {
  display: none;
}

/* Original TV-mode blurb sizing, kept here so the cascade rule below
   (which would otherwise apply to a hidden element) doesn't carry
   surprise impact when the rule is unhidden in a future redesign. */
:root[data-mode='tv'] .tv-landing-blurb.show {
  display: block;
  font-size: 18px;
  max-width: 64ch;
}

/* Bigger search box; on TV the on-screen keyboard takes a chunk of
   the screen, so a tall input target reads better. */
:root[data-mode='tv'] .tv-search-input {
  font-size: 22px;
  padding: 14px 18px;
}

:root[data-mode='tv'] .tv-tag-chip {
  font-size: 14px;
  padding: 8px 14px;
}

/* Episode grid uses the 16:9-thumb tile size (`--tv-tile-min`).
   1fr lets tiles expand to fill the row evenly when the season is
   large; on a fully-populated grid this looks tidy. */
:root[data-mode='tv'] .tv-ep-grid {
  grid-template-columns: repeat(auto-fill, minmax(var(--tv-tile-min), 1fr));
  gap: var(--tv-tile-gap);
}

/* Show grid uses the smaller poster-width (`--tv-poster-min`) so the
   home page reads as a Plex-style poster wall rather than a row of
   tall billboards. Same 1fr stretch behavior — the minmax is the
   only thing that differs between these grids. */
:root[data-mode='tv'] .tv-show-grid {
  grid-template-columns: repeat(auto-fill, minmax(var(--tv-poster-min), 1fr));
  gap: var(--tv-tile-gap);
}

/* "Rail"-style grids (Continue Watching, Saved Offline) only ever
   hold the user's recent items — usually 1–4. With minmax(_, 1fr)
   those expand to "fill" the row, blowing 1–2 cards up to half-screen
   each (the "gigantic cards" TV-layout bug). Fixed track width pins
   them to poster proportions and aligns left like a Plex / Netflix
   carousel; the rest of the row just sits empty. */
:root[data-mode='tv'] .tv-continue-grid,
:root[data-mode='tv'] .tv-saved-grid {
  grid-template-columns: repeat(auto-fill, var(--tv-tile-min));
  gap: var(--tv-tile-gap);
  justify-content: start;
}

:root[data-mode='tv'] .tv-show-name {
  font-size: 12px;
  font-weight: 500;
  /* Couch-readable but kept compact so the caption doesn't dominate
     the poster — Plex's TV grid uses 12–13px captions on a 1080p
     panel for the same reason. We also drop the line-height a touch
     (default 1.3 → 1.2) so a single-line caption slot feels close
     to its poster instead of floating mid-air. */
  line-height: 1.2;
}

:root[data-mode='tv'] .tv-show-tag {
  font-size: 15px;
}

:root[data-mode='tv'] .tv-section-label {
  font-size: 18px;
}

:root[data-mode='tv'] .tv-chip {
  font-size: 14px;
  padding: 8px 14px;
}

/* Chip row: drop the horizontal scroll on TV. With a remote, `auto`
   overflow on a focusable container is a focus trap — Chromium
   spatial-nav skips chips inside an overflow-scrolling row when
   navigating from below, leaving the user unable to reach the
   season chips on shows with many seasons (Aqua Teen has 12). Wrap
   instead — taller chip cluster, every chip directly reachable. */
:root[data-mode='tv'] .tv-chip-row {
  flex-wrap: wrap;
  overflow-x: visible;
  scroll-snap-type: none;
}

:root[data-mode='tv'] .tv-btn {
  font-size: 18px;
  padding: 12px 22px;
}

/* Focus styling — the headline TV-mode change. Across all interactive
   surfaces, focus paints a thick accent ring + slight scale to read
   from the couch. We rely on `:focus-visible` so a finger-tap on a
   touchscreen TV doesn't leave a stuck ring. Show cards moved to a
   poster-wall layout (transparent card, rounded poster) so the ring
   lands on `.tv-show-poster` instead of the card itself; without
   that override the ring would frame the caption text too. */
:root[data-mode='tv'] .tv-show-card:focus-visible {
  outline: none;
}

:root[data-mode='tv'] .tv-show-card:focus-visible .tv-show-poster {
  outline: 4px solid var(--show-accent, var(--tv-accent));
  outline-offset: 4px;
  transform: scale(1.04);
  z-index: 1;
  box-shadow: 0 0 0 1px var(--show-accent, var(--tv-accent)), 0 12px 30px rgba(0, 0, 0, 0.65),
    0 0 32px color-mix(in srgb, var(--show-accent, var(--tv-accent)) 45%, transparent);
}

:root[data-mode='tv'] .tv-show-card:focus-visible .tv-show-name {
  color: var(--tv-text);
}

:root[data-mode='tv'] .tv-continue-card:focus-visible,
:root[data-mode='tv'] .tv-ep-card:focus-visible,
:root[data-mode='tv'] .tv-upnext-card:focus-visible {
  outline: 4px solid var(--show-accent, var(--tv-accent));
  outline-offset: 4px;
  transform: scale(1.04);
  z-index: 1;
  box-shadow: 0 0 0 1px var(--show-accent, var(--tv-accent)), 0 12px 30px rgba(0, 0, 0, 0.65),
    0 0 32px color-mix(in srgb, var(--show-accent, var(--tv-accent)) 45%, transparent);
}

:root[data-mode='tv'] .tv-tag-chip:focus-visible,
:root[data-mode='tv'] .tv-chip:focus-visible,
:root[data-mode='tv'] .tv-btn:focus-visible {
  outline: 4px solid var(--tv-accent);
  outline-offset: 3px;
  box-shadow: 0 0 0 1px var(--tv-accent),
    0 0 24px color-mix(in srgb, var(--tv-accent) 50%, transparent);
}

:root[data-mode='tv'] .tv-search-input:focus-visible {
  outline: 4px solid var(--tv-accent);
  outline-offset: 0;
}

/* Chrome that mouse-driven UX leans on but a remote can't reach.
   `nav.js`, `feedback-button`, and `share-button` all live in the
   global launcher styles; on TV we hide them and rely on the
   in-app breadcrumb trail + hardware Back key for navigation. */
/* ====================================================================
 * Fullscreen player layout (TV mode, watch view only)
 * ====================================================================
 * The CRT chassis (bezel, marquee, base, up-next rail, controls row)
 * burns half the panel on decoration. On TV we collapse it: watch-view.js
 * sets `data-fullscreen="player"` on <html> at mount and clears it on
 * unmount; these rules pin <video> to the viewport. Hardware Back
 * unwinds pushState to the episode list — the only "exit fullscreen"
 * gesture that exists on a TV remote. */
:root[data-mode='tv'][data-fullscreen='player'] {
  overflow: hidden;
}

:root[data-mode='tv'][data-fullscreen='player'] body,
:root[data-mode='tv'][data-fullscreen='player'] .tv-room {
  background: #000 !important;
  max-width: none !important;
  margin: 0 !important;
  padding: 0 !important;
  gap: 0 !important;
}

/* Hide chrome OUTSIDE the player but keep the slot wrapper
   (`.tv-view` / `.tv-room`) intact — they're the ancestors of
   `.tv-player`, and a `display: none` on either collapses the whole
   video tree. We only hide the chassis bits inside `.tv-player`
   that aren't the video surface itself. The resume overlay and
   end-card are children of `.tv-screen` so they stay visible —
   they ARE the only legit non-video controls on a playing TV. */
:root[data-mode='tv'][data-fullscreen='player'] .tv-header,
:root[data-mode='tv'][data-fullscreen='player'] .tv-breadcrumbs,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-bezel-label,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-channel-chip,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-base,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-stand,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-marquee,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-controls,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-summary,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-section,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-all-eps-link,
:root[data-mode='tv'][data-fullscreen='player'] .tv-player .tv-help {
  display: none !important;
}

/* Flatten the `.tv-player → .tv-set → .tv-bezel → .tv-screen` nesting
   to a single fullscreen layer. All four are forced to the viewport
   bounds; the innermost (`.tv-screen`) holds the <video>, overlays,
   and the end-card. The aspect-ratio rule on `.tv-screen` (16:9 in
   the chassis CSS) is overridden so it can match the panel exactly. */
:root[data-mode='tv'][data-fullscreen='player'] .tv-player,
:root[data-mode='tv'][data-fullscreen='player'] .tv-set,
:root[data-mode='tv'][data-fullscreen='player'] .tv-bezel,
:root[data-mode='tv'][data-fullscreen='player'] .tv-screen {
  position: fixed !important;
  inset: 0 !important;
  width: 100vw !important;
  height: 100vh !important;
  max-width: none !important;
  max-height: none !important;
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 0 !important;
  aspect-ratio: auto !important;
  background: #000 !important;
  box-shadow: none !important;
  filter: none !important;
}

:root[data-mode='tv'][data-fullscreen='player'] .tv-video {
  width: 100vw !important;
  height: 100vh !important;
  object-fit: contain;
}

:root[data-mode='tv'] .back-to-launcher,
:root[data-mode='tv'] feedback-button,
:root[data-mode='tv'] share-button,
:root[data-mode='tv'] .tv-fineprint {
  display: none !important;
}

/* Pointer modality — a hover-equipped session, regardless of device
   class. Suppress focus outlines so a click doesn't leave a stuck
   ring on mouse-driven TVs. We deliberately do NOT touch
   `:focus-visible` — the browser already handles modality for that
   selector; this just covers the ambient `:focus`. */
:root[data-modality='pointer'] *:focus:not(:focus-visible) {
  outline: none;
}
