/* ==========================================================================
   Scott's Shelf — Blockbuster Video design system.
   Midnight blue chrome with a torn-ticket yellow accent. Posters still
   carry the color; the shelf line is the yellow ticket band.
   ========================================================================== */

@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 200 800;
  font-stretch: 100%;
  font-display: swap;
  src: url("/static/fonts/bricolage-latin.woff2") format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 200 800;
  font-stretch: 100%;
  font-display: swap;
  src: url("/static/fonts/bricolage-latin-ext.woff2") format("woff2-variations");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --bg: #0a1838;
  --bg-elevated: #11214a;
  --bg-card: #152960;
  --bg-hover: #1d3578;
  --border: #2b4490;
  --border-strong: #3a5cb0;
  --border-focus: #ffcc00;
  --text: #f5f3e8;
  --text-muted: #a8bce0;
  --text-dim: #6c83b3;
  --accent: #ffcc00;
  --accent-hover: #ffe34d;
  --ink: #0a1838; /* text on yellow ticket */
  --shelf: #ffcc00;
  --rating: #ffcc00;
  --success: #8be4a8;
  --warn: #ff9a3c;
  --error: #ff7066;
  --score-neg: #ff7066;
  --score-low: #ff9a3c;
  --score-mid: #ffe34d;
  --score-high: #8be4a8;
  --radius-sm: 3px;
  --radius: 4px;
  --radius-lg: 6px;
  --shadow: 0 12px 20px -10px rgba(0, 0, 0, 0.7);
  --font: "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  --font-display: "Bricolage Grotesque", "Segoe UI", system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --max-width: 1200px;
}

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

html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
}

/* One faint film-grain pass over everything; surfaces read as material,
   not flat hex. The parked decoy keeps its own (deliberately plain) look. */
body:not(.parked-page)::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); }

img { max-width: 100%; height: auto; }

:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}

/* ---------- async fragment settle ------------------------------------- */

/* The skeletons hold layout still while fragments load; this makes the
   swap itself gentle. htmx inserts new content with .htmx-added and drops
   the class ~20ms later, so fragment roots that carry a transition fade
   from 0 to 1 instead of popping. Scoped to the known swap roots — a
   global opacity transition would leak into hover/disabled states. */
.imdb-async, .rt-async, .lb-async, .boxoffice-strip,
#rt-reviews, #lb-reviews,
#movie-releases-body, #movie-suggestions-body,
#monitor-callout,
#results > *, .episode-release-results > *, .grab-cell > *, #dl-panel > * {
  transition: opacity 0.18s ease;
}
.htmx-added { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .htmx-added { opacity: 1; }
}

/* ---------- header ---------------------------------------------------- */

/* The header sits on the iconic yellow ticket band — Blockbuster's
   storefront signage strip. */
.app-header {
  background: var(--bg);
  border-bottom: 3px solid var(--accent);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
}

.header-top {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0.75rem 1.25rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: 0 0 auto;
  color: var(--text);
}

.brand-mark {
  display: flex;
  width: 2.25rem;
  height: 2.25rem;
  flex: 0 0 auto;
}
.brand-mark img {
  display: block;
  width: 100%;
  height: 100%;
}

.brand-text {
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
  gap: 3px;
  line-height: 1;
}
/* Bricolage italic + tight tracking gives "SCOTT'S SHELF" the slanted-bold
   feel of the Blockbuster wordmark without literally being it. */
.brand-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-style: italic;
  font-size: 1.1rem;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--text);
}
.brand-name-accent { color: var(--accent); }
.brand:hover .brand-name,
.brand:hover .brand-name-accent { color: var(--accent-hover); }

/* The yellow band under the wordmark is the torn-ticket signature line. */
.brand-shelf {
  height: 3px;
  border-radius: 1px;
  background: var(--accent);
}

.header-search {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 28rem;
  max-width: 36rem;
}
.header-search input[type="search"] {
  min-width: 10rem;
  flex: 1;
  background: var(--bg-elevated);
  text-overflow: ellipsis;
}
.header-search .btn {
  padding-inline: 0.95rem;
}

.header-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* Chips are spine labels: square-cornered mono tags, not pills. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.28rem 0.55rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  border-radius: 2px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  color: var(--text-muted);
  white-space: nowrap;
  max-width: 14rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chip-rating { color: var(--rating); border-color: rgba(255, 204, 0, 0.35); }
.rt-async {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
}
.rt-score-loader .chip {
  color: var(--text-dim);
  border-color: rgba(108, 131, 179, 0.25);
}
/* Skeletons hold still — quiet dim slabs, no shimmer sweep. No fixed width:
   the chip sizes to its content (icon + percentage placeholder) so it matches
   the loaded chip. */
.rt-skeleton-chip {
  min-height: 1.85rem;
  border-color: rgba(108, 131, 179, 0.22);
  background: rgba(108, 131, 179, 0.08);
}
.rt-icon-skeleton,
.rt-skeleton-line {
  border-radius: 2px;
  background: rgba(108, 131, 179, 0.2);
}
.rt-icon-skeleton {
  width: 1rem;
  height: 1rem;
}
/* Score chips run a touch larger than the utility chips (release size/age,
   spine labels): they're the hero's headline data, and the brand marks need
   the room to read. The skeletons share these classes, so loading and loaded
   footprints stay identical. */
.rt-chip {
  gap: 0.35rem;
  min-height: 1.85rem;
  font-size: 0.78rem;
  font-weight: 650;
  color: var(--text);
  background: var(--bg-card);
}
.rt-chip:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--bg-hover);
}
.rt-critics { border-color: rgba(229, 57, 53, 0.45); }
.rt-audience { border-color: rgba(245, 193, 72, 0.45); }
.rt-icon {
  position: relative;
  display: inline-block;
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
}
.rt-icon-tomato {
  border-radius: 50%;
  background:
    radial-gradient(circle at 35% 30%, #ff8a80 0 16%, transparent 17%),
    #e53935;
  box-shadow: inset 0 -0.08rem 0 rgba(91, 15, 15, 0.28);
}
.rt-icon-tomato::before {
  content: "";
  position: absolute;
  left: 0.25rem;
  top: -0.12rem;
  width: 0.42rem;
  height: 0.28rem;
  background: #4ecf8a;
  clip-path: polygon(50% 0, 63% 36%, 100% 24%, 72% 54%, 88% 100%, 50% 70%, 12% 100%, 28% 54%, 0 24%, 37% 36%);
}
.rt-icon-popcorn {
  border-radius: 0.16rem 0.16rem 0.26rem 0.26rem;
  background:
    linear-gradient(90deg, #f8f0d6 0 25%, #d83535 25% 50%, #f8f0d6 50% 75%, #d83535 75%),
    #f8f0d6;
  box-shadow: inset 0 -0.08rem 0 rgba(88, 40, 15, 0.25);
}
.rt-icon-popcorn::before {
  content: "";
  position: absolute;
  left: -0.04rem;
  right: -0.04rem;
  top: -0.18rem;
  height: 0.32rem;
  border-radius: 999px;
  background:
    radial-gradient(circle at 12% 70%, #ffd65a 0 0.13rem, transparent 0.14rem),
    radial-gradient(circle at 32% 40%, #fff0a8 0 0.15rem, transparent 0.16rem),
    radial-gradient(circle at 55% 60%, #ffd65a 0 0.14rem, transparent 0.15rem),
    radial-gradient(circle at 78% 42%, #fff0a8 0 0.15rem, transparent 0.16rem);
}
.rt-icon-splat {
  background: #a8c25e;
  clip-path: polygon(47% 0, 58% 24%, 83% 9%, 76% 36%, 100% 48%, 76% 59%, 88% 91%, 59% 75%, 45% 100%, 35% 72%, 8% 86%, 23% 57%, 0 42%, 28% 34%, 17% 8%, 38% 24%);
  filter: drop-shadow(0 0.03rem 0 rgba(0, 0, 0, 0.35));
}

/* ---------- imdb chip ------------------------------------------------- */

.imdb-async {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
}
.imdb-chip {
  gap: 0.35rem;
  min-height: 1.85rem;
  font-size: 0.78rem;
  font-weight: 650;
  color: var(--text);
  background: var(--bg-card);
  border-color: rgba(245, 197, 24, 0.5);
}
.imdb-chip:hover {
  color: var(--text);
  border-color: #f5c518;
  background: var(--bg-hover);
}

/* IMDb's gold wordmark badge, drawn in CSS to match how the RT/LB marks
   are done — black "IMDb" on the brand yellow (#f5c518). */
.imdb-logo {
  flex: 0 0 auto;
  padding: 0.08rem 0.26rem;
  border-radius: 2px;
  background: #f5c518;
  color: #000;
  font-weight: 800;
  font-size: 0.65rem;
  letter-spacing: -0.01em;
  line-height: 1.3;
}
.imdb-rating {
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.imdb-count {
  font-family: var(--font-mono);
  color: var(--text-dim);
  font-size: 0.7rem;
  padding-left: 0.15rem;
  border-left: 1px solid var(--border);
}
/* Number-shaped placeholder shared by the IMDb and Letterboxd skeletons.
   The loading chip carries real placeholder digits sized for the assumed
   character count (rendered as a dim slab via transparent text), so the
   chip reserves exactly the width the loaded value will occupy and the row
   doesn't reflow when the async number swaps in. The chip numbers are
   monospace, so a same-length placeholder is a pixel-accurate stand-in.
   Height/overflow keep it a thin slab rather than a full-height block. */
/* Scoped under the chip wrappers so `color: transparent` reliably beats the
   .lb-rating/.lb-count (and IMDb / rt-chip) color rules regardless of source
   order. */
.imdb-async .chip-skeleton-text,
.lb-async .chip-skeleton-text,
.rt-async .chip-skeleton-text {
  display: inline-block;
  height: 0.6rem;
  overflow: hidden;
  color: transparent;
  background: rgba(108, 131, 179, 0.2);
  border-radius: 2px;
}

/* ---------- metacritic chip ------------------------------------------ */

/* The chip leads with the Metacritic wordmark (its logo) then the
   Metascore, matching the "logo: rating" shape of the IMDb/RT/LB chips.
   Metacritic colors a metascore by band — green (favorable, 61+), yellow
   (mixed, 40–60), red (unfavorable, ≤39) — so the number sits in a small
   rounded square in its band color, Metacritic's signature Metascore. */
:root {
  --mc-good:  #00a85a;
  --mc-mixed: #ffcc33;
  --mc-bad:   #e74c3c;
}

.metacritic-chip {
  gap: 0.35rem;
  min-height: 1.85rem;
  font-size: 0.78rem;
  font-weight: 650;
  color: var(--text);
  background: var(--bg-card);
  border-color: rgba(108, 131, 179, 0.35);
}
.metacritic-chip:hover {
  color: var(--text);
  border-color: var(--text-dim);
  background: var(--bg-hover);
}
/* The Metacritic roundel leads the chip like the gold badge leads the
   IMDb chip / the tomato leads RT — an inline-SVG brand mark, so the
   colored Metascore stays the rating to its right. */
.metacritic-logo {
  flex: 0 0 auto;
  width: 1.2rem;
  height: 1.2rem;
  display: block;
}
/* The colored Metascore square (the rating): white digits on the band
   color, except the light yellow "mixed" band which needs dark digits. */
.metacritic-badge {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.2rem;
  height: 1.2rem;
  padding: 0 0.18rem;
  border-radius: 3px;
  color: #fff;
  font-weight: 800;
  font-size: 0.75rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.metascore-good  { background: var(--mc-good); }
.metascore-mixed { background: var(--mc-mixed); color: #1a1a1a; }
.metascore-bad   { background: var(--mc-bad); }
.metacritic-count {
  font-family: var(--font-mono);
  color: var(--text-dim);
  font-size: 0.7rem;
  padding-left: 0.2rem;
  border-left: 1px solid var(--border);
}
/* Loading skeleton reuses the loaded chip's exact structure (real roundel
   + Metascore square + count) so the footprint matches to the pixel. The
   square drops its band color for a neutral slab; the count is painted
   transparent by the .imdb-async .chip-skeleton-text rule. */
.metacritic-badge.metascore-skeleton {
  background: rgba(108, 131, 179, 0.2);
  color: transparent;
}

/* ---------- box office strip ------------------------------------------ */

/* A row of labeled money chips ("Budget $160M · Worldwide $864M") under
   the hero overview, loaded async off IMDb. Same chip anatomy as the
   score chips: muted label leading, value in the chip's text color. */
.boxoffice-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.9rem;
}
.bo-chip {
  gap: 0.35rem;
  min-height: 1.85rem;
  font-size: 0.78rem;
  background: var(--bg-card);
}
.bo-label {
  color: var(--text-dim);
  font-size: 0.62rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.bo-value {
  color: var(--text);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}
/* Skeleton values reuse the transparent-text slab treatment the IMDb/LB
   chip skeletons use, scoped here because those rules are scoped to
   their own wrappers. */
.boxoffice-loader .chip-skeleton-text {
  display: inline-block;
  height: 0.6rem;
  overflow: hidden;
  color: transparent;
  background: rgba(108, 131, 179, 0.2);
  border-radius: 2px;
}

/* ---------- letterboxd chip + histogram ------------------------------ */

/* Letterboxd's brand greens (the iconic three-dot logo's hues) — used to
   tell the LB chip and histogram apart from the RT chips on the meta row. */
:root {
  --lb-green: #00d735;
  --lb-orange: #ff8000;
  --lb-blue:  #40bcf4;
}

.lb-async {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
}
.lb-score-loader .chip {
  color: var(--text-dim);
  border-color: rgba(108, 131, 179, 0.25);
}
/* No fixed width: the skeleton sizes to its content (icon + rating + count
   placeholders) so it occupies the same space the loaded chip will. */
.lb-skeleton-chip {
  min-height: 1.85rem;
  border-color: rgba(108, 131, 179, 0.22);
  background: rgba(108, 131, 179, 0.08);
}
/* Matches the real three-dot icon's footprint (1.65rem × 0.65rem). */
.lb-icon-skeleton {
  width: 1.65rem;
  height: 0.65rem;
  border-radius: 2px;
  background: rgba(108, 131, 179, 0.2);
}

.lb-chip {
  gap: 0.35rem;
  min-height: 1.85rem;
  font-size: 0.78rem;
  font-weight: 650;
  color: var(--text);
  background: var(--bg-card);
  border-color: rgba(0, 215, 53, 0.45);
}
.lb-chip:hover {
  color: var(--text);
  border-color: var(--lb-green);
  background: var(--bg-hover);
}
.lb-rating {
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.lb-count {
  font-family: var(--font-mono);
  color: var(--text-dim);
  font-size: 0.7rem;
  padding-left: 0.15rem;
  border-left: 1px solid var(--border);
}

/* Letterboxd's three-dot mark, drawn in CSS like the RT tomato/popcorn
   icons: orange, green, blue circles in the brand's left-to-right order,
   overlapping slightly so it reads as the Letterboxd logo at chip size. */
.lb-icon {
  display: inline-block;
  flex: 0 0 auto;
  width: 1.65rem;
  height: 0.65rem;
  background:
    radial-gradient(circle 0.32rem at 0.32rem 50%, var(--lb-orange) 96%, transparent) no-repeat,
    radial-gradient(circle 0.32rem at 0.82rem 50%, var(--lb-green) 96%, transparent) no-repeat,
    radial-gradient(circle 0.32rem at 1.32rem 50%, var(--lb-blue) 96%, transparent) no-repeat;
}

/* ---------- reviews tabs (movie page) -------------------------------- */

/* The Critics (RT) + Letterboxd review panels share one screen region
   on the movie page via this tablist. CSS only handles the chrome —
   show/hide is the [hidden] attribute set by inline JS. */
.reviews-tabs {
  margin: 0 0 2rem;
}
.reviews-tablist {
  display: flex;
  gap: 0.15rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1rem;
}
.reviews-tab {
  flex: 0 0 auto;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  margin-bottom: -1px;
  padding: 0.6rem 0.95rem;
  color: var(--text-muted);
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.reviews-tab:hover { color: var(--text); }
.reviews-tab[aria-selected="true"] {
  color: var(--accent-hover);
  border-bottom-color: var(--accent);
}
.reviews-tab:focus-visible { outline: 2px solid var(--border-focus); outline-offset: -2px; }
/* The fragment templates carry their own section heading (the season
   page reuses them outside the tabs and still needs it). Inside a tab
   panel that heading is redundant with the tab label — hide it and
   collapse the section's extra margin. */
.reviews-panel .section-heading-row { display: none; }
.reviews-panel .rt-review-section,
.reviews-panel .lb-review-section { margin: 0; }
.reviews-empty {
  background: var(--bg-card);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}

/* ---------- letterboxd review cards ---------------------------------- */

.lb-review-section {
  --lb-review-card-height: 12rem;
  margin: 0 0 2rem;
}
.lb-review-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.85rem;
}
.lb-review-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  height: var(--lb-review-card-height);
  overflow: hidden;
  padding: 0.95rem;
  border-radius: var(--radius);
  background: var(--bg-card);
  border: 1px solid var(--border);
}
.lb-review-card::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  border-radius: var(--radius) 0 0 var(--radius);
  background: var(--lb-green);
}
.lb-review-card-skeleton::before {
  background: rgba(108, 131, 179, 0.3);
}
.lb-review-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}
.lb-review-reviewer {
  font-weight: 650;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lb-review-rating {
  font-family: var(--font-mono);
  letter-spacing: 0.04em;
  color: var(--lb-orange);
}
.lb-review-liked {
  color: var(--lb-orange);
  font-size: 0.9rem;
  line-height: 1;
}
.lb-review-quote {
  margin: 0;
  color: var(--text);
  font-size: 0.9rem;
  font-style: italic;
  line-height: 1.45;
  display: -webkit-box;
  line-clamp: 5;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
  flex: 1 1 auto;
}
.lb-review-link {
  align-self: flex-start;
  margin-top: auto;
  color: var(--lb-green);
  font-size: 0.78rem;
  font-weight: 650;
}
.lb-review-link:hover {
  color: var(--lb-orange);
}

/* ---------- layout ---------------------------------------------------- */

.page-main {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 1.25rem 1.25rem 3rem;
}

.page-title {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.page-lead {
  margin: 0 0 1.25rem;
  color: var(--text-dim);
  font-size: 0.92rem;
  text-wrap: balance;
}

.section-title {
  margin: 0 0 0.5rem;
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 650;
}
/* Subtitles are prose, so they stay in the body face — mono is reserved
   for data (years, scores, sizes, release names, spine labels). */
.section-subtitle {
  margin: -0.25rem 0 0;
  color: var(--text-dim);
  font-size: 0.82rem;
}

.section-meta {
  margin: 0 0 1rem;
  color: var(--text-muted);
  font-size: 0.88rem;
}

/* ---------- breadcrumbs ----------------------------------------------- */

.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.5rem;
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  font-size: 0.88rem;
  color: var(--text-muted);
}
.breadcrumb li:not(:last-child)::after {
  content: "›";
  margin-left: 0.5rem;
  color: var(--text-dim);
}
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: var(--accent); }
.breadcrumb [aria-current="page"] { color: var(--text); }

/* ---------- banners & alerts ------------------------------------------ */

.banner {
  padding: 0.75rem 1rem;
  border-radius: var(--radius);
  margin-bottom: 1rem;
  font-size: 0.9rem;
  line-height: 1.45;
}
.banner-warn {
  background: rgba(255, 154, 60, 0.08);
  border: 1px solid rgba(255, 154, 60, 0.35);
  color: #ffd9a8;
}
.alert {
  padding: 0.85rem 1rem;
  border-radius: var(--radius);
  margin-bottom: 1rem;
}
.alert-error,
.banner-error {
  background: rgba(255, 112, 102, 0.1);
  border: 1px solid rgba(255, 112, 102, 0.35);
  color: #ffd0c8;
}
.alert-error p { margin: 0.35rem 0 0; font-family: var(--font-mono); font-size: 0.85rem; word-break: break-word; }

/* The database-health banner lives inside the full-bleed header; align it
   with the header-top content column. */
.db-health-banner {
  max-width: var(--max-width);
  width: calc(100% - 2.5rem);
  margin: 0 auto 0.75rem;
}
.db-health-banner p { margin: 0.35rem 0 0; font-family: var(--font-mono); font-size: 0.85rem; word-break: break-word; }

/* ---------- forms & buttons ------------------------------------------- */

input, select, button {
  font: inherit;
  color: var(--text);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.55rem 0.75rem;
}
input[type="search"] { border-radius: var(--radius); }

/* Box model lives here (not just on the input/select/button selector) so
   .btn styles an <a> as well as a <button>. */
.btn {
  display: inline-block;
  padding: 0.55rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
  font-weight: 500;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s;
}
.btn:hover { background: var(--bg-hover); }
.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
  font-weight: 600;
}
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
/* Grab is the marquee action — the loudest thing on any page. */
.btn-grab {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
  font-weight: 650;
  padding: 0.45rem 1rem;
}
.btn-grab:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-ghost {
  background: transparent;
  border-color: var(--border);
  color: var(--text-muted);
  padding: 0.3rem 0.7rem;
  font-size: 0.78rem;
}
.btn-ghost:hover { background: var(--bg-hover); border-color: var(--accent); color: var(--text); }
.btn-danger:hover {
  color: var(--error);
  border-color: rgba(255, 112, 102, 0.5);
  background: rgba(255, 112, 102, 0.1);
}
.btn:disabled, .btn[disabled] { opacity: 0.6; cursor: wait; }

/* ---------- view toggles ---------------------------------------------- */

.view-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 0.75rem;
}
.view-toggle {
  font-size: 0.88rem;
  font-weight: 500;
}
.view-toggle-secondary { color: var(--text-muted); }
.view-footer { margin-top: 1.25rem; }

/* ---------- empty states ---------------------------------------------- */

/* Empty states speak; they don't sit in dashed boxes. The brand ticket
   presides over the full-size ones, washed out; compact variants (inline
   review/suggestion notices) stay text-only. */
.empty-state {
  padding: 3rem 1.5rem;
  text-align: center;
}
.empty-state::before {
  content: "";
  display: block;
  width: 64px;
  height: 64px;
  margin: 0 auto 0.85rem;
  background: url("/static/favicon.svg") center / contain no-repeat;
  opacity: 0.45;
}
.empty-compact::before { content: none; }
.empty-welcome { margin-top: 0.5rem; }
.empty-title {
  margin: 0 0 0.4rem;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 650;
  color: var(--text);
}
.empty-hint { margin: 0; color: var(--text-dim); font-size: 0.9rem; }

/* ---------- media cards (home + seasons) ------------------------------ */

.media-grid,
.season-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(155px, 1fr));
  gap: 1.75rem 1rem;
}

.season-grid { grid-template-columns: repeat(auto-fill, minmax(175px, 1fr)); }

/* Cards are shelf items, not bordered boxes: a bare poster standing on a
   shelf rule, with the caption below the rule like a shelf-edge label.
   The shelf is each card's .card-body border-top, bled half the grid gap
   to either side so neighboring segments join into one continuous line
   per row. Hover pulls the poster up off the shelf. */
.media-card,
.season-card {
  display: flex;
  flex-direction: column;
  color: inherit;
}

.poster-wrap { position: relative; }

.poster {
  width: 100%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  display: block;
  background: var(--bg-elevated);
  border-radius: 2px 2px 0 0;
  box-shadow: 0 10px 12px -10px rgba(0, 0, 0, 0.85);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
/* The shadow deepens as the poster lifts, so it reads as coming off the
   shelf rather than sliding up the wall. */
.media-card:hover .poster,
.season-card:hover .poster {
  transform: translateY(-6px);
  box-shadow: 0 18px 20px -12px rgba(0, 0, 0, 0.9);
}
.media-card:hover .card-title,
.season-card:hover .card-title { color: var(--accent); }
.poster.placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  padding: 0.75rem;
  text-align: center;
  font-size: 0.82rem;
}

.kind-badge {
  position: absolute;
  top: 0.45rem;
  right: 0.45rem;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.2rem 0.4rem;
  border-radius: 2px;
  background: rgba(10, 24, 56, 0.9);
  border: 1px solid var(--border);
}
.kind-badge.kind-tv { color: var(--success); border-color: rgba(139, 228, 168, 0.4); }
.kind-badge.kind-movie { color: var(--accent); border-color: rgba(255, 204, 0, 0.4); }
/* The home shelves and detail-page suggestion rows are already typed by
   their headings ("Popular movies", a movie's "If you liked this..."), so
   the per-poster MOVIE/TV badge only earns its keep in mixed grids (search
   results, filmography). CSS-scoped hiding, same pattern as .hide-ratings. */
.discovery-grid .kind-badge,
.suggestions-grid .kind-badge { display: none; }

.card-body {
  margin: 0 -0.5rem;
  padding: 0.5rem 0.5rem 0;
  border-top: 4px solid var(--shelf);
  box-shadow: inset 0 7px 7px -7px rgba(0, 0, 0, 0.55);
}
.card-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.25;
  transition: color 0.15s ease;
  display: -webkit-box;
  line-clamp: 2;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.card-meta {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.25rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
}
.card-meta .rating { color: var(--rating); }

/* "Hide ratings" setting: the detail-page score chips are dropped server-side
   (no fetch), but the TMDB ★ on cards is static markup, so the body class
   hides it here. Scoped to .rating so the cast strip and chip-muted meta
   (year, episode count) are untouched. */
.hide-ratings .rating { display: none; }

/* ---------- discovery dashboard -------------------------------------- */

/* Discovery rows are open shelves separated by air, not glassy panels. */
.discovery-board {
  display: grid;
  gap: 2.25rem;
  margin-top: 0.5rem;
}

/* Grid items default to min-width:auto, which would size each section to
   the full un-scrolled width of its poster row and push it past the page
   edge; min-width:0 lets the inner row overflow and scroll instead. */
.discovery-shelf { min-width: 0; }

.discovery-heading {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.6rem;
}

/* The scroll rows pad by exactly the card-body bleed (0.5rem) so the
   shelf line runs to the container edge without being clipped, and pad
   the top so the hover lift isn't cut off by the scroll container. */
/* Standard scrollbar properties only: unlike ::-webkit-scrollbar styling,
   they keep macOS overlay behavior, so the bar appears only mid-scroll
   (the clipped poster at the row's edge is the resting affordance). */
.discovery-grid,
.suggestions-grid {
  display: flex;
  gap: 0.85rem;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: x proximity;
  padding: 0.5rem 0.5rem 0.85rem;
  margin: -0.5rem -0.5rem 0;
  scrollbar-width: thin;
  scrollbar-color: rgba(108, 131, 179, 0.45) transparent;
}

.discovery-grid .media-card {
  flex: 0 0 155px;
  scroll-snap-align: start;
}

/* Scrollable shelves hint that they continue: the shelf-marker script in the
   shared head tags a scroller with shelf-fade-start/-end while content
   extends past that edge, and the mask feathers the clipped side. Without
   JS no class is set and the shelf renders at full opacity, as before. */
.shelf-fade-end {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 3.5rem), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 3.5rem), transparent);
}
.shelf-fade-start {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 3.5rem);
  mask-image: linear-gradient(to right, transparent, #000 3.5rem);
}
.shelf-fade-start.shelf-fade-end {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 3.5rem, #000 calc(100% - 3.5rem), transparent);
  mask-image: linear-gradient(to right, transparent, #000 3.5rem, #000 calc(100% - 3.5rem), transparent);
}

/* Two-row variant: cards flow top-to-bottom into two rows, then advance
   columns rightward — a deep shelf scrolls half as far as a single row. */
.discovery-grid--two-row {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(2, auto);
  grid-auto-columns: 155px;
  align-items: start;
}

.discovery-grid--two-row .media-card {
  flex: none;
  width: 155px;
}


/* ---------- similar movie shelf -------------------------------------- */

.releases-section { margin-bottom: 1.75rem; }

.suggestions-section {
  margin-top: 1.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

.suggestions-section .section-meta { margin: 0; }

/* Shelf layout is shared with .discovery-grid above; cards size
   intrinsically so text-metric changes can never clip them. */
.suggestions-grid .media-card {
  flex: 0 0 155px;
  scroll-snap-align: start;
}

.suggestion-skeleton-card {
  pointer-events: none;
}

.suggestion-skeleton-poster,
.suggestion-skeleton-line {
  border-radius: 2px;
  background: rgba(108, 131, 179, 0.16);
}

/* Slab heights come from the host .card-title/.card-meta line boxes (via
   the &nbsp; struts in the markup), never from hand-tuned values — that
   keeps skeleton and loaded cards the same height. Only widths here. */
.suggestion-skeleton-title { width: 82%; }
.suggestion-skeleton-meta { width: 42%; }

.empty-compact {
  padding-block: 1.4rem;
}

/* ---------- media hero (movie / tv) --------------------------------- */

.media-hero {
  position: relative;
  border-radius: var(--radius-lg);
  margin-bottom: 2rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  overflow: hidden;
}
/* Scrim over the backdrop: heavy enough on the left for the title/overview
   to stay readable, light on the right so the artwork actually shows, plus
   a bottom fade so the hero settles into the cast band underneath. */
.media-hero.has-backdrop::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to bottom, rgba(10, 24, 56, 0) 55%, rgba(10, 24, 56, 0.75) 100%),
    linear-gradient(105deg, rgba(10, 24, 56, 0.88) 30%, rgba(10, 24, 56, 0.42) 100%),
    var(--hero-bg);
  background-size: cover;
  background-position: center top;
}
.media-hero-inner {
  position: relative;
  display: flex;
  gap: 1.5rem;
  padding: 1.5rem;
  align-items: flex-start;
}
.media-poster {
  width: 230px;
  flex: none;
  border-radius: var(--radius);
  aspect-ratio: 2 / 3;
  object-fit: cover;
  box-shadow: var(--shadow);
  background: var(--bg-elevated);
}
.media-poster.placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  font-size: 0.85rem;
  text-align: center;
  padding: 0.5rem;
}
.media-hero-body { flex: 1; min-width: 0; }
.media-kind {
  margin: 0 0 0.25rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--accent);
}
.media-title {
  margin: 0 0 0.6rem;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 4vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.12;
}
.media-title .year { font-weight: 400; color: var(--text-muted); font-size: 0.85em; }
.media-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  margin-bottom: 0.75rem;
}
/* Everything on the hero meta row shares the score chips' height so the
   runtime chip and external-id pills don't read as a ragged second tier. */
.media-meta .chip,
.media-meta .pill-link {
  min-height: 1.85rem;
}
.pill-link {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.25rem 0.5rem;
  border-radius: 2px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--accent);
}
.pill-link:hover { border-color: var(--accent); background: var(--bg-hover); }
.media-overview {
  margin: 0;
  color: #d4dbe9;
  max-width: 65ch;
  font-size: 0.95rem;
}

/* ---------- hero cast strip (movie) --------------------------------- */

/* "Top cast" band flush on the bottom of the movie hero. It sits above the
   backdrop pseudo (position: relative) with its own darkened fill + top
   hairline so it reads as a continuation of the header. Round headshots
   flow through the /img cache like every other TMDB image; cast with no
   headshot fall back to an initials avatar. */
.hero-cast {
  position: relative;
  border-top: 1px solid var(--border);
  background: rgba(8, 17, 40, 0.55);
  padding: 0.9rem 1.5rem 1rem;
}
.hero-cast-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.75rem;
}
.hero-cast-title {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.hero-cast-link {
  font-size: 0.78rem;
  color: var(--accent);
  white-space: nowrap;
}
.cast-row {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  padding-bottom: 0.3rem;
  scrollbar-width: thin;
}
.cast-card {
  flex: none;
  width: 92px;
  text-align: center;
}
.cast-photo {
  display: block;
  width: 66px;
  height: 66px;
  border-radius: 50%;
  margin: 0 auto 0.45rem;
  object-fit: cover;
  object-position: center top;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
}
.cast-photo.placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--text-muted);
}
.cast-name {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text);
  line-height: 1.2;
  /* Reserve exactly two lines so 1- and 2-line names occupy the same
     height: the async cast then swaps into the skeleton without shifting
     the rest of the page (names past two lines clamp with an ellipsis). */
  height: 2.4em;
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}
.cast-char {
  margin-top: 0.12rem;
  font-size: 0.74rem;
  color: var(--text-dim);
  line-height: 1.2;
  /* One fixed line (ellipsis past it) — same no-shift reason as the name. */
  height: 1.2em;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* A cast card linking to the person's in-app filmography page: keep the
   link invisible (inherit colour, no underline) and lift the name + photo
   border on hover so it reads as tappable. */
.cast-card-link {
  display: block;
  text-decoration: none;
  color: inherit;
  border-radius: var(--radius-sm);
}
.cast-card-link:hover .cast-name { color: var(--accent); }
.cast-card-link:hover .cast-photo { border-color: var(--accent); }

/* ---------- person / filmography page -------------------------------- */

/* TMDB biographies run to many paragraphs; clamp the hero to a readable
   teaser instead of pushing the filmography below the fold. */
.person-bio {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 6;
  line-clamp: 6;
}
.filmography-section { margin-top: 2rem; }
/* The character line on a filmography card — same quiet tone as the
   cast strip's role line, clipped to the card width. */
.credit-role {
  color: var(--text-dim);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.rt-review-section {
  --rt-review-card-height: 12rem;
  margin: -0.5rem 0 2rem;
}
.rt-review-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.85rem;
}
.rt-review-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  height: var(--rt-review-card-height);
  overflow: hidden;
  padding: 0.95rem;
  border-radius: var(--radius);
  background: var(--bg-card);
  border: 1px solid var(--border);
}
.rt-review-card.rt-review-card-skeleton::before {
  background: rgba(108, 131, 179, 0.25);
}
.rt-skeleton-line-quote {
  width: 100%;
  height: 0.82rem;
  border-radius: 0.35rem;
}
.rt-review-meta-skeleton {
  width: 100%;
}
.rt-skeleton-line-source {
  flex: 1 1 auto;
  height: 0.72rem;
  max-width: 8rem;
}
.rt-skeleton-line-mark {
  width: 1.5rem;
  height: 1.15rem;
  border-radius: 0.4rem;
}
.rt-skeleton-line-link {
  width: 4.6rem;
  height: 0.72rem;
}
.rt-review-source-skeleton .rt-skeleton-line-link {
  margin-top: 0.25rem;
}
.rt-review-card::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  border-radius: var(--radius) 0 0 var(--radius);
  background: var(--text-dim);
}
.rt-review-card.is-fresh::before { background: #e53935; }
.rt-review-card.is-rotten::before { background: #6c83b3; }
.rt-review-quote {
  margin: 0;
  padding-bottom: 0.15em;
  color: var(--text);
  font-size: 0.9rem;
  font-style: italic;
  line-height: 1.45;
  display: -webkit-box;
  line-clamp: 4;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.rt-review-meta {
  margin-top: auto;
  padding-right: 2.25rem;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.75rem;
}
.rt-review-source {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  color: var(--text-muted);
  font-size: 0.78rem;
  min-width: 0;
}
.rt-review-source strong {
  color: var(--text);
  font-size: 0.82rem;
}
.rt-review-mark {
  position: absolute;
  bottom: 0.95rem;
  right: 0.95rem;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.2rem;
}
.rt-review-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
}
.rt-review-sentiment-icon {
  width: 1rem;
  height: 1rem;
}
.rt-review-score {
  color: var(--text-dim);
  font-size: 0.72rem;
}
.rt-review-link {
  align-self: flex-start;
  margin-top: 0.25rem;
  color: var(--accent);
  font-size: 0.78rem;
  font-weight: 650;
}

@media (prefers-reduced-motion: reduce) {
  .poster,
  .media-card:hover .poster,
  .season-card:hover .poster {
    transition: none;
    transform: none;
    box-shadow: 0 10px 12px -10px rgba(0, 0, 0, 0.85);
  }
}

/* ---------- season header & layout ---------------------------------- */

.season-header {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  margin-bottom: 1.5rem;
}
.season-header-inner {
  display: flex;
  gap: 1.25rem;
  padding: 1.25rem;
  align-items: flex-start;
}
.season-poster {
  width: 130px;
  border-radius: var(--radius);
  aspect-ratio: 2 / 3;
  object-fit: cover;
  flex: none;
}
.season-header-text .media-kind a { color: var(--accent); }

.section-heading-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.episode-release-section { margin-top: 1.5rem; }

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

.episode-card {
  display: flex;
  gap: 0.85rem;
  padding: 0.65rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  align-items: flex-start;
}
.episode-still {
  width: 160px;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius-sm);
  flex: none;
  background: var(--bg-elevated);
}
.episode-still.placeholder { min-height: 90px; }
.episode-body { flex: 1; min-width: 0; }
.episode-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: baseline;
}
.episode-num {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-dim);
}
.episode-name { font-weight: 600; }
.episode-meta {
  display: flex;
  gap: 0.6rem;
  margin-top: 0.2rem;
  font-size: 0.82rem;
  color: var(--text-muted);
}
.episode-overview {
  margin: 0.35rem 0 0;
  font-size: 0.88rem;
  color: #cbd3e3;
}

.episode-release-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}
.episode-card-with-releases {
  padding: 0.85rem;
}
.episode-release-count {
  padding: 0.15rem 0.45rem;
  border-radius: 2px;
  font-family: var(--font-mono);
  background: rgba(255, 204, 0, 0.1);
  border: 1px solid rgba(255, 204, 0, 0.3);
  color: var(--accent);
  font-size: 0.7rem;
  white-space: nowrap;
}
.episode-release-results {
  margin-top: 0.75rem;
}
/* Count + on-demand "Search this episode" control above each episode's
   releases. The count moved out of the head so it (and the button) come
   back together when HTMX swaps in a deep-search result. */
.episode-release-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
/* Keep the search control right-aligned whether or not a count precedes it. */
.episode-search { margin: 0 0 0 auto; }
/* htmx in-flight: swap the button label for "Searching…". The form is the
   requesting element, so it carries the htmx-request class. */
.htmx-indicator { display: none; }
.episode-search.htmx-request .episode-search-label:not(.htmx-indicator) { display: none; }
.episode-search.htmx-request .htmx-indicator { display: inline; }

/* ---------- releases panel -------------------------------------------- */

.releases-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.5rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-bottom: 1rem;
}
.releases-summary .dot { color: var(--text-dim); }

/* Releases loading skeleton: quiet dim slabs, held still (no shimmer) and
   softly rounded — matching the RT/LB review and cast/suggestion skeletons.
   The structure mirrors a collapsed top-pick card so the swap doesn't jump. */
.release-skeleton-card,
.releases-loading {
  pointer-events: none;
}
.releases-skeleton-slab {
  display: inline-block;
  border-radius: 0.35rem;
  background: rgba(108, 131, 179, 0.2);
}
/* "Ghost" struts: representative text rendered invisibly inside the real
   .releases-summary / .release-title / .releases-group-head / .chip elements,
   so every line wraps to the same height as the loaded panel at any viewport
   width instead of guessing a fixed size. Scoped under .releases-loading so it
   reliably overrides each host element's own color/background regardless of
   source order; box-decoration-break paints the slab per wrapped line fragment. */
.releases-loading .releases-skeleton-ghost {
  color: transparent;
  background: rgba(108, 131, 179, 0.2);
  border-radius: 0.35rem;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
/* The remaining slabs stand in for width-independent, single-line controls,
   sized once to match their loaded counterparts (Top pick label, Score details
   line, Grab button, "Show more" pill, blocked-profile divider). */
.releases-skeleton-toplabel { width: 3.4rem; height: 0.6rem; }
.releases-skeleton-details { display: block; width: fit-content; margin-top: 0.35rem; font-size: 0.8rem; }
/* The grab placeholder reuses the real .btn-grab box (padding/border/font) so
   it has the exact width and height of the loaded button and doesn't shift the
   column when the real one swaps in; only its accent border is muted. */
.releases-skeleton-grab { border-color: transparent; }
.releases-skeleton-more { display: block; width: 9rem; height: 2rem; margin: 0.15rem 0 0.35rem; border-radius: var(--radius-sm); }
/* The blocked-profile divider is rebuilt from the real .releases-divider with
   ghost text so its hint wraps to the same height at narrow widths; hide the ▶
   marker's color but keep its width so wrapping still matches the loaded row. */
.releases-loading .releases-blocked > summary::before { color: transparent; }

.release-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 0.65rem;
  overflow: hidden;
}
.release-compact {
  background: var(--bg-elevated);
}
.release-compact .release-main {
  padding: 0.7rem 0.8rem;
}
.release-compact .release-score {
  font-size: 1rem;
}
.release-compact .release-score-block {
  min-width: 2.6rem;
}
.release-compact .release-title {
  font-size: 0.82rem;
}
.release-top-pick {
  border-color: rgba(255, 204, 0, 0.45);
  background: var(--bg-card);
  margin-bottom: 1.25rem;
}
.release-top-label {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--accent);
  padding: 0.5rem 1rem 0;
}
/* Compact top picks (the season page's per-episode/pack picks) align the
   label with the tighter compact padding and sit closer to the "Show
   more" disclosure below them. */
.release-compact .release-top-label { padding: 0.45rem 0.8rem 0; }
.release-compact.release-top-pick { margin-bottom: 0.8rem; }
.release-main {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 1rem;
  padding: 0.85rem 1rem 1rem;
  align-items: start;
}
/* A featured-alone row has no peers to compare against, so its raw score
   is just noise — it drops the score gutter and tucks the number into
   "Score details" instead. Every other (comparable) row keeps it. */
.release-main-solo { grid-template-columns: 1fr auto; }
.release-score-block { text-align: center; min-width: 3.5rem; }
.release-score {
  display: block;
  font-family: var(--font-mono);
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1;
}
.score-neg .release-score, .score-neg .cf-total-score { color: var(--score-neg); }
.score-low .release-score, .score-low .cf-total-score { color: var(--score-low); }
.score-mid .release-score, .score-mid .cf-total-score { color: var(--score-mid); }
.score-high .release-score, .score-high .cf-total-score { color: var(--score-high); }

/* Profile-blocked releases (TELESYNC under UHD Bluray + WEB, etc.) are
   sorted last and dimmed, while keeping the score's color band visible. */
.release-blocked {
  opacity: 0.55;
  background: rgba(17, 33, 74, 0.4);
}
.release-blocked:hover { opacity: 0.85; }
.release-blocked .release-title { color: var(--text-muted); text-decoration: line-through; text-decoration-color: rgba(255, 112, 102, 0.45); }
.release-blocked .release-score { filter: grayscale(0.6); }

.chip-blocked {
  background: rgba(255, 112, 102, 0.14);
  color: var(--score-neg);
  font-weight: 600;
  border: 1px solid rgba(255, 112, 102, 0.35);
}

/* Per-resolution section header for the grouped releases panel
   ("2160p", "1080p", …). Allowed-tier sections only; the blocked
   bucket keeps the dashed .releases-divider treatment below. */
.releases-group-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 1.15rem 0 0.6rem;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-weight: 700;
  color: var(--text);
}
.releases-group-label { color: var(--text); }
.releases-group-count {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--text-dim);
}
.releases-group-head .dot { color: var(--text-dim); }
/* Tighten the gap above the very first section so it sits close to
   the summary line / bucket title rather than floating. */
.releases-groups > .releases-group:first-child .releases-group-head { margin-top: 0.2rem; }

.releases-divider {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 1.1rem 0 0.65rem;
  padding: 0.4rem 0.65rem;
  border-top: 1px dashed var(--border);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--text-muted);
}
.releases-divider .dot { color: var(--text-dim); }
.releases-divider-hint {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  color: var(--text-dim);
}

/* Collapsed release lists — only the top pick shows; the rest sit behind
   a "Show N more" disclosure. Used on the movie page and on each
   per-episode / season-pack / other bucket on the season page. */
.releases-more { margin: 0.15rem 0 0.35rem; }
.releases-more > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  user-select: none;
  padding: 0.35rem 0.8rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.releases-more > summary::-webkit-details-marker { display: none; }
.releases-more > summary::before {
  content: "▼";
  font-size: 0.9em;
  line-height: 1;
}
.releases-more[open] > summary::before { content: "▲"; }
.releases-more > summary:hover {
  color: var(--accent);
  border-color: rgba(255, 204, 0, 0.4);
}
.releases-more[open] > summary .more-show,
.releases-more:not([open]) > summary .more-hide { display: none; }
.releases-more-body { margin-top: 0.8rem; }

/* The profile-blocked bucket doubles as a disclosure: the dashed divider
   is the clickable summary, and the rejected releases stay hidden (they
   never auto-download) until expanded. A leading triangle (matching the
   custom-format disclosure) marks it as expandable. */
.releases-blocked > summary {
  cursor: pointer;
  user-select: none;
  list-style: none;
  transition: color 0.15s ease;
}
.releases-blocked > summary::-webkit-details-marker { display: none; }
.releases-blocked > summary::before {
  content: "▶";
  font-size: 0.85em;
}
.releases-blocked[open] > summary::before { content: "▼"; }
.releases-blocked > summary:hover,
.releases-blocked > summary:hover::before { color: var(--text); }
.releases-blocked > summary:hover .releases-divider-hint { color: var(--text-muted); }
.releases-blocked-count {
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--text-dim);
}
.releases-blocked-body { margin-top: 0.6rem; }

/* "Nothing passes the profile" notice, shown in place of a top pick. */
.releases-empty {
  padding: 1rem 1.1rem;
  background: var(--bg-elevated);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  margin-bottom: 0.65rem;
}
.releases-empty.compact { padding: 0.7rem 0.85rem; }
.releases-empty .empty-title { font-size: 1rem; }
.releases-empty.compact .empty-title { font-size: 0.9rem; }
.releases-empty .empty-hint { font-size: 0.85rem; }

.release-title {
  margin: 0 0 0.4rem;
  font-size: 0.9rem;
  font-weight: 500;
  font-family: var(--font-mono);
  word-break: break-word;
  color: var(--text);
}
.release-top-pick .release-title { font-size: 0.95rem; }
.release-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.35rem; }
.chip-muted { background: var(--bg-elevated); }

.cf-details {
  margin-top: 0.35rem;
  font-size: 0.8rem;
}
.cf-details summary {
  cursor: pointer;
  color: var(--text-muted);
  user-select: none;
}
.cf-details summary:hover { color: var(--accent); }
.cf-body { margin-top: 0.45rem; }
.cf-total {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
.cf-total-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}
.cf-total-score {
  font-family: var(--font-mono);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  color: var(--text);
}
.cf-empty {
  margin: 0.4rem 0 0;
  font-size: 0.75rem;
  color: var(--text-dim);
}
.cf-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.65rem;
  margin-top: 0.4rem;
  padding-top: 0.35rem;
}
.cf-tag {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--text-dim);
}
.cf-tag.neg { color: var(--score-neg); }
.cf-tag .cfs { color: var(--text-muted); }
.cf-tag.neg .cfs { color: var(--score-neg); }

.release-action { align-self: center; }
.release-action form { margin: 0; }

/* Grab feedback */
.grab-result {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.1rem;
  padding: 0.35rem 0.55rem;
  border-radius: var(--radius-sm);
  font-size: 0.78rem;
  max-width: 10rem;
  text-align: right;
}
.grab-result.ok { color: var(--success); background: rgba(139, 228, 168, 0.12); }
.grab-result.err { color: var(--error); background: rgba(255, 112, 102, 0.12); }
.grab-label { font-weight: 600; text-transform: uppercase; font-size: 0.65rem; letter-spacing: 0.05em; }
.grab-msg { font-family: var(--font-mono); word-break: break-word; }

/* Live grab progress: the SSE listener fills the bar as NZBGet downloads. */
.grab-live { gap: 0.25rem; }
.grab-status { display: inline-flex; align-items: baseline; gap: 0.4rem; }
.grab-pct { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text); }
.grab-pct[hidden] { display: none; }
.grab-progress { display: block; width: 9rem; max-width: 100%; margin: 0.1rem 0; }
.grab-progress .dl-progress-fill { display: block; }
.grab-progress[hidden] { display: none; }

/* ---------- responsive ------------------------------------------------ */

@media (max-width: 640px) {
  .header-top {
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 0.75rem;
    padding: 0.65rem 0.85rem;
  }
  .brand { order: 1; gap: 0.5rem; }
  .brand-name { font-size: 1rem; }
  .header-search {
    order: 3;
    flex: 0 0 100%;
    width: 100%;
    max-width: none;
  }
  .header-search input[type="search"] { min-width: 0; }
  .header-actions {
    order: 2;
    margin-left: auto;
    gap: 0.4rem;
  }
  /* Four icon buttons must share the top row with the brand on a ~360px
     screen, so trim them down from the desktop 2.4rem hit area. */
  .header-icon-btn { width: 2.1rem; height: 2.1rem; }
  .header-icon-btn svg { width: 18px; height: 18px; }
  .media-hero-inner {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }
  .media-poster { width: 160px; }
  .media-meta { justify-content: center; }
  .release-main {
    grid-template-columns: 1fr;
    gap: 0.65rem;
  }
  .release-score-block { text-align: left; }
  .release-action { justify-self: start; }
  .episode-still { width: 100px; }
  .season-header-inner { flex-direction: column; align-items: center; text-align: center; }
}

@media (max-width: 480px) {
  .page-main { padding: 1rem 0.85rem 2rem; }
  .header-search input[type="search"] {
    min-width: 0;
  }
  .media-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }
}

/* Below ~400px even the slimmed-down row can't hold the wordmark plus four
   icon buttons, so keep just the ticket mark as the home link. */
@media (max-width: 400px) {
  .brand-text { display: none; }
}

/* ---------- settings -------------------------------------------------- */

.header-icon-btn {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--text-muted);
  cursor: pointer;
}
.header-icon-btn:hover { color: var(--text); border-color: var(--accent); background: var(--bg-hover); }
.header-icon-btn:disabled { opacity: 0.65; cursor: wait; }

/* Download count badge that rides the top-right corner of the nav icon. */
.header-badge {
  position: absolute;
  top: -0.45rem;
  right: -0.45rem;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.3rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  border: 2px solid var(--bg);
  background: var(--accent);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
/* display:inline-flex would otherwise defeat the hidden attribute. */
.header-badge[hidden] { display: none; }

.banner-ok {
  background: rgba(139, 228, 168, 0.1);
  border: 1px solid rgba(139, 228, 168, 0.35);
  color: #c4f3d8;
}
.settings-main { max-width: 760px; }
.settings-hero { margin-bottom: 1.25rem; }

/* First-run setup wizard (/setup). Reuses the settings form styles; the
   stepper chrome below is revealed by JS (no-JS just stacks the steps). */
.setup-main { max-width: 640px; }
.setup-progress {
  display: flex;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: setup-step;
}
.setup-progress li {
  counter-increment: setup-step;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  padding: 0.3rem 0.75rem 0.3rem 0.3rem;
}
.setup-progress li::before {
  content: counter(setup-step);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  font-size: 0.78rem;
}
.setup-progress li.on { color: var(--text); }
.setup-progress li.on::before {
  background: var(--accent);
  border-color: var(--accent);
  color: #0a1838;
}
.setup-actions { justify-content: flex-end; }
.setup-actions #setup-back { margin-right: auto; }
/* .btn sets display, which beats the UA [hidden] rule — restore it so the
   stepper can hide the buttons that don't apply to the current step. */
.setup-actions .btn[hidden] { display: none; }
.setup-alt-links {
  text-align: center;
  font-size: 0.85rem;
  color: var(--text-dim);
  margin: 0 0 1.5rem;
}
.setup-alt-links .dot { margin: 0 0.4rem; }
.setup-suggest {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: -0.25rem 0 0.5rem;
}
.setup-suggest-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.setup-chip {
  font-size: 0.8rem;
  padding: 0.25rem 0.7rem;
}

.settings-tabs {
  display: flex;
  gap: 0.15rem;
  overflow-x: auto;
  border-bottom: 1px solid var(--border);
  scrollbar-width: thin;
}
.settings-tab {
  flex: 0 0 auto;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  margin-bottom: -1px; /* sit the active underline on the tablist border */
  padding: 0.6rem 0.95rem;
  color: var(--text-muted);
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
}
.settings-tab:hover { color: var(--text); }
.settings-tab[aria-selected="true"] {
  color: var(--accent-hover);
  border-bottom-color: var(--accent);
}
.settings-tab:focus-visible { outline: 2px solid var(--border-focus); outline-offset: -2px; }

/* The panel's own display:grid would otherwise beat the [hidden] UA rule,
   so make the hidden tab panels explicitly disappear. */
.settings-group[hidden] { display: none; }

.settings-form {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.settings-group {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  padding: 1.1rem 1.25rem 1.25rem;
  margin: 0;
  display: grid;
  gap: 0.9rem;
}
.settings-group legend {
  padding: 0 0.5rem;
  margin-left: -0.25rem;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--text);
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.field-label {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.field input,
.field select,
.field textarea {
  width: 100%;
  background: var(--bg-card);
}
.field textarea {
  font: inherit;
  resize: vertical;
  min-height: 3.2rem;
}
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--border-focus);
}
.field input[readonly] { color: var(--text-muted); cursor: not-allowed; }
.field-hint { font-size: 0.78rem; color: var(--text-dim); line-height: 1.4; }
/* Small group label inside a panel (e.g. "Indexer" / "Metadata" under Sources). */
.field-subhead {
  margin: 0;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--text-dim);
}
.field-subhead + .field { margin-top: -0.2rem; }
.field-hint code, .field code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  color: var(--text-muted);
  background: var(--bg-card);
  padding: 0.05rem 0.3rem;
  border-radius: 4px;
}
.field-tag {
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.05rem 0.45rem;
}
.req {
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--warn);
}
.checkbox-field {
  flex-direction: row;
  align-items: center;
  gap: 0.55rem;
}
.checkbox-field input[type="checkbox"] {
  width: auto;
  flex: 0 0 auto;
  accent-color: var(--accent);
}
.checkbox-field .field-label { color: var(--text); }

/* ---------- indexer list (Sources tab) ------------------------------- */
/* Each indexer is a row of URL + API key fields with a Remove button; the
   "Add indexer" button clones the last row. Several indexers can be
   configured and every search queries all of them. */
.indexer-list {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.indexer-row {
  display: flex;
  gap: 0.6rem;
  align-items: flex-end;
  padding: 0.7rem;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.indexer-row .field {
  flex: 1 1 0;
  margin: 0;
}
.indexer-remove {
  flex: 0 0 auto;
  white-space: nowrap;
}
.indexer-actions {
  display: flex;
  justify-content: flex-start;
}
@media (max-width: 560px) {
  .indexer-row {
    flex-direction: column;
    align-items: stretch;
  }
  .indexer-remove {
    align-self: flex-end;
  }
}

/* ---------- scoring profile radio list -------------------------------- */
.scoring-intro {
  margin: 0;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--text-muted);
}
.profile-list {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.profile-option {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  cursor: pointer;
}
.profile-option:hover { background: var(--bg-hover); }
.profile-option input[type="radio"] {
  width: auto;
  flex: 0 0 auto;
  margin-top: 0.15rem;
  accent-color: var(--accent);
}
.profile-option:has(input:checked) {
  border-color: var(--accent);
  background: var(--bg-hover);
}
.profile-option-body {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}
.profile-name { font-weight: 500; font-size: 0.9rem; color: var(--text); }
.profile-explain { font-size: 0.78rem; line-height: 1.35; color: var(--text-dim); }
.profile-favors { margin-top: 0.15rem; font-size: 0.72rem; line-height: 1.35; color: var(--text-dim); }

.profile-more > summary {
  cursor: pointer;
  padding: 0.45rem 0.2rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  user-select: none;
}
.profile-more > summary:hover { color: var(--text); }
.profile-more[open] > summary { margin-bottom: 0.4rem; }

/* Save bar sticks to the bottom of the viewport so it's reachable from
   anywhere in the (long) form — no scrolling to the end to find Save. */
.settings-actions {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  gap: 0.75rem;
  align-items: center;
  padding: 0.9rem 0;
  background: var(--bg);
  border-top: 1px solid var(--border);
}

.test-field {
  flex-direction: row;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.btn-test { flex: 0 0 auto; }
.btn.htmx-request { opacity: 0.6; pointer-events: none; }
.test-result {
  font-size: 0.85rem;
  line-height: 1.4;
}
.test-result.ok { color: var(--success); }
.test-result.err { color: var(--error); word-break: break-word; }

.update-status { display: flex; flex-direction: column; gap: 0.5rem; }
.update-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; }
.update-spinner {
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-right: 0.45em;
  vertical-align: -0.1em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: update-spin 0.7s linear infinite;
}
@keyframes update-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .update-spinner { animation-duration: 2.4s; }
}

.settings-invite-field {
  max-width: 46rem;
}

.settings-invite-row {
  display: flex;
  gap: 0.65rem;
  align-items: center;
  flex-wrap: wrap;
}

.settings-invite-row .btn {
  flex: 0 0 auto;
}

.settings-invite-row input {
  flex: 1 1 24rem;
  min-width: 16rem;
}

.settings-users-wrap {
  max-width: 100%;
  overflow-x: auto;
  margin-bottom: 1.2rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.settings-users-table {
  width: 100%;
  min-width: 620px;
  border-collapse: collapse;
  font-size: 0.92rem;
}

.settings-users-table th,
.settings-users-table td {
  padding: 0.7rem 0.8rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
  /* break-word (not anywhere) so a long word can wrap, but a column is
     never shrunk below its longest word — which is what let Last Location
     collapse to one character per line. */
  overflow-wrap: break-word;
}

.settings-users-table thead th {
  color: var(--text-muted);
  font-weight: 700;
  background: var(--bg-elevated);
}

/* Last IP: a long IPv6 has no natural break points, so the old nowrap made
   it sit on one ~306px line, hogging table width and starving the columns
   beside it. overflow-wrap: anywhere lets it wrap inside its own column (and
   shrinks its min-content), so it stays one line when there's room and folds
   gracefully when there isn't, instead of forcing the rest to collapse. */
.settings-users-table th:nth-child(4),
.settings-users-table td:nth-child(4) {
  min-width: 7.5rem;
  overflow-wrap: anywhere;
}

.settings-users-table th:last-child,
.settings-users-table td:last-child {
  width: 1%;
  white-space: nowrap;
}

.settings-users-table tbody tr:last-child th,
.settings-users-table tbody tr:last-child td {
  border-bottom: 0;
}

.settings-user-name,
.settings-user-subname {
  display: block;
}

.settings-user-subname {
  margin-top: 0.15rem;
  color: var(--text-muted);
  font-size: 0.84rem;
  font-weight: 500;
}

/* ---------- passkey setup --------------------------------------------- */

.auth-main {
  max-width: 760px;
}

.auth-hero {
  margin-bottom: 1.25rem;
}

.auth-panel {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  padding: 1.1rem 1.25rem 1.25rem;
}

.auth-message {
  margin: 0 0 1rem;
  color: var(--text-muted);
}

.auth-actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  align-items: center;
}

.auth-invite {
  display: flex;
  gap: 0.65rem;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.auth-invite-link {
  flex: 1 1 22rem;
  min-width: 16rem;
  max-width: 100%;
  height: 2.35rem;
  padding: 0 0.75rem;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
}

/* ---------- downloads page ------------------------------------------- */

.downloads-main { max-width: var(--max-width); margin: 0 auto; padding: 1.5rem 1.25rem 3rem; }
.downloads-hero { margin-bottom: 1.25rem; }

/* Overall status strip */
.dl-overall {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem;
  padding: 0.9rem 1.1rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 1.25rem;
}
.dl-stat { display: flex; flex-direction: column; gap: 0.15rem; }
.dl-stat-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); }
.dl-stat-value { font-size: 1.05rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.dl-overall-actions { margin-left: auto; display: flex; align-items: center; gap: 0.6rem; }

/* Queue */
.dl-queue { list-style: none; display: flex; flex-direction: column; gap: 0.75rem; margin: 0 0 1.75rem; padding: 0; }
.dl-item {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
}
.dl-item-head { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.6rem; }
.dl-item-name {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dl-progress {
  height: 8px;
  border-radius: 999px;
  background: var(--bg-elevated);
  overflow: hidden;
  border: 1px solid var(--border);
}
.dl-progress-fill { height: 100%; background: var(--accent); border-radius: 999px; transition: width 0.4s ease; }
.dl-fill-active { background: var(--accent); }
.dl-fill-paused { background: var(--text-dim); }
.dl-fill-pp { background: var(--warn); }
.dl-fill-queued { background: var(--text-muted); }

/* Indeterminate progress: a phase with no measurable sub-progress (unpacking)
   sweeps a segment across the track instead of freezing at 100%. The parent
   .dl-progress clips it (overflow: hidden). */
.dl-progress-indeterminate .dl-progress-fill {
  width: 40%;
  animation: dl-progress-sweep 1.15s ease-in-out infinite;
  transition: none;
}
@keyframes dl-progress-sweep {
  0% { margin-left: -42%; }
  100% { margin-left: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .dl-progress-indeterminate .dl-progress-fill {
    width: 100%;
    margin-left: 0;
    animation: none;
    opacity: 0.7;
  }
}

.dl-item-meta {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-top: 0.55rem;
  font-size: 0.82rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.dl-pct { font-weight: 600; color: var(--text); }
.dl-item-actions { margin-left: auto; display: flex; gap: 0.4rem; }
/* Status chips */
.dl-chip {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.18rem 0.5rem;
  border-radius: 2px;
  border: 1px solid var(--border);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.dl-chip-active { color: var(--accent); border-color: rgba(255, 204, 0, 0.45); background: rgba(255, 204, 0, 0.1); }
.dl-chip-paused { color: var(--text-muted); }
.dl-chip-queued { color: var(--text-muted); }
.dl-chip-pp { color: #ffd599; border-color: rgba(255, 154, 60, 0.4); background: rgba(255, 154, 60, 0.1); }
.dl-chip-ok { color: var(--success); border-color: rgba(139, 228, 168, 0.4); background: rgba(139, 228, 168, 0.1); }
.dl-chip-err { color: var(--error); border-color: rgba(255, 112, 102, 0.4); background: rgba(255, 112, 102, 0.1); }
.dl-chip-warn { color: var(--warn); border-color: rgba(255, 154, 60, 0.4); background: rgba(255, 154, 60, 0.1); }
.dl-chip-muted { color: var(--text-dim); }

/* History */
.dl-history-head { margin-bottom: 0.75rem; }
.dl-history { list-style: none; display: flex; flex-direction: column; gap: 0.4rem; margin: 0; padding: 0; }
.dl-hist-item {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.55rem 0.85rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
}
.dl-hist-name {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dl-cat {
  flex: 0 0 auto;
  font-size: 0.72rem;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.1rem 0.45rem;
}
.dl-size { flex: 0 0 auto; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.dl-age { flex: 0 0 auto; color: var(--text-dim); font-variant-numeric: tabular-nums; min-width: 2.5rem; text-align: right; }
.dl-empty {
  padding: 1.25rem;
  text-align: center;
  color: var(--text-muted);
  background: var(--bg-card);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  margin: 0 0 1.75rem;
}

/* ---------- parked domain page --------------------------------------- */

body.parked-page {
  min-height: 100vh;
  background: #fff;
  color: #333;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 12px;
  line-height: 1.16;
}

.parked-page a {
  color: inherit;
}

.parked-shell {
  width: 980px;
  min-height: 100vh;
  margin: 0 auto;
  background: #fff;
}

.parked-masthead {
  height: 65px;
  background: #242624;
  padding: 0;
}

.parked-masthead h1 {
  margin: 0;
  padding: 20px 0 0 15px;
  color: #fff;
  font-size: 20px;
  line-height: normal;
  font-weight: 700;
}

.parked-layout {
  display: grid;
  grid-template-columns: 200px 1fr;
  column-gap: 15px;
  height: 718px;
}

.parked-nav {
  width: 200px;
  padding-top: 5px;
}

.parked-nav a {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  width: 200px;
  height: 26px;
  margin-bottom: 4px;
  padding: 6px 8px 6px 10px;
  background: #84aa1c;
  color: #fff;
  font-size: 12px;
  line-height: normal;
  font-weight: 400;
  text-decoration: none;
}

.parked-nav a:hover {
  background: #779818;
  color: #fff;
}

.parked-main {
  width: 750px;
  min-width: 0;
  padding-top: 112px;
  padding-bottom: 0;
}

.parked-copy {
  width: 520px;
}

.parked-copy h2 {
  margin: 0 0 24px;
  color: #070707;
  font-size: 16px;
  line-height: 1;
  font-weight: 700;
}

.parked-copy p {
  margin: 0 0 14px;
  color: #333;
  font-size: 12px;
  line-height: 1.2;
}

.parked-copy ul {
  margin: 0 0 0 28px;
  padding-left: 18px;
}

.parked-copy li {
  color: #333;
  font-size: 12px;
  line-height: 1.08;
}

.parked-domain-search {
  position: relative;
  width: 700px;
  height: 78px;
  margin: 107px auto 0;
  padding: 0;
}

.parked-domain-search::before {
  content: "";
  position: absolute;
  inset: 0 auto auto 0;
  width: 660px;
  height: 78px;
  border-radius: 5px;
  background:
    linear-gradient(21deg, transparent 0 40%, rgba(235, 143, 44, 0.74) 41% 62%, transparent 63%),
    linear-gradient(90deg, #ffd64f 0%, #f8b343 48%, #ffde67 100%);
  overflow: hidden;
}

.parked-search-label {
  position: absolute;
  left: 25px;
  top: 14px;
  color: #fff;
  font-size: 20px;
  line-height: 0.84;
  font-weight: 700;
  text-shadow: 0 1px 3px rgba(139, 91, 9, 0.22);
  z-index: 1;
}

.parked-domain-search input {
  position: absolute;
  left: 112px;
  top: 12px;
  width: 426px;
  height: 32px;
  border: 0;
  border-radius: 0;
  background: #fff;
  color: #111;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 18px;
  padding: 2px 5px 0;
  z-index: 1;
}

.parked-domain-search button {
  position: absolute;
  left: 545px;
  top: 12px;
  width: 72px;
  height: 36px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 6px;
  color: #fff;
  background: linear-gradient(#f38441, #e84f25);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35);
  font-family: Arial, Helvetica, sans-serif;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
  z-index: 1;
}

.parked-offer-copy {
  width: 520px;
}

.parked-offer-copy p {
  width: 520px;
  margin-bottom: 0;
}

.parked-offer-form {
  margin-top: 0;
}

.parked-field {
  display: grid;
  grid-template-columns: 100px 147px 18px;
  align-items: center;
  column-gap: 5px;
  height: 32px;
  margin-bottom: 0;
}

.parked-field label,
.parked-label {
  color: #333;
  font-size: 12px;
  line-height: normal;
  font-weight: 400;
  text-align: left;
  padding: 5px;
}

.parked-field input,
.parked-field textarea {
  border: 1px solid #d1d1d1;
  border-radius: 6px;
  background: #fff;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.04);
  color: #222;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 13.3333px;
  line-height: normal;
}

.parked-field input {
  width: 147px;
  height: 22px;
  padding: 1px 2px;
}

.parked-required {
  color: #bde9ff;
  font-size: 17px;
  line-height: 1;
  font-weight: 700;
}

.parked-field-area {
  grid-template-columns: 100px 296px;
  align-items: start;
  column-gap: 5px;
  height: 136px;
  margin-top: 0;
  margin-bottom: 0;
}

.parked-field-area textarea {
  width: 296px;
  height: 126px;
  margin-top: 5px;
  padding: 2px;
  resize: vertical;
}

.parked-field-validation {
  grid-template-columns: 100px 310px;
  column-gap: 5px;
  align-items: start;
  height: 88px;
  margin-bottom: 0;
}

.parked-captcha {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 310px;
  height: 78px;
  margin-top: 5px;
  padding: 0 10px 0 16px;
  border: 1px solid #d3d3d3;
  border-radius: 3px;
  background: #f9f9f9;
  box-shadow: none;
}

.parked-captcha-error {
  color: #f2241e;
  font-size: 14px;
  line-height: 1.16;
  font-weight: 400;
}

.parked-captcha-mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 68px;
  color: #666;
  align-self: flex-start;
  margin-top: 8px;
}

.parked-captcha-mark span {
  display: block;
  width: 32px;
  height: 32px;
  border-radius: 0;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath fill='%234d6ed3' d='M23.9 5.7l.6-3.4L31 9.1l-7.7 4.7.6-3.5c-3.6-2.4-8.7-1.9-11.8 1.3L8.7 8.2c4-4 10.3-5.1 15.2-2.5z'/%3E%3Cpath fill='%234d6ed3' d='M27.3 12.8c2.2 4.5 1.2 10.1-2.7 13.8l3.1 2.1-8.6 2-1.2-8.7 2.7 1.8c2.7-2.5 3.4-6.6 1.6-9.9l5.1-1.1z'/%3E%3Cpath fill='%238f969f' d='M19.5 27.1c-4.7 1.4-10 .1-13.2-3.7l-2.6 2.1.2-8.9 8.5 2.8-3 2.3c2.1 2.3 5.4 3.2 8.4 2.4l1.7 3z'/%3E%3Cpath fill='%238f969f' d='M7.2 20.5C4.8 16.1 5.5 10.5 9.1 6.7L6 4.8l8.5-2.4 1.6 8.6-2.9-1.7c-2.3 2.4-2.9 6-1.5 9l-4.5 2.2z'/%3E%3C/svg%3E") center / 32px 32px no-repeat;
}

.parked-captcha-mark small {
  font-size: 10px;
  line-height: 1;
}

.parked-submit-row {
  grid-template-columns: 100px 92px;
  column-gap: 5px;
  height: 32px;
  margin-top: 0;
  margin-bottom: 0;
}

.parked-submit-row button {
  width: 92px;
  height: 22px;
  margin-top: 5px;
  padding: 0;
  border: 1px solid #2e73dc;
  border-radius: 7px;
  color: #fff;
  background: linear-gradient(#4d91ff, #2d72e8);
  font-family: Arial, Helvetica, sans-serif;
  font-size: 12px;
  line-height: 1.05;
  font-weight: 500;
  cursor: pointer;
}

.parked-offer-copy + .parked-domain-search {
  margin-top: 27px;
}

.parked-footer {
  width: 980px;
  height: 70px;
  margin: 0 auto;
  padding: 0;
  background: #242624;
  color: #fff;
  font-size: 12px;
  line-height: 35px;
  text-align: center;
  font-weight: 400;
}

.parked-footer p {
  margin: 0;
}

.parked-footer p + p {
  margin-top: 0;
}

.parked-footer a {
  color: #fff;
  text-decoration: none;
}

.parked-footer a:hover {
  text-decoration: underline;
}

/* ---------- TV watchlist callout (monitor_callout) ------------------- */

/* The TV detail page's watchlist control sits above the Seasons grid. It
   reuses the movie CTA's info-card styles (.ew-card / .ew-wl-btn, below); this
   wrapper just spaces it off the grid and is the HTMX outerHTML swap target. */
.monitor-callout-wrap {
  margin-bottom: 1.5rem;
}

/* ---------- movie empty-state watchlist CTA (monitor_cta) ------------- */

/* The movie "Available copies" empty state is an info card: the situation as
   the title, a one-line conditional status, and an explicit watchlist button.
   The form is the card and its submit is the button, so clicking POSTs the
   toggle and swaps the fragment; the card + star tint gold once it's on. */
.ew-cta-form { margin: 0; }
.ew-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 1rem 1.1rem;
  background: var(--bg-card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
}
.ew-card.is-on { border-color: rgba(255, 204, 0, 0.45); background: rgba(255, 204, 0, 0.07); }
.ew-card-text { flex: 1 1 17rem; min-width: 0; }
.ew-card-title {
  display: block;
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 650;
  line-height: 1.2;
  color: var(--text);
}
.ew-card-sub {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--text-muted);
}
/* The watchlist action is the card's primary CTA, so off-state it's a loud
   gold (accent) button. On-state it relaxes to a gold outline — still clearly
   active and clickable to remove, but not a second solid fill fighting the
   card's gold tint. The star adornment fills gold once it's on. */
.ew-wl-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
  font-weight: 600;
}
.ew-wl-btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.ew-wl-btn.is-on {
  background: transparent;
  border-color: rgba(255, 204, 0, 0.55);
  color: var(--accent);
}
.ew-wl-btn.is-on:hover { background: rgba(255, 204, 0, 0.12); border-color: var(--accent); }
.ew-wl-star { width: 17px; height: 17px; flex: none; fill: none; stroke: currentColor; transition: fill 0.15s, stroke 0.15s; }
.ew-wl-btn.is-on .ew-wl-star { fill: var(--accent); stroke: var(--accent); }

/* ---------- wanted (watchlist) page ----------------------------------- */

.wanted-hero {
  margin-bottom: 1.25rem;
}

/* Auto-download status strip above the watchlist table. */
.ag-status {
  margin-bottom: 1.25rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
}

.ag-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2.25rem;
  margin: 0;
}

.ag-stat {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.ag-stat dt {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

.ag-stat dd {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
}

.ag-stat-warn dd {
  color: var(--rating, #f0b429);
}

.ag-warn {
  margin: 0.85rem 0 0;
  padding: 0.6rem 0.8rem;
  border: 1px solid rgba(255, 204, 0, 0.35);
  border-radius: var(--radius-sm);
  background: rgba(255, 204, 0, 0.1);
  font-size: 0.9rem;
}

.ag-warn a {
  color: inherit;
  text-decoration: underline;
}

.wanted-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.wanted-table {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
  font-size: 0.92rem;
}

.wanted-table th,
.wanted-table td {
  padding: 0.7rem 0.8rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  overflow-wrap: break-word;
}

.wanted-table thead th {
  color: var(--text-muted);
  font-weight: 700;
  background: var(--bg-elevated);
}

.wanted-table tbody tr:last-child th,
.wanted-table tbody tr:last-child td {
  border-bottom: 0;
}

.wanted-title .year {
  color: var(--text-muted);
  font-weight: 400;
  margin-left: 0.3rem;
}

.wanted-checked {
  color: var(--text-muted);
  white-space: nowrap;
}

.wanted-actions {
  width: 1%;
  white-space: nowrap;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
