/*
 * Rising Seasons — app styles.
 *
 * Loaded after assets/css/main.css so these rules win on conflicts. The
 * site chrome (#header / #footer / #menu) keeps its dark #181818 styling
 * from main.css; this file forces the dark IMDb-style theme on the body
 * background and normalizes every interactive control to the same height
 * + alignment so the toolbar reads as one row.
 */

:root {
  /* Tells the browser to use dark UI for native widgets — scrollbars,
     <select> popup menus, calendar pickers — so dropdowns aren't white. */
  color-scheme: dark;

  /* Cinematic dark palette. The base is a near-black with a slight blue
     hue; surfaces step up through warm dark greys for tactile depth. */
  --bg: #07090f;
  --bg-grad:
    radial-gradient(ellipse 90% 60% at 50% -10%, rgba(245, 197, 24, 0.08), transparent 60%),
    radial-gradient(ellipse 60% 40% at 10% 10%, rgba(245, 197, 24, 0.04), transparent 70%),
    radial-gradient(ellipse 50% 50% at 90% 30%, rgba(74, 222, 128, 0.025), transparent 70%),
    linear-gradient(180deg, #0a0c14 0%, #07090f 60%, #050609 100%);
  --surface: #12151d;
  --surface-2: #181c26;
  --surface-3: #232836;
  --surface-glass: rgba(24, 28, 38, 0.65);

  --text: #f1f3f8;
  --muted: #a4adbd;
  --muted-2: #6f7785;

  --accent: #f5c518;     /* IMDb yellow */
  --accent-warm: #ffd24a;
  --accent-ink: #1a1500; /* high-contrast text on yellow */
  --accent-soft: rgba(245, 197, 24, 0.12);
  --accent-strong: rgba(245, 197, 24, 0.32);
  --accent-glow: 0 0 0 1px rgba(245, 197, 24, 0.4), 0 0 18px rgba(245, 197, 24, 0.15);

  --good: #34d39e;
  --good-soft: rgba(52, 211, 158, 0.14);
  --good-ink: #04231a;

  --warn: #fb923c;
  --danger: #f87171;

  --border: #232838;
  --border-strong: #343a4f;

  --radius: 16px;
  --radius-sm: 10px;
  --radius-xs: 6px;
  --header-h: 3.25rem;

  /* Uniform interactive sizing — every button, input, and chip respects
     these so the filter toolbar lines up cleanly. */
  --ctrl-h: 40px;
  --ctrl-h-sm: 32px;
  --ctrl-radius: 10px;
  --ctrl-pad-x: 0.95rem;
  --ctrl-font: 0.9rem;

  --shadow-lift: 0 6px 22px -8px rgba(0, 0, 0, 0.55), 0 2px 4px rgba(0, 0, 0, 0.3);
  --shadow-modal: 0 30px 80px -10px rgba(0, 0, 0, 0.7), 0 8px 24px -4px rgba(0, 0, 0, 0.4);

  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);

  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
}

* { box-sizing: border-box; }

/* --- background hard-locked to dark theme ---
   Set on <html> as well as the scoped body class so even if a higher-specificity
   selector somewhere in main.css fights for body's background, the dark colour
   shows through from html. */
html {
  background: var(--bg);
  background-image: var(--bg-grad);
  background-attachment: fixed;
  background-color: var(--bg);
  min-height: 100%;
  /* Always show the vertical scrollbar so its gutter is permanently
     reserved. Without this, expanding the More-filters panel grows the
     page tall enough to surface the scrollbar, which steals ~15px from
     the viewport width and yanks the centered .page sideways. Using
     overflow-y:scroll instead of scrollbar-gutter:stable for universal
     browser support (Safari < 18 ignores scrollbar-gutter). */
  overflow-y: scroll;
  scrollbar-gutter: stable;
}

body.rising-seasons-app {
  background: transparent !important;
  color: var(--text);
  line-height: 1.5;
  min-height: 100vh;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  /* main.css adds padding-top: 3.25rem for the fixed header — keep that. */
}

/* Shared site texture: main.css already applies the faint bg.jpg overlay
   via body::before at 5% opacity. We keep it (matches every other app)
   and just guarantee it sits BEHIND our dark gradients so the dark theme
   stays dominant and the overlay reads as subtle grain on top of the
   indigo/green corner washes. */
body.rising-seasons-app::before {
  z-index: -1;
}

body.rising-seasons-app a {
  color: var(--accent);
  text-decoration: none;
}
body.rising-seasons-app a:hover { text-decoration: underline; }

body.rising-seasons-app h1,
body.rising-seasons-app h2,
body.rising-seasons-app h3,
body.rising-seasons-app h4 {
  color: var(--text);
  margin: 0;
  letter-spacing: -0.01em;
  text-transform: none;
  font-weight: 600;
  line-height: 1.25;
}

body.rising-seasons-app input,
body.rising-seasons-app select,
body.rising-seasons-app textarea,
body.rising-seasons-app button {
  font-family: inherit;
}

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

/* ---------- Buttons (single source of truth) ----------
   Every interactive control in the app uses one of: .btn, .shape-chip,
   .genre-chip, .watch-toggle. They share the same height tokens and use
   flex for true horizontal+vertical centering so text never drifts.

   IMPORTANT — main.css line ~322 has `button:hover { color: #ce1b28
   !important }` (the site's red link colour). To stop our buttons
   turning red on hover, every color rule below uses !important so the
   class-scoped colour wins. The hover declaration must also be
   !important — without it, only the default state survives. */

.btn[hidden] { display: none; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  height: var(--ctrl-h);
  padding: 0 var(--ctrl-pad-x);
  border: 1px solid var(--border);
  border-radius: var(--ctrl-radius);
  background: var(--surface);
  color: var(--text) !important;
  font: inherit;
  font-size: var(--ctrl-font);
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  box-shadow: none !important;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease), transform 0.1s var(--ease), filter 0.18s var(--ease);
}

.btn:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--text) !important;
  box-shadow: none !important;
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft), 0 0 0 1px var(--accent) !important;
}
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

/* Primary (yellow CTA) — high-contrast dark text on yellow */
.btn-primary,
.btn-primary:hover {
  background: linear-gradient(135deg, var(--accent-warm), var(--accent) 55%, #e0b00f);
  color: var(--accent-ink) !important;
  border-color: transparent;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.btn-primary:hover {
  filter: brightness(1.06);
  transform: translateY(-1px);
  box-shadow: 0 8px 20px -8px rgba(245, 197, 24, 0.45) !important;
}
.btn-primary:active { transform: translateY(0); }

/* Ghost (secondary action) — white text per the site's light-on-dark rule */
.btn-ghost,
.btn-ghost:hover {
  background: transparent;
  color: var(--text) !important;
  border-color: var(--border-strong);
}
.btn-ghost:hover { background: var(--surface-2); border-color: var(--border-strong); }

/* View-toggle buttons live inside a wrapper that draws the border, so they
   reset their own border to look like a single segmented control. */
.view-toggle {
  display: inline-flex;
  height: var(--ctrl-h);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
  border-radius: var(--ctrl-radius);
  overflow: hidden;
  align-self: stretch;
  padding: 3px;
  gap: 2px;
}
.view-btn,
.view-btn:hover {
  border: none;
  border-radius: 7px;
  background: transparent;
  color: var(--muted) !important;
  width: 2.4rem;
  height: 100%;
  font-size: 1rem;
  box-shadow: none !important;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}
.view-btn:hover { background: var(--surface-3); color: var(--text) !important; }
.view-btn[aria-pressed="true"],
.view-btn[aria-pressed="true"]:hover {
  background: var(--accent);
  color: var(--accent-ink) !important;
}

/* Modal close X — small circular glass button. Smaller than the
   surrounding controls (32 px) so it doesn't read as a primary action,
   uses backdrop-blur + a hairline border to feel like a deliberate
   floating overlay rather than a misplaced grey square. Icon sized in
   px and lined up with `line-height: 1` so the × is mathematically
   centred regardless of font metrics. */
.modal-close,
.modal-close:hover {
  /* 36 px circular icon button — comfortable touch target, anchored
     to the panel's top-right via absolute positioning below. */
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.85) !important;
  font-size: 0;
  line-height: 1;
  box-shadow:
    0 4px 14px rgba(0, 0, 0, 0.3),
    0 0 0 1px rgba(0, 0, 0, 0.35) inset !important;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease), transform 0.08s var(--ease),
              box-shadow 0.18s var(--ease);
}
.modal-close::before,
.modal-close::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
  transform-origin: center;
  transition: transform 0.2s var(--ease);
}
.modal-close::before { transform: translate(-50%, -50%) rotate(45deg); }
.modal-close::after  { transform: translate(-50%, -50%) rotate(-45deg); }
.modal-close:hover {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.3);
  color: #ffffff !important;
  box-shadow:
    0 4px 14px rgba(0, 0, 0, 0.35),
    0 0 0 1px rgba(255, 255, 255, 0.08),
    0 0 18px rgba(255, 255, 255, 0.12) !important;
}
.modal-close:active { transform: scale(0.92); }
.modal-close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Modal "Mark as watched" button — extends .btn (white text by default). */
.watch-btn.is-watched,
.watch-btn.is-watched:hover,
.compare-btn.is-in-compare,
.compare-btn.is-in-compare:hover {
  background: var(--good);
  color: var(--good-ink) !important;
  border-color: var(--good);
}
.watch-btn.is-watched:hover,
.compare-btn.is-in-compare:hover { filter: brightness(1.05); }

/* Card/row corner watched toggle — circular pill, smaller height */
.watch-toggle,
.watch-toggle:hover {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.4);
  background: rgba(0, 0, 0, 0.6);
  color: #ffffff !important;
  font-size: 0.95rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(4px);
  box-shadow: none !important;
  transition: background 0.15s, color 0.15s, transform 0.1s, border-color 0.15s;
  z-index: 2;
}
.watch-toggle:hover { transform: scale(1.08); background: rgba(0, 0, 0, 0.75); }
.watch-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.watch-toggle[aria-pressed="true"],
.watch-toggle[aria-pressed="true"]:hover {
  background: var(--good);
  color: var(--good-ink) !important;
  border-color: var(--good);
}

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

.page {
  /* Match the site footer's text container exactly — main.css `.inner` is
     `width: 75rem; max-width: calc(100% - 6rem)`, so we mirror that here.
     The page's content edges then line up with the footer text edges on
     every viewport size, and there's no horizontal shift when the body
     scrollbar appears (overflow-y: scroll on <html> keeps the gutter
     reserved at all times). */
  width: 75rem;
  max-width: calc(100% - 6rem);
  box-sizing: border-box;
  margin: 0 auto;
  padding: 0 0 3rem;
}

.hero {
  padding: 4.5rem 0 1.25rem;
  text-align: center;
  background: none !important;
  position: relative;
  isolation: isolate;
}

/* Soft amber glow behind the title — cinematic spotlight, low alpha so
   the edge fades into the page texture instead of forming a visible disc. */
.hero::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 35%;
  width: min(900px, 90%);
  height: 360px;
  transform: translate(-50%, -50%);
  background: radial-gradient(ellipse at center,
    rgba(245, 197, 24, 0.18) 0%,
    rgba(245, 197, 24, 0.08) 30%,
    rgba(245, 197, 24, 0) 70%);
  filter: blur(6px);
  pointer-events: none;
  z-index: -1;
}

.hero h1 {
  font-size: clamp(2.5rem, 6vw, 4.25rem);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.05;
  background: linear-gradient(135deg, #ffe89a 0%, #ffd35a 35%, var(--accent) 70%, #f5c518 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 2px 1px rgba(0, 0, 0, 0.35))
          drop-shadow(0 8px 32px rgba(245, 197, 24, 0.28));
}

.tagline {
  margin: 1rem auto 0;
  max-width: 38rem;
  color: var(--muted);
  font-size: 1.08rem;
  letter-spacing: 0;
  line-height: 1.5;
}
.tagline em {
  color: var(--text);
  font-style: normal;
  font-weight: 500;
  background: linear-gradient(135deg, var(--text) 0%, var(--accent-warm) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.hero-sep {
  margin: 0 0.5rem;
  color: var(--muted-2);
}

/* ---------- Shape chips ----------
   Editorial discovery chips: a real subtle surface that defines each
   pill, a visible-but-soft outline, full white name + readable
   description + tertiary count. Compact, uniform, intentional.
   Active state lifts to a soft yellow wash + accent border + accent
   text without resorting to a bright slab. */

.shapes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.45rem;
  justify-content: center;
  /* Hidden header text via aria-label is enough for screen readers;
     visual rhythm is intentionally label-less so the chips themselves
     read as the controls — fewer chrome elements above the fold. */
  padding: 1.4rem 0 0.6rem;
  max-width: 48rem;
  margin: 0 auto;
}

.shape-chip,
.shape-chip:hover {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  height: 30px;
  padding: 0 0.7rem 0 0.6rem;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 999px;
  color: #FFFFFF !important;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.005em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02) inset !important;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease), transform 0.18s var(--ease),
              box-shadow 0.18s var(--ease);
}
.shape-chip:hover {
  /* Hint of gold on hover so inactive chips foreshadow the active
     yellow state, unifying the interaction model. */
  background: rgba(245, 197, 24, 0.06);
  border-color: rgba(245, 197, 24, 0.32);
  color: #FFFFFF !important;
  transform: scale(1.03);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02) inset,
              0 4px 14px rgba(245, 197, 24, 0.1) !important;
}
.shape-chip:hover .shape-icon {
  opacity: 1;
}
.shape-chip:focus-visible {
  /* Same reasoning as .label-chip — inset focus ring so adjacent chips
     never share a visual frame. */
  outline: 2px solid rgba(245, 197, 24, 0.65);
  outline-offset: -2px;
}
.shape-chip[aria-pressed="true"],
.shape-chip[aria-pressed="true"]:hover {
  background: rgba(245, 197, 24, 0.12);
  border-color: rgba(245, 197, 24, 0.45);
  color: var(--accent) !important;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02) inset !important;
}

.shape-icon {
  color: var(--accent);
  font-weight: 700;
  font-size: 0.9rem;
  line-height: 1;
  opacity: 0.95;
}
.shape-name {
  font-weight: 600;
  color: #FFFFFF;
  /* Stronger than description so the pattern name reads first. */
}

/* Descriptions are kept in the DOM (a11y + content for the tooltip-from-text
   JS pass), but hidden visually at every viewport. They render via the
   chip's title attribute on hover instead, so the row stays calm and the
   teaching content is still one hover away. */
.shape-desc {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.shape-count {
  display: inline-flex;
  align-items: center;
  height: 17px;
  padding: 0 0.45rem;
  /* Tertiary badge — a real container so the number sits in a defined
     slot rather than floating loose, but quiet enough that the name
     still owns the chip. */
  background: rgba(255, 255, 255, 0.05);
  border-radius: 999px;
  font-size: 0.68rem;
  color: rgba(243, 244, 246, 0.72);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  margin-left: 0.1rem;
}
.shape-chip:hover .shape-count {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(243, 244, 246, 0.88);
}
.shape-chip[aria-pressed="true"] .shape-count {
  background: rgba(245, 197, 24, 0.18);
  color: var(--accent);
}

.shape-chip:disabled,
.shape-chip.is-disabled {
  /* Muted but still legible — 0.3 read as "broken element" on mobile
     next to brighter siblings. */
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}
.shape-chip.is-disabled .shape-count {
  /* Zero-count badges look mid-render on disabled chips. Drop the
     pill background so the disabled state reads as "no hits in this
     filter combo," not as a partial render. */
  background: transparent;
}

/* ---------- Filter toolbar ---------- */

.filters {
  padding: 1.5rem 0 0.5rem;
}

.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: stretch;
}

/* Primary row reads as a subtle platform under the controls — soft glass
   surface with a barely-there stroke, no decorative gold rim competing
   with the Surprise CTA, no heavy drop shadow framing it like a card.
   IMPORTANT: backdrop-filter creates a stacking context. The search
   dropdown (.search-suggestions) lives inside this row and can't escape
   that context. Lifting the whole row with position:relative + z-index
   guarantees the dropdown paints above .label-filters and .stats-bar
   below. The row has no visible overlap of its own — sibling content
   only appears once the dropdown is open. */
.filter-row.primary {
  position: relative;
  z-index: 50;
  isolation: isolate;
  /* Scoped control height — overrides the global 40 px just for this
     toolbar. The custom property cascades to every descendant that uses
     `var(--ctrl-h)` (search input, view-toggle rail, view buttons, the
     Surprise primary button), so they all shrink to 32 px in one shot
     while keeping the rest of the app (modal close, modal buttons, etc.)
     untouched. */
  --ctrl-h: 32px;
  align-items: center;
  background: rgba(18, 21, 29, 0.55);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.04);
  /* Top half of a unified two-row toolbar — rounded on top, squared off
     at the bottom, no bottom border. The filter row below extends the
     same surface and closes the shell. */
  border-radius: 12px 12px 0 0;
  border-bottom: 0;
  padding: 4px 6px;
  gap: 0.5rem;
}
/* Subtle vertical separator dividing the search ("find") cluster from
   the right-side ("view + discover") cluster. Without it the row reads
   as one indistinct strip of widgets — the divider helps the eye chunk
   the controls. Only shown at viewports wide enough that everything
   sits on a single line. */
@media (min-width: 760px) {
  /* 5-column grid: [left spacer 1fr] [search ≤600px] [right spacer 1fr]
     [view-toggle auto] [surprise-group auto]. Matching 1fr columns
     visually center the search; trailing auto columns keep the
     view/surprise controls pinned to the right edge. */
  .filter-row.primary {
    display: grid;
    grid-template-columns: 1fr minmax(0, 600px) 1fr auto auto auto;
  }
  .filter-row.primary .search-cluster { grid-column: 2; }
  .filter-row.primary .view-toggle {
    position: relative;
    grid-column: 4;
  }
  .filter-row.primary .surprise-group { grid-column: 5; }
  .filter-row.primary .shortcut-legend-btn { grid-column: 6; }
  .filter-row.primary .view-toggle::before {
    content: '';
    position: absolute;
    left: -0.35rem;
    top: 18%;
    bottom: 18%;
    width: 1px;
    background: rgba(255, 255, 255, 0.1);
  }
}
/* At narrow widths the row wraps. Lock the visual order so search is
   always first (primary task), view-toggle second, surprise actions
   last — regardless of which break point the row wraps at. */
@media (max-width: 759px) {
  .filter-row.primary .search-cluster { order: 1; flex-basis: 100%; }
  .filter-row.primary .view-toggle { order: 2; }
  .filter-row.primary .surprise-group { order: 3; margin-left: auto; }
  .filter-row.primary .shortcut-legend-btn { order: 4; }
}

/* Wraps the shortcut-legend `?` button alongside the search input so the
   legend sits immediately to the left of the search field, instead of
   floating at the far right of the toolbar. */
.search-cluster {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}

/* Surprise group — two adjacent buttons sharing a labeled boundary:
   primary "Surprise me" (random across all filtered) + secondary
   "Popular pick" (random across the top 50 by current sort). Tight gap
   makes them read as a pair, not two independent buttons. */
.surprise-group {
  display: inline-flex;
  align-items: stretch;
  gap: 0.25rem;
}
/* Soften the Surprise CTA — was the heaviest element on the bar,
   competing with active filter pills. Now reads as a secondary action
   that's still clearly the primary yellow surface, just less shouty. */
.filter-row.primary .surprise {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0;
  padding: 0 0.85rem;
  filter: saturate(0.92);
}
.filter-row.primary .surprise:hover { filter: saturate(1) brightness(1.04); }
.filter-row.primary .surprise-popular {
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0;
  padding: 0 0.75rem;
  /* Sits next to the primary yellow Surprise me — quieter so the
     primary stays clearly primary, but visible enough to discover. */
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: rgba(231, 233, 238, 0.85);
}
.filter-row.primary .surprise-popular:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.22);
  color: #ffffff;
}

/* Pill-shaped buttons inside the polished toolbar — capsule corners +
   tighter padding give the row a streaming-app-header feel. The view-
   toggle rail stays segmented (its own rounded wrapper), only its
   children pick up the pill radius. */
.filter-row.primary .btn { padding: 0 0.85rem; font-size: 0.85rem; border-radius: 999px; }
.filter-row.primary .surprise { font-weight: 600; }
.filter-row.primary .view-toggle { border-radius: 999px; }
.filter-row.primary .view-btn { width: 2.1rem; font-size: 0.95rem; border-radius: 999px; }

.filter-row label {
  display: flex;
  flex-direction: column;
  font-size: 0.75rem;
  color: var(--muted);
  gap: 0.25rem;
  min-width: 110px;
  flex: 1 1 110px;
}

.search {
  flex: 1 1 100% !important;
  min-width: 0 !important;
  position: relative;
}
@media (min-width: 760px) {
  .search { flex: 1 1 240px !important; }
}

.search > label { display: block; }

.search-suggestions {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  z-index: 100;
  margin: 0;
  padding: 0.3rem;
  list-style: none;
  /* Explicit solid color (not the theme alias) so the panel is never
     semi-transparent — content underneath must never bleed through. */
  background-color: #161922;
  background-image: linear-gradient(180deg, rgba(255,255,255,0.025), rgba(255,255,255,0));
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  box-shadow:
    0 14px 36px rgba(0, 0, 0, 0.55),
    0 2px 8px rgba(0, 0, 0, 0.35),
    0 0 0 1px rgba(255, 255, 255, 0.03) inset;
  max-height: 380px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.search-suggestions::-webkit-scrollbar {
  width: 10px;
}
.search-suggestions::-webkit-scrollbar-track {
  background: transparent;
  margin: 6px 0;
}
.search-suggestions::-webkit-scrollbar-thumb {
  background-color: var(--border-strong);
  background-clip: content-box;
  border: 3px solid transparent;
  border-radius: 999px;
}
.search-suggestions::-webkit-scrollbar-thumb:hover {
  background-color: var(--muted-2);
  background-clip: content-box;
}

.search-suggestion {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.45rem 0.6rem;
  min-height: 44px;
  cursor: pointer;
  color: var(--text);
  font-size: 0.9rem;
  border: 0;
  background: transparent;
  border-radius: 8px;
  transition: background 0.12s var(--ease);
}

.search-suggestion + .search-suggestion { margin-top: 1px; }

.search-suggestion:hover,
.search-suggestion[aria-selected="true"] {
  background: var(--surface-2);
}

.search-suggestion .ss-poster {
  flex: 0 0 32px;
  width: 32px;
  height: 48px;
  background: var(--border);
  border-radius: 3px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted-2);
  font-size: 0.7rem;
}

.search-suggestion .ss-poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Same hash-based tint as the card/modal poster placeholder, scaled to
   the dropdown's 32×48 slot. A single initial sits centered. */
.search-suggestion .ss-poster.ss-poster-fallback {
  --poster-hue: 220;
  background:
    radial-gradient(circle at 30% 25%, hsl(var(--poster-hue) 70% 45% / 0.7), transparent 70%),
    radial-gradient(circle at 70% 75%, hsl(calc(var(--poster-hue) + 60) 70% 35% / 0.55), transparent 70%),
    var(--surface-2);
}
.search-suggestion .ss-poster-initial {
  font-size: 1rem;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.95);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.search-suggestion .ss-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1 1 auto;
}

.search-suggestion .ss-title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.search-suggestion .ss-meta {
  font-size: 0.75rem;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.search-suggestion .ss-ep-hint {
  display: block;
  font-size: 0.72rem;
  color: var(--accent);
  font-style: italic;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 0.1rem;
}

.search-suggestion mark {
  background: transparent;
  color: var(--accent);
  font-weight: 600;
  padding: 0;
}

.search input,
.filter-row input,
.filter-row select {
  height: var(--ctrl-h);
  padding: 0 0.7rem;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--ctrl-radius);
  font: inherit;
  font-size: var(--ctrl-font);
  width: 100%;
  appearance: none;
  -webkit-appearance: none;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease), box-shadow 0.2s var(--ease);
  /* Tell the browser this control is dark so the native popup menu uses
     a dark theme too instead of the OS default light. */
  color-scheme: dark;
}

/* Some browsers (Firefox in particular) render <option> with the OS
   light theme even with color-scheme: dark — force dark explicitly. */
.filter-row select option {
  background: var(--surface);
  color: var(--text);
}

/* Inside the glassy primary toolbar the search field needs a transparent
   look so the surface reads as one piece, not nested boxes. Lighter and
   smaller text reads as a refined search field, not a heavy form input. */
.filter-row.primary .search input {
  background: transparent;
  border-color: transparent;
  padding: 0 0.8rem 0 2.15rem;
  font-size: 0.82rem;
  font-weight: 400;
  border-radius: 999px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a4adbd' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='7'/><path d='M21 21l-4.35-4.35'/></svg>");
  background-repeat: no-repeat;
  background-position: 0.7rem center;
  background-size: 0.9rem 0.9rem;
  transition: background-color 0.18s var(--ease), border-color 0.18s var(--ease),
              box-shadow 0.18s var(--ease);
}
.filter-row.primary .search input::placeholder {
  color: rgba(231, 233, 238, 0.62);
  font-weight: 400;
}
.filter-row.primary .search input:hover { background-color: rgba(255, 255, 255, 0.025); }
.filter-row.primary .search input:focus {
  background-color: rgba(255, 255, 255, 0.05);
  border-color: rgba(245, 197, 24, 0.55);
  outline: none;
  /* Soft amber glow on focus — clearly highlights the active state
     without competing with the yellow Surprise CTA. */
  box-shadow: 0 0 0 3px rgba(245, 197, 24, 0.22);
}
.search input { padding: 0 0.85rem; font-size: 1rem; }
.search input::placeholder { color: rgba(243, 244, 246, 0.55); }

.filter-row select {
  padding-right: 2rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 1rem) calc(50% - 2px), calc(100% - 0.6rem) calc(50% - 2px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.filter-row input:focus,
.filter-row select:focus,
.search input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* ---------- Quick filters drawer ----------
 * Wraps the row of Type / Watched / IMDb / Gems chips behind a small
 * toggle so the main page reads results-first. The .label-filters
 * styles handle the panel chrome when expanded. */
.quick-filters-wrap {
  margin: 0.6rem 0;
}
.quick-filters-wrap > summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--border);
  color: rgba(243, 244, 246, 0.88);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), color 0.15s var(--ease);
}
.quick-filters-wrap > summary::-webkit-details-marker { display: none; }
.quick-filters-wrap > summary:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--border-strong);
  color: #F3F4F6;
}
.quick-filters-wrap[open] > summary {
  background: var(--accent-soft);
  border-color: var(--accent-strong);
  color: var(--accent);
}

/* ---------- "More filters" panel ----------
   Editorial filter sheet: caption-style labels, dense grid of slim
   inputs, soft hairline-divided sub-sections, integrated Reset link.
   Surface is just a hint above the page bg, not a strong card. */

/* Always-on panel chrome — keeps the section the same outer width whether
   the user has expanded the additional filters or not. Previously the
   background/border/padding only applied with [open], so opening the
   section made it appear to widen. */
.advanced {
  margin-top: 0.6rem;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.018));
  border: 1px solid rgba(255, 255, 255, 0.085);
  border-radius: 12px;
  padding: 0.9rem 1.1rem;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.04) inset,
    var(--shadow-lift);
}

/* Trigger: small, quiet, refined. A "+" rotates to "×" when open. */
.advanced summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.35rem 0.7rem;
  border-radius: 7px;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(243, 244, 246, 0.88);
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}
/* The browser draws a default focus ring on <summary> when one of its
 * children (e.g. the in-summary "Clear all filters" pill) is clicked —
 * focus moves to the summary and the ring frames BOTH children, looking
 * like a single black border wrapping the chip and the pill together.
 * Suppress that ring; the pill and the summary text each have their own
 * hover/focus indicators so no accessibility is lost. */
.advanced summary:focus,
.advanced summary:focus-visible {
  outline: none !important;
  box-shadow: none !important;
}
.advanced summary::-webkit-details-marker { display: none; }
.advanced summary:hover {
  color: #F3F4F6;
  background: rgba(255, 255, 255, 0.035);
}
.advanced summary::before {
  content: '+';
  display: inline-block;
  width: 0.9rem;
  text-align: center;
  font-size: 0.95rem;
  font-weight: 400;
  color: rgba(243, 244, 246, 0.68);
  transition: transform 0.2s var(--ease), color 0.2s var(--ease);
}
.advanced[open] summary {
  color: var(--text);
  margin: 0 0 0.65rem;
}
.advanced[open] summary::before {
  transform: rotate(45deg);  /* + → × */
  color: var(--accent);
}

/* Row that pairs the Quick filters pill with the Clear-filters pill
 * immediately to its right. The .label-filters panel lives as a sibling
 * row below — its visibility is driven by the <details>' [open] state via
 * :has(), so the panel can't widen the row and push the Clear pill below
 * the summary. */
.quick-filters-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.6rem 0;
}
.quick-filters-row > .quick-filters-wrap { margin: 0; }

#quickFiltersPanel { display: none; }
.filters:has(.quick-filters-wrap[open]) #quickFiltersPanel {
  display: flex;
  margin-top: 0.5rem;
}

/* Show Finder reuses the same collapse mechanism. Its panel needs its own
   id (the Seasons one is unique) but the .filters:has([open]) ancestor rule
   is identical, so the "Quick filters" summary actually toggles the panel
   instead of just recoloring itself. */
#finderQuickFiltersPanel { display: none; }
.filters:has(.quick-filters-wrap[open]) #finderQuickFiltersPanel {
  display: flex;
  margin-top: 0.5rem;
}

.advanced summary .advanced-summary-label { display: inline-flex; align-items: center; }

/* "Clear filters" pill — anchored to the right side of the Quick filters
 * row so it's always visible alongside the filter controls, regardless of
 * whether either drawer is open. Hidden via [hidden] when no filters are
 * active. */
.advanced-reset,
.advanced-reset:hover {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 0.85rem;
  background: rgba(248, 113, 113, 0.12);
  border: 1px solid rgba(248, 113, 113, 0.4);
  border-radius: 999px;
  color: var(--danger, #f87171) !important;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: none !important;
  line-height: 1;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), color 0.15s var(--ease);
  flex: 0 0 auto;
  white-space: nowrap;
}
.advanced-reset:hover {
  background: rgba(248, 113, 113, 0.2);
  border-color: rgba(248, 113, 113, 0.6);
}
.advanced-reset:focus-visible {
  outline: 2px solid rgba(245, 197, 24, 0.55);
  outline-offset: 2px;
}
.advanced-reset[hidden] { display: none; }


/* Numeric inputs + Sort: 4-column grid for an even rhythm, caption
   labels above slim 32 px controls. */
.advanced .filter-row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.7rem 0.7rem;
  align-items: end;
}
.advanced .filter-row label {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
  flex: 0 1 auto;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* Captions in supporting role above the input — bright enough to
     scan cleanly against the dark panel without competing with the
     input value in full white. */
  color: rgba(231, 233, 238, 0.78);
}
.advanced .filter-row input,
.advanced .filter-row select {
  width: 100%;
  height: 32px;
  padding: 0 0.7rem;
  /* Stronger contrast than the panel itself so inputs read as
     defined slots, not the same plane as the panel surface. */
  background: rgba(255, 255, 255, 0.055);
  border: 1px solid rgba(255, 255, 255, 0.11);
  border-radius: 7px;
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  font-family: inherit;
  appearance: none;
  -webkit-appearance: none;
  color-scheme: dark;
  /* Subtle inner depth so each input sits like a small pressed slot. */
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.18) inset;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease);
}
.advanced .filter-row input:hover,
.advanced .filter-row select:hover {
  background: rgba(255, 255, 255, 0.075);
  border-color: rgba(255, 255, 255, 0.18);
}
.advanced .filter-row input:focus,
.advanced .filter-row select:focus {
  outline: none;
  background: rgba(255, 255, 255, 0.09);
  border-color: rgba(245, 197, 24, 0.55);
  box-shadow:
    0 0 0 2px rgba(245, 197, 24, 0.14),
    0 1px 0 rgba(0, 0, 0, 0.18) inset;
}
.advanced .filter-row input::placeholder {
  color: rgba(231, 233, 238, 0.7);
  font-weight: 400;
}
/* Restore the chevron on the slim Sort select (the default arrow gets
   stripped by appearance:none). Same dark SVG the toolbar uses. */
.advanced .filter-row select {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23a4adbd' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='3,5 6,8 9,5'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.55rem center;
  background-size: 12px 12px;
  padding-right: 1.75rem;
}

/* Genres: editorial sub-section with its own caption + visible
   divider above. Chips are bigger and brighter than the inputs so
   the section reads as "tap a chip" rather than another row of
   form fields. */
.chip-section {
  position: relative;
  margin-top: 1.15rem;
  padding-top: 1rem;
  border-top: 1px solid rgba(255, 255, 255, 0.075);
}
.chip-section-title {
  display: block;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(231, 233, 238, 0.85);
  margin-bottom: 0.5rem;
}
/* Hide the whole section when its chip row is empty (e.g. providers
   before the cache has been populated). :has() saves a JS toggle. */
.chip-section:has(.genre-row:empty) { display: none; }

.genre-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.4rem;
}
.genre-row:empty { display: none; }

.genre-chip,
.genre-chip:hover {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 0.8rem;
  background: rgba(255, 255, 255, 0.075);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 999px;
  color: #ffffff !important;
  font: inherit;
  font-size: 0.74rem;
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  line-height: 1;
  /* Subtle top-edge lift gives each chip a sense of object */
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.03) inset !important;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease), color 0.18s var(--ease);
}
.genre-chip:hover {
  background: rgba(255, 255, 255, 0.11);
  border-color: rgba(255, 255, 255, 0.26);
  color: #ffffff !important;
}
/* Pin the pressed-state background (main.css red button:hover:active). */
.genre-chip:hover:active {
  background: rgba(255, 255, 255, 0.13);
}
.genre-chip[aria-pressed="true"]:hover:active {
  background: rgba(245, 197, 24, 0.16);
}
.genre-chip[data-exclude="true"]:hover:active {
  background: rgba(248, 113, 113, 0.16);
}
.genre-chip[aria-pressed="true"],
.genre-chip[aria-pressed="true"]:hover {
  background: rgba(245, 197, 24, 0.13);
  border-color: rgba(245, 197, 24, 0.5);
  color: var(--accent) !important;
}
/* Tri-state for genre chips only — clicking a green chip again flips it to
   "exclude" (red). Click once more to clear. */
.genre-chip[data-exclude="true"],
.genre-chip[data-exclude="true"]:hover {
  background: rgba(248, 113, 113, 0.12);
  border-color: rgba(248, 113, 113, 0.55);
  color: var(--danger) !important;
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
}
.genre-chip:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow:
    0 0 0 2px rgba(245, 197, 24, 0.18),
    0 1px 0 rgba(255, 255, 255, 0.03) inset !important;
}

/* Reset link: integrated, not a stranded button. */
.filter-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  margin-top: 1rem;
  padding-top: 0.8rem;
  border-top: 1px solid rgba(255, 255, 255, 0.075);
}
.filter-actions .btn,
.filter-actions .btn:hover {
  height: 26px;
  padding: 0 0.6rem;
  background: transparent;
  border: 0;
  color: rgba(231, 233, 238, 0.85) !important;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  box-shadow: none !important;
  transition: color 0.15s var(--ease);
}
.filter-actions .btn:hover { color: var(--accent) !important; background: transparent; }
.filter-actions .btn:disabled,
.filter-actions .btn[disabled] {
  opacity: 0.4;
  pointer-events: none;
}

/* Mobile: collapse the 4-up grid to 2 so inputs don't get squished. */
@media (max-width: 540px) {
  .advanced { padding: 0.75rem 0.85rem; }
  .advanced .filter-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .chip-section { margin-top: 0.9rem; padding-top: 0.8rem; }
  .filter-actions { margin-top: 0.85rem; padding-top: 0.7rem; }
}

/* ---------- Stats bar ---------- */

.stats-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2rem;
  padding: 0.85rem 1.1rem;
  background: var(--surface-glass);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid var(--border);
  border-radius: 12px;
  margin: 1rem 0 0.5rem;
  font-size: 0.85rem;
  color: var(--muted);
  align-items: center;
  letter-spacing: 0.005em;
}
.stats-bar:empty { display: none; }
.stats-bar > span { display: inline-flex; align-items: baseline; gap: 0.35rem; }
.stats-bar strong {
  color: var(--text);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.stats-bar .progress {
  flex: 1 1 200px;
  min-width: 160px;
  height: 6px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}
.stats-bar .progress-fill {
  position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--accent) 0%, var(--accent-warm) 50%, var(--good) 100%);
  border-radius: 999px;
  transition: width 0.4s var(--ease);
  width: 0;
}
.stats-bar .stale {
  color: var(--warn);
  font-size: 0.78rem;
  padding: 0.15rem 0.55rem;
  background: rgba(251, 146, 60, 0.12);
  border-radius: 999px;
}

/* ---------- Meta + results ---------- */

.meta, .footer-meta {
  color: var(--muted);
  font-size: 0.85rem;
  letter-spacing: 0.005em;
}
.meta {
  margin: 1rem 0 0.75rem;
  padding-left: 0.15rem;
}
.footer-meta {
  padding: 1.5rem 0 0.5rem;
  text-align: center;
  font-size: 0.78rem;
  color: var(--muted-2);
}

/* Data-source attribution (TMDB logo + disclaimer, JustWatch, IMDb).
   Required by the TMDB API terms. */
.data-attribution {
  padding: 0.25rem 1rem 1rem;
  text-align: center;
  font-size: 0.72rem;
  color: var(--muted-2);
}
.data-attribution .tmdb-logo {
  height: 13px;
  width: auto;
  vertical-align: middle;
  opacity: 1;
}
.data-attribution p {
  margin: 0.45rem auto 0;
  max-width: 640px;
  line-height: 1.5;
}
.data-attribution a {
  color: var(--muted);
  text-decoration: underline;
}
.data-attribution a:hover { color: var(--text); }

.results-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 1.25rem;
  margin: 0.5rem 0 1rem;
}

.results-grid.list-view {
  grid-template-columns: 1fr;
  gap: 0.5rem;
}

@media (min-width: 980px) {
  .results-grid {
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  }
}

/* ---------- Card (grid view) ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color 0.2s var(--ease), transform 0.25s var(--ease),
              box-shadow 0.25s var(--ease);
  cursor: pointer;
  position: relative;
}
.card:hover {
  border-color: var(--border-strong);
  transform: translateY(-3px);
  box-shadow: var(--shadow-lift);
}
.card:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--accent-glow);
}
.card.is-watched,
.card.is-in-compare {
  border-color: rgba(52, 211, 158, 0.4);
}
.card.is-watched::after,
.card.is-in-compare::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(52, 211, 158, 0.25);
}
.card.is-watched .card-poster img { opacity: 0.55; }

/* Posters: TMDB poster_path images are 2:3 — match the natural aspect so
   the full poster shows without zooming or cropping. */
.card-poster {
  position: relative;
  aspect-ratio: 2 / 3;
  background: var(--surface-2);
  overflow: hidden;
}

.card-poster img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;          /* container is 2:3 = native aspect, so no crop */
  object-position: center;
  background: var(--surface-2);
  transition: opacity 0.2s;
}

.poster-fallback {
  --poster-hue: 220;
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.6rem;
  background:
    radial-gradient(circle at 30% 25%, hsl(var(--poster-hue) 70% 45% / 0.55), transparent 65%),
    radial-gradient(circle at 70% 75%, hsl(calc(var(--poster-hue) + 60) 70% 35% / 0.4), transparent 65%),
    var(--surface-2);
}
.poster-fallback-title {
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1.2;
  color: rgba(255, 255, 255, 0.92);
  text-align: center;
  letter-spacing: -0.01em;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
}
/* Big modal-poster context — larger title text so it doesn't look lost */
.modal-poster .poster-fallback-title {
  font-size: 1.05rem;
  -webkit-line-clamp: 5;
}
/* Compact row-poster context — smaller title */
.row-poster .poster-fallback-title {
  font-size: 0.72rem;
  padding: 0.35rem;
}

.card-body {
  padding: 0.85rem 1rem 0.55rem;
  /* Grid so the chip row absorbs any extra vertical height — keeps the
     sparkline (a sibling of card-body, not a child) at the same y across
     cards of varying chip-row line counts. Row order matches DOM source:
       1. card-head    (title + season)
       2. card-meta    (year + genres)
       3. card-shapes  (1fr — absorbs leftover vertical space)
     Since .results-grid stretches all cards in a row to the same height,
     the 1fr region resolves to the same value across siblings. */
  display: grid;
  grid-template-rows: auto auto 1fr;
  row-gap: 0.55rem;
  flex: 1;
}

.card-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
}
.card-title {
  font-size: 1rem;
  letter-spacing: -0.01em;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.card-season {
  font-size: 0.78rem;
  color: var(--muted);
  white-space: nowrap;
  flex-shrink: 0;
}
.card-meta {
  display: flex;
  gap: 0.6rem;
  font-size: 0.75rem;
  color: var(--muted);
  flex-wrap: wrap;
}
/* Chip row wraps freely — overflow is preserved so every shape/provider
 * chip stays visible. Card-body's grid handles curve alignment so a
 * 2-line wrap here doesn't push the sparkline below it; the curve stays
 * pinned to its own row, the chip area just consumes more of the 1fr
 * flex region between meta and curve. */
.card-shapes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  align-content: flex-start;
}

.shape-tag {
  background: var(--surface-3);
  color: var(--text);
  font-size: 0.68rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font-family: inherit;
  line-height: 1.4;
}
button.shape-tag.is-clickable {
  cursor: pointer;
  transition: background-color 0.12s, color 0.12s, border-color 0.12s;
}
button.shape-tag.is-clickable:hover {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent-strong);
}
.shape-tag.active {
  background: var(--accent-soft);
  color: var(--accent);
}

/* Streaming-platform chip — distinct from the shape tags so the row reads
 * as "<pattern tags> · <where to watch>" rather than one homogenous token
 * blob. Cyan-tinted outline + leading play glyph give it a "channel" feel
 * versus the warm-filled trajectory tags. */
.provider-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: rgba(56, 189, 248, 0.08);
  color: rgba(125, 211, 252, 0.95);
  font-size: 0.66rem;
  font-weight: 600;
  padding: 0.1rem 0.55rem 0.1rem 0.45rem;
  border-radius: 999px;
  border: 1px solid rgba(56, 189, 248, 0.28);
  line-height: 1.4;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.provider-tag::before {
  content: '▶';
  font-size: 0.55rem;
  opacity: 0.85;
}

/* List-view genre line — mirrors .card-meta on the grid card. Lives inside
 * .row-head so it flows inline with title / season / year, separated by a
 * subtle bullet so the row stays compact. */
.row-genres {
  font-size: 0.78rem;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.row-genres:empty,
.row-genres[hidden] { display: none; }
.row-genres::before {
  content: '·';
  margin: 0 0.45rem 0 0;
  color: var(--muted-2);
}

/* Best / Worst season rank badges. Designed to sit inline with the
   season-metadata line ("S2 · 10 eps") rather than the title — compact
   pill, no wrap, subtle tinted surface. Icon + uppercase label give a
   premium streaming-app feel without competing with the title. */
.best-badge,
.worst-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  /* No left margin — these now sit at the start of .card-shapes/.row-shapes
     where the row's gap handles spacing from the next pill. */
  padding: 0 0.42rem;
  height: 1.05rem;
  line-height: 1;
  border-radius: 999px;
  border: 1px solid transparent;
  font-weight: 700;
  letter-spacing: 0.06em;
  white-space: nowrap;
  vertical-align: middle;
  font-size: 0.6rem;
  text-transform: uppercase;
  flex-shrink: 0;
}
.best-badge {
  color: var(--accent);
  background: rgba(245, 197, 24, 0.12);
  border-color: rgba(245, 197, 24, 0.28);
}
.worst-badge {
  color: var(--danger);
  background: rgba(248, 113, 113, 0.10);
  border-color: rgba(248, 113, 113, 0.28);
}
.rank-badge-icon {
  font-size: 0.72rem;
  line-height: 1;
  /* Slight optical lift — symbols sit lower than baseline. */
  transform: translateY(-0.04em);
}
.worst-badge .rank-badge-icon {
  /* The ▼ symbol is bottom-heavy; shift the opposite way. */
  transform: translateY(0.02em);
  font-size: 0.58rem;
}
.rank-badge-label { line-height: 1; }

.above-imdb {
  display: inline-block;
  margin-left: 0.3rem;
  color: var(--good);
  font-weight: 700;
  font-size: 0.85em;
  cursor: help;
  vertical-align: middle;
}
.above-imdb-pill {
  padding: 0.05rem 0.5rem;
  background: rgba(74, 222, 128, 0.15);
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: default;
}

/* ---------- Quick label filters ----------
   Bottom half of the unified toolbar — same glass surface as the
   search row above, separated only by a near-invisible hairline. No
   per-group boxes, no per-pill borders, no extra container. The shared
   shell anchors the filters to the toolbar so they read as part of it,
   not floating below it. */
.label-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Horizontal gap between groups (~1rem) does the work of dividers. */
  gap: 0.3rem 1rem;
  /* Snug interior — close to the search row above, just enough room
     for the pills not to touch the bottom of the shell. */
  padding: 0.3rem 0.5rem 0.32rem;
  background: var(--surface-glass);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  /* Inner hairline divider only — perimeter shares the search row's
     border, top is the seam between rows. */
  border-top: 1px solid rgba(255, 255, 255, 0.04);
  border-radius: 0 0 12px 12px;
  /* Generous spacing AFTER the toolbar before the shape chips. */
  margin-bottom: 0.85rem;
}

/* Group = just a flex line of [tiny caption] [chips]. No surface, no
   border, no fixed height. */
.label-group {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: auto;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.label-group:hover,
.label-group:focus-within {
  background: transparent;
  border: 0;
  box-shadow: none;
}

/* Section caption: quiet, low-contrast, no divider. Just a label. */
.label-group-name {
  display: inline-flex;
  align-items: center;
  height: auto;
  padding: 0;
  margin: 0 0.05rem 0 0;
  font-size: 0.62rem;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: rgba(231, 233, 238, 0.75);
  font-weight: 600;
  border: 0;
  user-select: none;
  white-space: nowrap;
}

/* Chips: invisible until you hover or activate. No border, no shadow,
   no container — they read as tokens on the page. */
/* Total reset across every conceivable chip state. main.css applies a
 * sitewide `button { box-shadow: inset 0 0 0 1px #555 }` and changes it
 * to red on hover; without aggressive overrides, both the hovered chip
 * and its neighbor in the same group end up with 1px insets that read
 * together as a single border wrapping both buttons. We also keep the
 * focus indicator inside the chip (background-only) so the focus ring
 * never extends into a neighbor. */
.label-chip,
.label-chip:hover,
.label-chip:focus,
.label-chip:focus-visible,
.label-chip:active,
.label-chip[aria-pressed="true"],
.label-chip[aria-pressed="true"]:hover,
.label-chip[aria-pressed="true"]:focus,
.label-chip[aria-pressed="true"]:focus-visible {
  outline: 0 !important;
  /* One hairline border we own, pinned across every state with !important
     so main.css's inset rings can never combine with it. State rules below
     vary only border-color (also !important, later in the file, so they
     win this tie). */
  border: 1px solid rgba(255, 255, 255, 0.1) !important;
  box-shadow: none !important;
}

.label-chip {
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 26px;
  padding: 0 0.65rem;
  /* Faint surface + hairline border so inactive options read as quiet
     controls rather than flat gray text. The color needs !important:
     main.css paints every button #555555 !important, which silently wins
     over any non-important color here regardless of specificity. */
  background: rgba(255, 255, 255, 0.045);
  color: #ffffff !important;
  font: inherit;
  font-size: 0.76rem;
  font-weight: 600;
  line-height: 1;
  border-radius: 7px;
  cursor: pointer;
  transition:
    background 0.18s var(--ease),
    border-color 0.18s var(--ease),
    color 0.18s var(--ease);
  white-space: nowrap;
}
/* Bigger gap so even if some downstream theme tries to draw a 1px outline
 * it can't visually touch the neighbor — but they still feel like one
 * group thanks to the parent .label-group's gap rhythm. */
.label-chip + .label-chip { margin-left: 0.35rem; }
.label-chip:hover {
  color: #ffffff !important;
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.22) !important;
}
/* Pin the pressed-state background too — main.css gives every
   button:hover:active a translucent red fill otherwise. */
.label-chip:hover:active {
  background: rgba(255, 255, 255, 0.1);
}
.label-chip[aria-pressed="true"]:hover:active {
  background: rgba(245, 197, 24, 0.16);
}
.label-chip:focus-visible {
  /* Background + border focus indicator — no outline, no shadow. Cannot
     possibly extend into the adjacent chip's space. */
  background: rgba(245, 197, 24, 0.2);
  border-color: rgba(245, 197, 24, 0.45) !important;
  color: var(--accent) !important;
}

/* Active = a soft accent wash + accent text. No ring, no shadow.
   Reads as a quietly highlighted token rather than a button. Tinted
   one notch quieter than the shape chips above so the toolbar pills
   don't draw attention away from the editorial pattern chips. */
.label-chip[aria-pressed="true"] {
  background: rgba(245, 197, 24, 0.085);
  border-color: rgba(245, 197, 24, 0.4) !important;
  color: rgba(245, 197, 24, 0.92) !important;
  box-shadow: none;
}
.label-chip[aria-pressed="true"]:hover {
  background: rgba(245, 197, 24, 0.14);
  border-color: rgba(245, 197, 24, 0.5) !important;
  color: var(--accent) !important;
}
.label-chip-good[aria-pressed="true"] {
  background: rgba(74, 222, 128, 0.085);
  border-color: rgba(74, 222, 128, 0.4) !important;
  color: rgba(74, 222, 128, 0.92) !important;
}
.label-chip-good[aria-pressed="true"]:hover {
  background: rgba(74, 222, 128, 0.14);
  border-color: rgba(74, 222, 128, 0.5) !important;
  color: var(--good) !important;
}

.search-suggestion-empty {
  cursor: default;
  color: var(--muted-2);
  font-style: italic;
  padding: 0.5rem 0.7rem;
}
.search-suggestion-empty:hover {
  background: transparent;
}

/* fuzzy-search: non-interactive "Did you mean?" header that introduces
   the typo-tolerant matches appended after the strict suggestions. */
.search-suggestion-subheader {
  cursor: default;
  color: var(--muted-2);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.45rem 0.7rem 0.2rem;
  pointer-events: none;
  border-top: 1px solid var(--border);
  margin-top: 0.25rem;
}
.search-suggestion-subheader:hover {
  background: transparent;
}

.show-seasons {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.show-season {
  /* Sparkline is the whole point — give it its own full-width row at all
     breakpoints. Top row keeps S# / meta / Avg compact; bottom row is a
     full-bleed spark. */
  display: grid;
  grid-template-columns: 56px 1fr auto;
  grid-template-areas:
    "num   meta  stats"
    "spark spark spark";
  column-gap: 0.85rem;
  row-gap: 0.55rem;
  align-items: start;
  padding: 0.7rem 0.9rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  cursor: pointer;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease), transform 0.18s var(--ease);
}
.show-season:hover {
  background: var(--surface-3);
  border-color: var(--border-strong);
  transform: translateX(2px);
}
.show-season:focus-visible {
  background: var(--surface-3);
  border-color: var(--accent);
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.show-season.is-watched {
  border-color: rgba(52, 211, 158, 0.35);
}

.show-season .ss-num {
  grid-area: num;
  font-weight: 600;
  font-size: 1rem;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.show-season .ss-meta {
  grid-area: meta;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.show-season .ss-eps {
  font-size: 0.78rem;
  color: var(--muted);
}
.show-season .ss-shape-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-top: 0.2rem;
}

/* The actual grid child is the wrapper (.ss-spark-wrap), not the SVG —
   grid-area must live here so the spark row spans full card width. */
.show-season .ss-spark-wrap {
  grid-area: spark;
  width: 100%;
}
.show-season .ss-spark {
  width: 100%;
  height: 48px;
  background: var(--surface);
  border-radius: 4px;
  display: block;
}

.show-season .ss-stats {
  grid-area: stats;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  font-size: 0.78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.show-season .ss-avg {
  color: var(--text);
  font-weight: 600;
}
.show-season .ss-watched-tag {
  color: var(--good);
  font-size: 0.7rem;
}

@media (max-width: 560px) {
  /* Tighter S# column + row gap on phones; the 2-row grid layout itself is
     defined at the base level so the spark always spans full width. */
  .show-season {
    grid-template-columns: 48px 1fr auto;
    row-gap: 0.45rem;
  }
  .show-season .ss-spark { height: 44px; }
}

.curve {
  /* .curve-wrap is a direct child of .card now (no parent padding to
     bleed out of), so the SVG sits at the full card width with no
     negative margins. */
  width: 100%;
  height: 70px;
  background:
    radial-gradient(ellipse 60% 50% at 50% 100%, rgba(245, 197, 24, 0.06), transparent 70%),
    var(--surface-2);
  /* Keep the bottom corners rounded with the card (top corners flush against
     the content above) — but no border-radius makes the line/area truly hit
     the corner pixels without any rounded clip. */
  border-radius: 0;
  display: block;
  /* Let hover dots at viewBox x=0 / x=W render fully instead of half-clipped. */
  overflow: visible;
}
.curve-area { fill: rgba(245, 197, 24, 0.18); stroke: none; }
.curve-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 4px rgba(245, 197, 24, 0.35));
}
.curve-dots circle {
  fill: var(--accent);
  cursor: help;
}
.curve-dots circle.special-ep {
  fill: #c084fc;
  stroke: rgba(255, 255, 255, 0.7);
  stroke-width: 1;
}
.curve-axis .axis-grid {
  stroke: rgba(255, 255, 255, 0.08);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
/* Y-axis labels live in HTML (not SVG) so the SVG's preserveAspectRatio="none"
   doesn't horizontally squish the glyphs. The wrapper sits over the SVG,
   labels are positioned by percentage of the SVG's height. */
.curve-with-axis {
  position: relative;
}
.curve-with-axis > svg {
  display: block;
}
.curve-axis-labels {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}
.curve-axis-labels .axis-label {
  position: absolute;
  /* padXLeft (36) / W (600) = 6% of the SVG's display width. The label's
     right edge sits just inside that — matching where the gridline begins
     regardless of how wide the modal becomes. */
  right: calc(100% - 5%);
  transform: translateY(-50%);
  padding-right: 4px;
  font-size: 11px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  white-space: nowrap;
}

.modal-curve {
  width: 100%;
  height: 180px;
  /* Modal-panel is a flex column with overflow-y: auto. Without flex-shrink: 0,
     the SVG (which has no intrinsic content height) collapses when the modal's
     other content pushes the panel past its max-height, squashing the curve
     into a thin strip even though the viewBox math is correct. */
  flex-shrink: 0;
  background:
    radial-gradient(ellipse 60% 50% at 50% 100%, rgba(245, 197, 24, 0.08), transparent 70%),
    var(--surface-2);
  border-radius: 12px;
  padding: 0.5rem;
}

.season-overlay {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  flex-shrink: 0;
}
.season-overlay[hidden] { display: none; }
.overlay-curve {
  width: 100%;
  height: 200px;
  flex-shrink: 0;
  background: var(--surface-2);
  border-radius: 12px;
  padding: 0.5rem;
}
.overlay-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.8rem;
  font-size: 0.78rem;
  color: var(--muted);
}
.overlay-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.overlay-legend-swatch {
  display: inline-block;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 2px;
}

/* Season visibility chips for the overlay chart. Each chip derives its
   filled active state and swatch glow from --season-color (set inline by
   openShowModal to the same hue as its chart line), so the controls read
   as extensions of the chart rather than form inputs. The :hover /
   :hover:active twins pin every box property against main.css's button
   styling (gray/red inset rings, red press fill). */
.overlay-legend-toggle,
.overlay-legend-toggle:hover,
.overlay-legend-toggle:hover:active {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 26px;
  padding: 0 0.7rem 0 0.5rem;
  background: color-mix(in srgb, var(--season-color, #888) 13%, rgba(16, 19, 28, 0.55));
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border: 1px solid color-mix(in srgb, var(--season-color, #888) 30%, transparent);
  border-radius: 999px;
  color: rgba(248, 249, 252, 0.95) !important;
  box-shadow: none !important;
  font: inherit;
  font-size: 0.74rem;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease),
              color 0.2s var(--ease), transform 0.2s var(--ease),
              opacity 0.2s var(--ease);
}
.overlay-legend-toggle:hover {
  background: color-mix(in srgb, var(--season-color, #888) 22%, rgba(16, 19, 28, 0.55));
  border-color: color-mix(in srgb, var(--season-color, #888) 48%, transparent);
  color: #ffffff !important;
  transform: translateY(-1px);
}
.overlay-legend-toggle:hover:active {
  transform: translateY(0) scale(0.97);
}
.overlay-legend-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Hidden season: the chip empties out — glassy neutral surface, quiet
   label, desaturated swatch — so re-enabling it feels like refilling it. */
.overlay-legend-toggle--off,
.overlay-legend-toggle--off:hover,
.overlay-legend-toggle--off:hover:active {
  background: rgba(255, 255, 255, 0.03);
  border-color: rgba(255, 255, 255, 0.08);
  color: rgba(241, 243, 248, 0.45) !important;
}
.overlay-legend-toggle--off:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.14);
  color: rgba(241, 243, 248, 0.7) !important;
}
.overlay-legend-toggle--off .overlay-legend-swatch {
  opacity: 0.35;
  filter: saturate(0.25);
  box-shadow: none;
}
/* Bigger, glowing color dot inside the toggle chips. (The compare-overlay
   legend keeps the plain square swatch from the base rule above.) */
.overlay-legend-toggle .overlay-legend-swatch {
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: 0 0 7px color-mix(in srgb, var(--season-color, transparent) 55%, transparent);
  transition: opacity 0.2s var(--ease), filter 0.2s var(--ease),
              box-shadow 0.2s var(--ease);
}
.overlay-season-hidden {
  display: none;
}

.compare-fab {
  position: fixed;
  bottom: 1.25rem;
  right: 1.25rem;
  z-index: 9000;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 1rem;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #1a1300;
  font-weight: 700;
  font-size: 0.9rem;
  cursor: pointer;
  box-shadow:
    0 10px 24px rgba(0, 0, 0, 0.45),
    0 2px 8px rgba(245, 197, 24, 0.35);
  transition: transform 0.12s var(--ease), box-shadow 0.2s var(--ease);
}
.compare-fab[hidden] { display: none; }
.compare-fab:hover { transform: translateY(-1px); }
.compare-fab-icon { font-size: 1.05rem; }
.compare-fab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.35rem;
  height: 1.35rem;
  padding: 0 0.4rem;
  border-radius: 999px;
  background: rgba(26, 19, 0, 0.18);
  font-size: 0.8rem;
}

.compare-legend-remove {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.2rem 0.55rem;
  color: var(--text);
  cursor: pointer;
  font-size: 0.78rem;
  transition: border-color 0.15s, background 0.15s;
}
.compare-legend-remove:hover {
  border-color: var(--danger);
  background: rgba(248, 113, 113, 0.08);
}
.compare-legend-x {
  margin-left: 0.15rem;
  color: var(--muted);
  font-weight: 700;
}

.provider-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-top: 0.35rem;
}
.provider-badges:empty { display: none; }
.provider-badge {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  font-size: 0.72rem;
  font-weight: 600;
  border-radius: 999px;
  color: var(--text);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border);
}

/* Result-tile stats — single horizontal line, comfortable gap, values are
 * self-describing so we don't need uppercase labels. Wraps when the row
 * is narrow, but stays compact on the common widths. */
.card-stats,
.row-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.9rem;
  font-size: 0.76rem;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  line-height: 1.3;
}
/* .card-stats is now a direct child of .card (was inside .card-body), so
   it needs its own horizontal padding to match the body's text gutter. */
.card > .card-stats {
  padding: 0.4rem 1rem 0.85rem;
}
.card-stats > span,
.row-stats > span {
  white-space: nowrap;
}
.card-stats .stat-runtime[hidden],
.row-stats .stat-runtime[hidden] { display: none; }
.stat-climb { color: var(--good); font-weight: 600; }

/* ---------- List view rows ---------- */

.row {
  display: grid;
  /* Layout: poster · text (1fr, fills available width so stats line out
     horizontally) · curve (240px, right of the row) · watch (36px, far
     right edge). With no gray sparkline backdrop, the empty space inside
     row-main between text and curve just reads as row surface — no
     visible padding gap. */
  grid-template-columns: 56px 1fr 240px 36px;
  gap: 1rem;
  align-items: center;
  padding: 0.7rem 0.95rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease), transform 0.2s var(--ease);
  position: relative;
}
.row:hover {
  border-color: var(--border-strong);
  background: var(--surface-2);
}
.row:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.row.is-watched,
.row.is-in-compare { border-color: rgba(52, 211, 158, 0.4); }
.row.is-watched .row-poster img { opacity: 0.55; }

.row-poster {
  width: 56px;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  overflow: hidden;
  position: relative;
  background: var(--surface-2);
}
.row-poster img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}

.row-main {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}
.row-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: baseline;
}
.row-title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.row-season, .row-year {
  font-size: 0.78rem;
  color: var(--muted);
}
.row-shapes { display: flex; gap: 0.3rem; flex-wrap: wrap; }
/* .row-stats styled together with .card-stats above as one stats grid. */
.row-curve {
  display: block;
  width: 100%;
  height: 56px;
  /* No gray rectangle backdrop — the sparkline floats directly on the
     row's surface. Removing the box also removes the "gray padding"
     perception, since there's no container that can look incompletely
     filled when the trend line happens to be high or low at the edges. */
  background: transparent;
  border-radius: 0;
  overflow: visible;
}
/* Slightly thicker stroke + brighter area fill scoped to the list-view
 * row sparkline, since it now sits on the row surface (darker than the
 * old --surface-2 box) and benefits from a touch more presence. */
.row-curve .curve-line { stroke-width: 2.4; }
.row-curve .curve-area { fill: rgba(245, 197, 24, 0.32); }
/* Stay in the grid cell — must override the base + :hover absolute
   positioning that .watch-toggle sets for the card corner overlay. */
.row-watch,
.row-watch:hover {
  position: static;
  top: auto;
  right: auto;
  width: 2rem;
  height: 2rem;
}

@media (max-width: 760px) {
  /* Stack the row into two rows so the season-trend curve — the whole
     point of the app — stays visible on mobile instead of being dropped
     for layout space. Top row: poster + title block + watch toggle.
     Bottom row: full-width curve. */
  .row {
    grid-template-columns: 48px 1fr 36px;
    grid-template-areas:
      "poster main   watch"
      "curve  curve  curve";
    row-gap: 0.5rem;
  }
  .row-poster { grid-area: poster; }
  .row-main   { grid-area: main; }
  .row-watch  { grid-area: watch; }
  .row-curve {
    grid-area: curve;
    display: block;
    height: 44px;
    width: 100%;
    /* Reset the desktop edge-bleed — on mobile the curve spans the full
       width of the row's second grid row already, so the negative margin
       would just push it under the row's left border. */
    margin-left: 0;
  }
}

/* ---------- Pagination (prev / numbers / next) ---------- */

.pager {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.25rem;
  margin: 1.25rem 0 0;
  padding: 0.25rem 0;
}
.pager[hidden] { display: none; }
/* Top pager: small, quiet — sits between the meta line and the results
 * grid. The bottom pager keeps the larger feel since it's the natural
 * landing spot after scrolling through results. */
.pager-top {
  margin: 0.25rem 0 0.85rem;
  padding: 0;
}

.pager .page-btn,
.pager .page-btn:hover {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 0.55rem;
  background: transparent;
  color: var(--muted) !important;
  border: 1px solid transparent;
  border-radius: 8px;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  box-shadow: none !important;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease), color 0.18s var(--ease);
}
.pager .page-btn:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: transparent;
  color: var(--text) !important;
}
.pager .page-btn:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.pager .page-btn[aria-current="page"],
.pager .page-btn[aria-current="page"]:hover {
  background: rgba(245, 197, 24, 0.14);
  color: var(--accent) !important;
  border-color: rgba(245, 197, 24, 0.28);
  font-weight: 700;
}
.pager .page-btn:disabled,
.pager .page-btn[aria-disabled="true"] {
  opacity: 0.3;
  cursor: not-allowed;
  color: var(--muted) !important;
}
.pager .page-ellipsis {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  height: 32px;
  color: var(--muted-2);
  user-select: none;
  font-size: 0.82rem;
}
/* Compact even further on the top pager so it never crowds the results. */
.pager-top .page-btn { min-width: 28px; height: 28px; font-size: 0.78rem; padding: 0 0.4rem; }
.pager-top .page-ellipsis { min-width: 1.25rem; height: 28px; font-size: 0.78rem; }

/* ---------- Empty + error ---------- */

.empty {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--muted);
  padding: 4rem 1.5rem;
  background:
    radial-gradient(ellipse 80% 60% at 50% 0%, rgba(245, 197, 24, 0.04), transparent 70%),
    var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: 14px;
  font-size: 0.95rem;
  line-height: 1.5;
}
.empty p { margin: 0.35rem 0; }
.empty p:first-child { color: var(--text); font-size: 1.05rem; font-weight: 500; }
.empty code {
  background: var(--surface-2);
  padding: 0.1rem 0.4rem;
  border-radius: 4px;
  font-size: 0.85em;
}
.empty pre {
  background: var(--surface-2);
  padding: 0.75rem;
  border-radius: var(--radius-sm);
  overflow-x: auto;
  text-align: left;
  display: inline-block;
  margin: 0.5rem 0;
}

/* ---------- Skeleton ---------- */

.skeleton { animation: skel-pulse 1.4s ease-in-out infinite; }
.skeleton .skeleton-block {
  background-image: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
  background-size: 200% 100%;
  animation: skel-shimmer 1.4s linear infinite;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-sm);
}
.skeleton .sk-curve { height: 70px; margin: 0.5rem 0; }
.skeleton .skeleton-line {
  height: 14px;
  border-radius: 4px;
  margin: 0.4rem 0;
  background-image: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
  background-size: 200% 100%;
  animation: skel-shimmer 1.4s linear infinite;
}
.skeleton .w-40 { width: 40%; }
.skeleton .w-70 { width: 70%; }
.skeleton .w-90 { width: 90%; }

@keyframes skel-pulse { 0%, 100% { opacity: 0.85; } 50% { opacity: 1; } }
@keyframes skel-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ---------- Modal ---------- */

.modal {
  position: fixed;
  inset: 0;
  z-index: 11000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
.modal[hidden] { display: none; }

/* The sitewide back-to-top button (assets/css/back-to-top.css) sits at
   z-index 9000 so it stays below the site nav dropdown. This app's modals
   are at 11000 to clear that same dropdown, which would bury the button's
   opt-in container behavior. While a modal is open, lift the button above
   the modal so its data-back-to-top scrolling works; it reverts to the
   shared z-index when no modal is open. */
body:has(.modal:not([hidden])) .back-to-top {
  z-index: 11001 !important;
}

.modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.modal-panel {
  position: relative;
  /* Brighter surface than the page bg so the panel reads as an elevated
     content layer rather than the same dark sheet with a thin outline.
     Stronger top-edge gradient + a more present border + a wider shadow
     give it visual lift away from the page. */
  background:
    radial-gradient(ellipse 80% 50% at 50% 0%, rgba(245, 197, 24, 0.07), transparent 60%),
    linear-gradient(180deg, #1a1f2c, #14182265 40%, #0e1219 100%),
    var(--surface);
  border: 1px solid rgba(255, 255, 255, 0.085);
  border-radius: 18px;
  max-width: 780px;
  width: 100%;
  /* 100dvh fixes iOS Safari over-counting the URL bar height. The vh
     line is a fallback for older browsers that don't know dvh. */
  max-height: calc(100vh - 2rem);
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 1.9rem 1.9rem 1.6rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.55),
    0 4px 14px rgba(0, 0, 0, 0.45),
    0 0 0 1px rgba(255, 255, 255, 0.03) inset;
}
.modal-panel::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 18px;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 25%);
}

.modal-close {
  /* Anchored to the panel's top-right, slightly inset from the edge.
     position:absolute pulls the button out of the flex flow so the
     header content (poster + heading) starts at the panel's top
     padding instead of beneath a 1.5rem negative-margin overlap. */
  position: absolute;
  top: 0.85rem;
  right: 0.85rem;
  z-index: 3;
}

/* Back: the close button's sibling — same 36px glass circle, anchored just
   left of the ×, with a CSS-drawn left chevron. Only visible while there is
   a previous modal view to return to (drill-downs via related cards, season
   rows, or "View show"). Hover mirrors the × (brighten + soft glow) and
   nudges the chevron left so the affordance reads as "step back". */
.modal-back,
.modal-back:hover {
  position: absolute;
  top: 0.85rem;
  right: 3.6rem;
  z-index: 3;
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.85) !important;
  font-size: 0;
  line-height: 1;
  box-shadow:
    0 4px 14px rgba(0, 0, 0, 0.3),
    0 0 0 1px rgba(0, 0, 0, 0.35) inset !important;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease), transform 0.08s var(--ease),
              box-shadow 0.18s var(--ease);
}
.modal-back[hidden] { display: none; }
.modal-back::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 9px;
  height: 9px;
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  border-radius: 1px;
  /* Chevron's optical center sits right of its geometric center. */
  transform: translate(calc(-50% + 1.5px), -50%) rotate(45deg);
  transition: transform 0.2s var(--ease);
}
.modal-back:hover {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.3);
  color: #ffffff !important;
  box-shadow:
    0 4px 14px rgba(0, 0, 0, 0.35),
    0 0 0 1px rgba(255, 255, 255, 0.08),
    0 0 18px rgba(255, 255, 255, 0.12) !important;
}
.modal-back:hover::before {
  transform: translate(calc(-50% + 0.5px), -50%) rotate(45deg);
}
.modal-back:active { transform: scale(0.92); }
.modal-back:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Reserve space on the header's right side so long titles don't run
   under the absolutely-positioned close button. ~2.75rem clears the
   36 px button plus the 0.85 rem inset. */
.modal-header,
.changelog-header,
#compareModalTitle {
  padding-right: 2.75rem;
}

/* Roll-again is anchored directly above the poster — same width, slim
   height, square-ish corners — so it reads as part of the poster card
   rather than a floating CTA. The shared --modal-poster-w token keeps
   it locked to the poster on every breakpoint. */
.modal-panel { --modal-poster-w: 120px; }
.modal-reroll {
  position: absolute;
  top: 1.75rem;            /* matches .modal-panel padding */
  left: 1.75rem;           /* matches .modal-panel padding */
  width: var(--modal-poster-w);
  height: 30px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0 0.55rem;
  border: 1px solid rgba(245, 197, 24, 0.55);
  border-radius: 8px;
  background: linear-gradient(180deg, var(--accent) 0%, #d8a912 100%);
  color: var(--accent-ink) !important;
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow:
    0 4px 14px rgba(245, 197, 24, 0.22),
    0 0 0 1px rgba(0, 0, 0, 0.18) inset !important;
  transition: filter 0.18s var(--ease), transform 0.08s var(--ease), box-shadow 0.18s var(--ease);
}
.modal-reroll:hover {
  filter: brightness(1.06);
  box-shadow:
    0 6px 18px rgba(245, 197, 24, 0.32),
    0 0 0 1px rgba(0, 0, 0, 0.18) inset !important;
}
.modal-reroll:active { transform: translateY(1px); }
.modal-reroll:focus-visible {
  outline: 2px solid #fff8d2;
  outline-offset: 2px;
}
.modal-reroll[hidden] { display: none !important; }

/* When Roll-again is visible (surprise pick), shift the modal header
   down by the button's height + a small breathing gap so the poster
   sits flush beneath it. Sibling combinator works because the reroll
   button precedes .modal-header in DOM order. */
.modal-reroll:not([hidden]) ~ .modal-header {
  margin-top: calc(30px - 0.4rem);
}

.modal-header {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

.modal-poster {
  width: 120px;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  position: relative;
  overflow: hidden;
  flex-shrink: 0;
}
.modal-poster img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}

.modal-heading {
  flex: 1 1 240px;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
}
.modal-heading h2 {
  font-size: 1.55rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
}
.modal-subtitle {
  margin: 0;
  color: var(--muted);
  font-size: 0.92rem;
  letter-spacing: 0.005em;
}
.modal-shapes { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-top: 0.15rem; }
.modal-shapes:empty { display: none; }
.modal-stats {
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.005em;
}

.modal-overview {
  margin: 0;
  color: var(--text);
  font-size: 0.95rem;
  line-height: 1.55;
  opacity: 0.92;
}
.modal-overview:empty { display: none; }

/* Primary actions cluster — sits inside .modal-heading so the action
   is tied visually to the show/season identity rather than buried in
   the row of external-link buttons below. */
.modal-primary-actions {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.35rem;
}

/* Cast strip — horizontal-scrolling row of head-and-shoulders cards
   showing the top-billed cast. Wrapper is [hidden] by default and
   only revealed when meta.cast has entries. */
.cast-section[hidden] {
  display: none;
}
.cast-section {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.cast-list {
  display: flex;
  gap: 0.6rem;
  overflow-x: auto;
  padding: 0.25rem 0 0.5rem;
  margin: 0;
  list-style: none;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
}
.cast-list::-webkit-scrollbar { height: 6px; }
.cast-list::-webkit-scrollbar-track { background: transparent; }
.cast-list::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.18);
  border-radius: 999px;
}
.cast-card {
  flex: 0 0 90px;
  scroll-snap-align: start;
}

/* Inner container — either an <a> (clickable, links to TMDB person
   page) or a plain <div> fallback for older cache entries with no
   person id. Same layout either way so the card looks identical. */
.cast-card-inner {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.35rem;
  text-align: center;
  text-decoration: none !important;
  color: inherit;
  transition: transform 0.18s var(--ease);
}
.cast-card-inner:hover {
  text-decoration: none !important;
}
a.cast-card-inner:hover {
  transform: translateY(-2px);
}
a.cast-card-inner:hover .cast-photo {
  box-shadow: 0 0 0 1px rgba(245, 197, 24, 0.45) inset,
              0 6px 16px rgba(0, 0, 0, 0.4);
}
a.cast-card-inner:hover .cast-name {
  color: var(--accent);
}
a.cast-card-inner:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 12px;
}
.cast-photo {
  transition: box-shadow 0.18s var(--ease);
}
.cast-photo {
  width: 90px;
  aspect-ratio: 2 / 3;
  border-radius: 10px;
  background: var(--surface-2);
  position: relative;
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06) inset;
}
.cast-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.cast-photo-fallback {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.45);
  background: linear-gradient(135deg, #1a2030 0%, #14182265 100%);
}
.cast-name {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text);
  line-height: 1.2;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.cast-character {
  font-size: 0.72rem;
  color: var(--muted);
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.modal-section {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted);
  font-weight: 600;
  margin: 0.2rem 0 -0.3rem;
}


.modal-episodes {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.35rem;
}
.modal-episodes li {
  display: grid;
  grid-template-columns: 3rem 1fr auto;
  gap: 0.75rem;
  align-items: center;
  padding: 0.55rem 0.85rem;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-variant-numeric: tabular-nums;
  font-size: 0.9rem;
  position: relative;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}
/* Stretched link: invisible <a> covering the whole row so clicking anywhere
   opens the episode on IMDb. The inner spans sit on top so text selection
   and tooltips still work. */
.modal-episodes .ep-link {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  z-index: 1;
}
.modal-episodes .ep-number,
.modal-episodes .ep-name,
.modal-episodes .ep-meta {
  position: relative;
  z-index: 2;
  pointer-events: none;
}
.modal-episodes li.has-link:hover,
.modal-episodes li.has-link:focus-within {
  border-color: var(--accent-strong, rgba(245, 197, 24, 0.5));
  background: rgba(245, 197, 24, 0.06);
}
.modal-episodes .ep-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.modal-episodes .ep-number {
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.modal-episodes li.ep-special {
  border-color: rgba(192, 132, 252, 0.4);
  background: rgba(192, 132, 252, 0.06);
}
.modal-episodes li.ep-special .ep-number {
  color: #c084fc;
}
.modal-episodes .ep-name {
  color: var(--text);
  font-weight: 500;
  font-size: 0.92rem;
  letter-spacing: -0.005em;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* When the build pipeline didn't include episode titles, hide the
   empty slot entirely so the layout collapses to ep# | rating cleanly. */
.modal-episodes .ep-name:empty { display: none; }
.modal-episodes .ep-meta {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.1rem;
  line-height: 1.15;
}
.modal-episodes .ep-rating {
  color: var(--accent);
  font-weight: 700;
  font-size: 1rem;
}
.modal-episodes .ep-votes  {
  color: var(--muted-2);
  font-size: 0.74rem;
  white-space: nowrap;
}
.modal-episodes .ep-runtime {
  color: var(--muted-2);
  font-size: 0.74rem;
  white-space: nowrap;
}

.modal-actions {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  flex-wrap: wrap;
}
/* Top-of-modal action row: a thin divider underneath separates it from the
 * descriptive content (overview / episodes) that follows. Extra vertical
 * padding gives the buttons breathing room. */
.modal-actions-top {
  margin: 0.25rem 0 0.1rem;
  padding: 0.75rem 0 1.1rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
/* Hierarchy inside .modal-actions: each modal's primary action (watch
 * on season-modal, compare on show-modal) gets an accent surface so it
 * reads as the obvious primary button. Secondary buttons stay ghost.
 * IMDb anchor sits flush-right via margin-left:auto. */
.modal-actions .watch-btn,
.modal-actions .compare-btn {
  background: var(--accent-soft);
  border: 1px solid var(--accent-strong);
  color: var(--accent) !important;
  font-weight: 700;
}
.modal-actions .watch-btn:hover,
.modal-actions .compare-btn:hover {
  background: rgba(245, 197, 24, 0.18);
  border-color: rgba(245, 197, 24, 0.5);
}
/* "Already added" state — green to signal the success-of-state, same
 * pattern the watch button uses when a season is marked watched. */
.modal-actions .watch-btn.is-watched,
.modal-actions .compare-btn.is-in-compare {
  background: rgba(74, 222, 128, 0.16);
  border-color: rgba(74, 222, 128, 0.45);
  color: var(--good) !important;
}
.modal-imdb { margin-left: auto; }

@media (prefers-reduced-motion: reduce) {
  .skeleton, .skeleton .skeleton-block, .skeleton .skeleton-line {
    animation: none;
  }
  .card, .row { transition: none; }
  /* The mobile drawer's slide-up and backdrop fade-in can feel abrupt
     for motion-sensitive users — disable both and just show/hide. */
  .advanced-backdrop,
  details.advanced[open] { animation: none; }
}

/* ---------- Content-width alignment guard ----------
   Every top-level block under <main class="page"> — the unified toolbar,
   the expanded More-filters panel, the stats bar, the results count, and
   the list/grid of results — has to share the same horizontal box so the
   left/right border edges stack vertically. Without this, intrinsic
   sizing from a <select>'s longest option, a long row title, or the
   advanced-panel's grid can grow a single block past .page's content
   area and the page reads as misaligned.

   The fix has three parts:
   1. Lock each block to width:100% inside .page's content box with
      box-sizing:border-box so border+padding live INSIDE the width.
   2. Set min-width:0 so flex/grid intrinsic minimums don't expand the
      box (default min-width:auto would let a wide <select> push a track
      out, even with minmax(0, 1fr) on the column).
   3. Belt-and-braces on the advanced-panel form controls themselves —
      their label parents already have min-width:0, but locking the
      input/select to min-width:0 guarantees they can't drag the 4-up
      grid wider than its container on any browser. */
.filter-row.primary,
.label-filters,
.advanced,
.advanced[open],
.stats-bar,
.meta,
.results-grid,
.row {
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  margin-left: 0;
  margin-right: 0;
}

.advanced .filter-row input,
.advanced .filter-row select {
  min-width: 0;
}

/* ============================================================================
   Mobile redesign — phones-first refinements that make the app feel
   intentional on a small screen, not a squeezed desktop layout.

   Targeted breakpoints:
     - 760px — large phones / small tablets in portrait
     - 600px — phone portrait (the main mobile experience)
     - 430px — small phones (iPhone SE / mini class)

   Anti-overflow guarantees:
   - the page never scrolls horizontally
   - long titles, chip labels, and poster captions wrap or ellipsis
   - the shape-chip strip wraps instead of forcing a horizontal scroll
   ========================================================================= */

/* No horizontal scroll, ever — long unbreakable text inside a control
   should reflow or ellipsis instead of pushing the viewport wide. */
html, body { overflow-x: hidden; }

/* Backdrop element for the mobile "advanced filters" drawer. Inserted
   into <body> by app.js; only visible while the drawer is open on a
   phone-sized viewport. Sits above the site header (z 10001) so the
   whole screen dims behind the drawer panel (z 10500). */
.advanced-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 10400;
  background: rgba(4, 6, 12, 0.62);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  animation: backdrop-fade-in 0.18s var(--ease);
}
body.advanced-drawer-open .advanced-backdrop { display: block; }

/* Body scroll-lock — iOS-safe. The `position: fixed` + width preserve the
   visual position while preventing the page from scrolling under the
   drawer. JS captures and restores scroll position; the top offset is
   set as a CSS custom property at the moment the drawer opens. */
body.advanced-drawer-open {
  position: fixed;
  top: var(--scroll-lock-y, 0);
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden;
}

@keyframes backdrop-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes drawer-slide-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}


/* ----------------------------------------------------------------------------
   ≤ 760px — large phones and small tablets
   ------------------------------------------------------------------------- */

@media (max-width: 760px) {
  /* Side spacing is handled by .page's max-width: calc(100% - 6rem) already,
     same as the footer .inner. Only the bottom padding gets compacted here. */
  .page { padding: 0 0 1.5rem; }

  /* Compact hero — claim less vertical space so results show above fold. */
  .hero { padding: 1.5rem 0 0.35rem; }
  .hero h1 { font-size: clamp(1.75rem, 8vw, 2.4rem); }
  .tagline { font-size: 0.92rem; max-width: 100%; line-height: 1.4; }

  /* Shape chips: tighter strip, but still readable and tappable. */
  .shapes {
    padding: 0.75rem 0 0.25rem;
    gap: 0.4rem 0.4rem;
  }
  .shape-chip,
  .shape-chip:hover {
    height: 36px;
    padding: 0 0.8rem;
    font-size: 0.8rem;
  }
  .shape-count { height: 18px; font-size: 0.68rem; }

  /* Filters block: extra breathing room between sections so the toolbar,
     label pills, and shape strip don't all crowd against each other. */
  .filters { padding: 0.75rem 0 0.25rem; }

  /* Primary toolbar — turn into a flexible two-row layout:
       row 1: search (full width)
       row 2: view-toggle + surprise (side by side)
     Inputs sized for thumb taps. */
  .filter-row.primary {
    --ctrl-h: 44px;
    padding: 4px 4px 4px;
    gap: 0.35rem;
    border-radius: 12px;
    align-items: stretch;
  }
  .filter-row.primary .search { flex: 1 1 100% !important; order: 1; }
  .filter-row.primary .search input {
    font-size: 16px;          /* prevents iOS auto-zoom on focus */
    padding-left: 2.4rem;
  }
  .filter-row.primary .view-toggle {
    order: 2;
    flex: 0 0 auto;
    align-self: stretch;
  }
  .filter-row.primary .view-btn { width: 2.6rem; font-size: 1rem; }
  .filter-row.primary .surprise {
    order: 3;
    flex: 1 1 auto;
    font-size: 0.88rem;
    padding: 0 1rem;
  }

  /* Quick label filters — keep glass surface but stack each group on its
     own line so the captions never collide with their pills. */
  .label-filters {
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    padding: 0.55rem 0.65rem;
    margin-bottom: 0.75rem;
  }
  .label-group {
    flex-wrap: wrap;
    gap: 0.25rem 0.35rem;
    padding: 0.2rem 0;
  }
  .label-group + .label-group {
    border-top: 1px solid rgba(255, 255, 255, 0.045);
    padding-top: 0.45rem;
    margin-top: 0.1rem;
  }
  .label-group-name {
    flex: 0 0 100%;
    margin: 0 0 0.15rem;
    font-size: 0.6rem;
  }
  .label-chip {
    height: 36px;
    padding: 0 0.85rem;
    font-size: 0.82rem;
    border-radius: 8px;
  }
  .label-chip + .label-chip { margin-left: 0; }

  /* List rows already shed the curve here — also bump tap height. */
  .row-watch { width: 2.25rem; height: 2.25rem; }

  /* Stats bar: shorter font + larger gap-rhythm. */
  .stats-bar {
    padding: 0.65rem 0.9rem;
    font-size: 0.8rem;
    gap: 0.5rem 1.25rem;
  }
  .stats-bar .progress { flex-basis: 100%; }

  /* Pager — tighter, taller tap targets. */
  .pager { gap: 0.3rem; }
  .pager .page-btn {
    padding: 0 0.55rem;
    min-width: 40px;
    height: 40px;
    font-size: 0.85rem;
  }

  /* Modal becomes a bottom sheet on phones — full width, rounded only
     at the top, anchored to the bottom of the viewport with safe-area
     padding so the action buttons don't sit under the home indicator. */
  .modal {
    padding: 0;
    align-items: flex-end;
  }
  .modal-panel {
    width: 100%;
    max-width: 100%;
    max-height: calc(100vh - 1rem);
    max-height: calc(100dvh - 1rem);
    border-radius: 18px 18px 0 0;
    border-bottom: 0;
    padding: 1rem 1rem 1.1rem;
    padding-bottom: max(1rem, env(safe-area-inset-bottom));
    gap: 0.85rem;
    --modal-poster-w: 92px;
  }
  .modal-panel::before { border-radius: 18px 18px 0 0; }
  .modal-poster { width: var(--modal-poster-w); }
  .modal-close {
    margin-bottom: -1.5rem;
    /* Float the close button over the header — it stays reachable at
       the top of the sheet without consuming a full row of padding. */
  }
  .modal-reroll {
    top: 1rem;
    left: 1rem;
    height: 28px;
    font-size: 0.72rem;
  }
  .modal-reroll:not([hidden]) ~ .modal-header {
    margin-top: calc(28px - 0.4rem);
  }
  .modal-header { gap: 0.85rem; }
  .modal-heading h2 { font-size: 1.35rem; line-height: 1.2; }
  .modal-curve { height: 150px; padding: 0.4rem; }
  .modal-episodes li {
    grid-template-columns: 2.6rem 1fr auto;
    gap: 0.6rem;
    padding: 0.55rem 0.7rem;
    font-size: 0.88rem;
  }

  /* Action buttons stack to be each full-width tappable bars. */
  .modal-actions {
    flex-direction: column;
    align-items: stretch;
    gap: 0.55rem;
  }
  .modal-actions .btn {
    width: 100%;
    height: 44px;
  }
  .modal-imdb { margin-left: 0; }
}

/* ----------------------------------------------------------------------------
   ≤ 600px — phone portrait (the main mobile experience)

   Major changes here:
   - Result cards in GRID view flip to full-width horizontal cards
   - The "More filters" details element becomes a bottom-sheet drawer
   - Tap targets uniformly meet the 40-44px guidance
   ------------------------------------------------------------------------- */

@media (max-width: 600px) {

  /* --- Cards in grid view: full-width horizontal layout ---------------- */

  .results-grid {
    grid-template-columns: 1fr;
    gap: 0.7rem;
  }
  .results-grid.list-view { gap: 0.55rem; }

  /* Mobile cards: two-column grid so the poster sits on the left next to
     the text, but the sparkline + stats span the full card width below.
     Previously the card was a horizontal flex row and the curve was
     trapped inside .card-body — visually appearing as a thin chart strip
     squeezed into the right-hand text column. */
  .card {
    display: grid;
    grid-template-columns: 104px 1fr;
    grid-template-areas:
      "poster body"
      "curve  curve"
      "stats  stats";
    align-items: stretch;
    min-height: auto;
  }
  .card-poster {
    grid-area: poster;
    flex: none;
    width: 104px;
    aspect-ratio: 2 / 3;
  }
  .card-body {
    grid-area: body;
    flex: none;
    min-width: 0;
    padding: 0.7rem 0.85rem 0.6rem;
    row-gap: 0.45rem;
  }
  .card .curve-wrap { grid-area: curve; }
  .card-stats {
    grid-area: stats;
    gap: 0.55rem;
    font-size: 0.72rem;
    padding: 0.5rem 0.85rem 0.7rem;
  }
  .card-head { gap: 0.4rem; }
  .card-title {
    font-size: 0.98rem;
    -webkit-line-clamp: 2;
  }
  .card-season { font-size: 0.74rem; }
  .card-meta { font-size: 0.72rem; gap: 0.45rem; }
  .card-shapes { gap: 0.25rem; }
  .card .curve { height: 48px; }
  /* Watch toggle is roomier so it's easier to thumb-tap on the corner. */
  .card .watch-toggle {
    width: 2.25rem;
    height: 2.25rem;
    top: 0.4rem;
    right: 0.4rem;
  }

  /* List view rows — stack the sparkline onto its own full-width row
     below poster+text, same pattern as the mobile season cards. The
     desktop 4-col layout (`56px 1fr 240px 36px`) squeezed the curve
     into a 40px sliver because the mobile rule only defined 3 columns
     and grid auto-flow trapped the spark in the 40px watch slot. */
  .row {
    grid-template-columns: 56px 1fr 40px;
    grid-template-areas:
      "poster main  watch"
      "curve  curve curve";
    column-gap: 0.85rem;
    row-gap: 0.55rem;
    padding: 0.7rem 0.85rem;
    align-items: start;
  }
  .row-poster        { grid-area: poster; }
  .row-main          { grid-area: main; }
  .row .curve-wrap   { grid-area: curve; }
  .row-watch         { grid-area: watch; align-self: center; }
  .row-curve { height: 48px; }
  .row-watch {
    width: 2.5rem;
    height: 2.5rem;
  }

  /* --- Shape chips: keep the descriptions hidden (already), but pad the
       tap area a touch more so they meet ~38-40px. -------------------- */
  .shape-chip,
  .shape-chip:hover {
    height: 38px;
    padding: 0 0.9rem;
    font-size: 0.82rem;
  }

  /* --- Advanced "More filters" — becomes a slide-up bottom sheet ----- */

  /* The trigger button: when CLOSED, render as a full-width pill so it
     reads as a clear action, not a tiny inline link. */
  .advanced summary {
    width: 100%;
    height: 44px;
    margin: 0;
    padding: 0 1rem;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    color: var(--text);
    font-size: 0.78rem;
    letter-spacing: 0.1em;
    justify-content: center;
  }
  .advanced summary:hover { background: rgba(255, 255, 255, 0.06); }

  /* When OPEN: the entire <details> becomes a fixed bottom sheet. The
     summary stays at the top of the sheet (sticky) and acts as the
     close handle ("×" replaces "+" via the existing transform). */
  details.advanced[open] {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: 100%;
    /* 100dvh accounts for the iOS URL bar — 100vh over-counts and the
       sheet would extend below the visible viewport. */
    max-height: 85vh;
    max-height: 85dvh;
    z-index: 10500;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    background:
      linear-gradient(180deg, #181c26 0%, #12151d 100%);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-bottom: 0;
    border-radius: 18px 18px 0 0;
    box-shadow:
      0 -20px 60px rgba(0, 0, 0, 0.55),
      0 -2px 8px rgba(0, 0, 0, 0.35);
    animation: drawer-slide-up 0.25s var(--ease);
  }
  details.advanced[open] > summary {
    position: sticky;
    top: 0;
    z-index: 2;
    width: 100%;
    height: 52px;
    margin: 0;
    padding: 0 1.1rem;
    background: rgba(18, 21, 29, 0.92);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    border: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 18px 18px 0 0;
    color: var(--text);
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    justify-content: space-between;
  }
  details.advanced[open] > summary::before { order: 2; font-size: 1.4rem; }
  details.advanced[open] > summary:hover { background: rgba(24, 28, 38, 0.95); }

  /* A subtle drag-handle bar at the very top of the sheet — signals
     "this is a sheet you can dismiss," even though we close via tap. */
  details.advanced[open] > summary::after {
    content: '';
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.15);
  }

  /* Strip the open-panel chrome the desktop layout adds — the fixed
     sheet provides its own background, border, and rounding. */
  .advanced[open] {
    background: transparent;
    border: 0;
    box-shadow: none;
  }
  .advanced[open] .filter-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.7rem 0.7rem;
    padding: 0.9rem 1.1rem 0;
  }
  .advanced[open] .filter-row input,
  .advanced[open] .filter-row select {
    height: 44px;
    font-size: 16px;          /* prevents iOS zoom on focus */
    border-radius: 9px;
  }
  .genre-row {
    padding: 1rem 1.1rem;
    margin-top: 0.5rem;
    gap: 0.4rem 0.4rem;
  }
  .genre-row::before { font-size: 0.62rem; margin-bottom: 0.3rem; }
  .genre-chip,
  .genre-chip:hover {
    height: 38px;
    padding: 0 0.95rem;
    font-size: 0.8rem;
  }
  .filter-actions {
    margin: 0;
    padding: 0.85rem 1.1rem 1.1rem;
    padding-bottom: max(0.85rem, env(safe-area-inset-bottom));
    border-top: 1px solid rgba(255, 255, 255, 0.075);
    justify-content: stretch;
  }
  .filter-actions .btn,
  .filter-actions .btn:hover {
    width: 100%;
    height: 40px;
    font-size: 0.75rem;
    color: var(--accent) !important;
  }

  /* --- Search dropdown: aligned to the toolbar but full-bleed inside
       the screen edges with a slightly taller row. ------------------ */
  .search-suggestions {
    max-height: 60vh;
    border-radius: 12px;
  }
  .search-suggestion {
    min-height: 48px;
    padding: 0.55rem 0.7rem;
    font-size: 0.92rem;
  }
  .search-suggestion .ss-poster {
    flex: 0 0 36px;
    width: 36px;
    height: 54px;
  }
}

/* ----------------------------------------------------------------------------
   ≤ 430px — small phones (iPhone SE / mini / Pixel 4a)
   ------------------------------------------------------------------------- */

@media (max-width: 430px) {
  /* Tighten the side gutter on the smallest phones to match the footer's
     <480px breakpoint (max-width: calc(100% - 3rem)). */
  .page { padding: 0 0 1.25rem; max-width: calc(100% - 3rem); }

  .hero { padding: 1.1rem 0 0.2rem; }
  .hero h1 { font-size: clamp(1.5rem, 8vw, 1.95rem); }
  .tagline { font-size: 0.85rem; }

  /* Narrower cards: poster shrinks just enough that the title can fit
     two lines of ~22 characters without ellipsising mid-word. */
  .card { min-height: 140px; }
  .card-poster { flex: 0 0 92px; width: 92px; }
  .card-body { padding: 0.6rem 0.7rem 0.7rem; gap: 0.4rem; }
  .card-title { font-size: 0.92rem; }
  .card .curve { height: 42px; }
  /* Votes column is the lowest-value stat in the trio; drop it on the
     tightest screens so climb + avg get full room. */
  .card .stat-votes { display: none; }

  /* List view: drop the season label inline (year stays). */
  .row-poster { width: 48px; }
  .row { grid-template-columns: 48px 1fr 40px; }
  .row-curve { height: 40px; }

  /* Drawer rows go single-column at this width so the input labels and
     values both have full breathing room. */
  .advanced[open] .filter-row {
    grid-template-columns: 1fr;
    gap: 0.55rem;
  }

  /* Modal sheet: slightly taller and tighter so episodes show in full. */
  .modal-panel {
    --modal-poster-w: 84px;
    padding: 0.9rem 0.85rem 1rem;
    padding-bottom: max(0.85rem, env(safe-area-inset-bottom));
  }
  .modal-heading h2 { font-size: 1.2rem; }
  .modal-subtitle, .modal-stats { font-size: 0.8rem; }
  .modal-curve { height: 130px; }
  .modal-episodes li {
    grid-template-columns: 2.4rem 1fr auto;
    padding: 0.5rem 0.6rem;
    font-size: 0.85rem;
  }

  /* Pager: drop ellipsis tokens and lean on prev/next for navigation. */
  .pager .page-ellipsis { display: none; }
  .pager .page-btn { min-width: 38px; height: 38px; font-size: 0.8rem; padding: 0 0.45rem; }

  /* Stats bar collapses to a stacked, scannable line list. */
  .stats-bar {
    gap: 0.4rem 0;
    flex-direction: column;
    align-items: stretch;
  }
  .stats-bar > span { justify-content: space-between; }
}

/* ---------- Footer "What's new" chip + changelog popover ---------- */

.footer-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
}
.footer-meta-text { color: var(--muted-2); }
.footer-meta-sep { color: var(--border-strong); }

/* Footer "What's new" pill. main.css applies height/line-height/color !important
 * to every button on the site, so most properties here need !important to win. */
.whats-new-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  height: auto !important;
  line-height: 1.2 !important;
  padding: 0.3rem 0.7rem 0.3rem 0.75rem !important;
  border-radius: 999px;
  font-size: 0.78rem !important;
  font-weight: 600;
  color: var(--text) !important;
  background: var(--surface-2);
  border: 1px solid var(--accent-strong);
  box-shadow: none !important;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.05s ease;
}
.whats-new-chip:hover {
  background: rgba(245, 197, 24, 0.14);
  border-color: var(--accent);
  color: var(--text) !important;
}
.whats-new-chip:active { transform: translateY(1px); }
.whats-new-chip:focus-visible {
  outline: none;
  box-shadow: var(--accent-glow) !important;
}
.whats-new-chip-counts { color: var(--muted); font-weight: 500; }
.whats-new-chip-added { color: var(--good); font-weight: 700; }
.whats-new-chip-removed { color: var(--danger); font-weight: 700; }
.whats-new-chip-cta {
  color: var(--accent);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.68rem;
  padding-left: 0.55rem;
  border-left: 1px solid var(--border-strong);
}
.whats-new-chip-caret { color: var(--accent); font-size: 0.7rem; }

/* Modal panel — slightly tighter than the season modal because it's a
 * read-only summary, no media or curve. */
.changelog-panel {
  max-width: 560px;
  gap: 1.1rem;
}
.changelog-header { display: flex; flex-direction: column; gap: 0.25rem; }
.changelog-header h2 { margin: 0; font-size: 1.35rem; }
.changelog-panel .modal-subtitle { color: var(--muted); font-size: 0.85rem; }

.changelog-section { display: flex; flex-direction: column; gap: 0.55rem; }
.changelog-section .modal-section {
  margin: 0;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-2);
  font-weight: 600;
}

/* "At a glance" totals: render as a horizontal stat strip with separators. */
#changelogTotals {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
  padding: 0.65rem 0.85rem;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.changelog-stat {
  font-size: 0.88rem;
  color: var(--muted);
}
.changelog-stat strong { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }

.changelog-shape-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.changelog-shape-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.18rem 0.55rem;
  border-radius: 999px;
  font-size: 0.74rem;
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--text);
}
.changelog-shape-pill.is-up {
  border-color: rgba(52, 211, 158, 0.35);
  background: var(--good-soft);
  color: var(--good);
}
.changelog-shape-pill.is-down {
  border-color: rgba(248, 113, 113, 0.32);
  background: rgba(248, 113, 113, 0.1);
  color: var(--danger);
}

/* Lists: a single softly-bordered card containing tight rows. Only the
 * Added list gets a max-height — Dropped + Swings are usually short. */
.changelog-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  overflow: hidden;
}
#changelogAddedList { max-height: 260px; overflow-y: auto; }
.changelog-list li {
  padding: 0;
  margin: 0;
  border-bottom: 1px solid var(--border);
}
.changelog-list li:last-child { border-bottom: 0; }
.changelog-list-muted li {
  padding: 0.5rem 0.85rem;
  font-size: 0.88rem;
  color: var(--muted);
}

/* Button rows inside lists. main.css's site-wide button defaults — 3.25rem
 * height, gray inset border, gray !important text — would otherwise paint
 * each row as a tall hollow button. Override all of them. */
.changelog-item-link {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  height: auto !important;
  line-height: 1.35 !important;
  padding: 0.5rem 0.85rem !important;
  background: transparent !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  color: var(--text) !important;
  text-align: left;
  font: inherit;
  font-size: 0.88rem !important;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.changelog-item-link:hover { background: var(--surface-3) !important; color: var(--text) !important; }
.changelog-item-link:focus-visible {
  outline: none;
  background: var(--surface-3) !important;
  box-shadow: inset 2px 0 0 var(--accent) !important;
}

.changelog-swing-delta {
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  color: var(--muted);
  white-space: nowrap;
}
.changelog-swing-delta.is-up { color: var(--good); }
.changelog-swing-delta.is-down { color: var(--danger); }

@media (max-width: 600px) {
  .changelog-panel {
    padding: 1.5rem 1.25rem 1.25rem;
    gap: 0.9rem;
  }
  .changelog-header h2 { font-size: 1.15rem; }
  .changelog-stat { font-size: 0.82rem; }
  #changelogAddedList { max-height: 220px; }
  .whats-new-chip {
    padding: 0.25rem 0.6rem !important;
    font-size: 0.72rem !important;
    gap: 0.4rem;
  }
  .whats-new-chip-cta { padding-left: 0.4rem; font-size: 0.62rem; }
}

/* ---- Mood presets (NUI-5) ---- */

.mood-presets {
  /* Tighter bottom padding than the hero above it, so the mood rail reads
     as the top of the filter stack rather than a separate hero section. */
  padding: 0.5rem 0 clamp(1.75rem, 3vw, 2.5rem);
  max-width: 48rem;
  margin: 0 auto;
}

/* Section label flanked by hairline rules so "Explore by mood" reads
   as a proper section header rather than floating text above chips. */
.mood-presets-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.85rem;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-2);
  margin-bottom: 1.75rem;
  text-align: center;
}
.mood-presets-title::before,
.mood-presets-title::after {
  content: '';
  flex: 0 1 4rem;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.12), transparent);
}

/* The mood strip is a <details> that ships open. On desktop it reads as a
   static heading + chips (the summary is not interactive); on mobile the
   summary becomes a collapse toggle (rules live in the max-width 600px
   block near the end of this file). app.js syncs the open state to the
   viewport, since a closed <details> hides its content at the UA level
   and child CSS cannot force it visible. */
.mood-collapsible > summary::-webkit-details-marker { display: none; }
.mood-collapsible > summary::marker { content: ''; }
@media (min-width: 601px) {
  .mood-collapsible > summary {
    cursor: default;
    pointer-events: none;
    user-select: none;
  }
  .mood-toggle-chevron { display: none; }
}

.mood-preset-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
}

/* Same visual language as .shape-chip — height, border, surface,
   hover gold-tint, scale on hover. */
.mood-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  height: 30px;
  padding: 0 0.85rem;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 999px;
  color: var(--text) !important;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.005em;
  text-decoration: none !important;
  white-space: nowrap;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02) inset;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease), transform 0.18s var(--ease),
              box-shadow 0.18s var(--ease);
}

.mood-chip:hover {
  background: rgba(255, 255, 255, 0.075);
  border-color: var(--border-strong);
  color: rgba(255, 255, 255, 0.98) !important;
  text-decoration: none !important;
  transform: translateY(-1px);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02) inset,
              0 4px 12px rgba(0, 0, 0, 0.25);
}

/* Active preset: a hairline accent border and an accent icon carry the
   selected state — the label stays white and the surface stays dark, so a
   pressed mood reads clearly without a heavy yellow fill. */
.mood-chip[aria-pressed="true"],
.mood-chip[aria-pressed="true"]:hover {
  background: rgba(245, 197, 24, 0.05);
  border-color: rgba(245, 197, 24, 0.5);
  color: var(--text) !important;
}
.mood-chip[aria-pressed="true"] .mood-chip-icon {
  color: var(--accent);
}

.mood-chip-icon {
  font-size: 0.78rem;
  color: rgba(231, 233, 238, 0.55);
  font-weight: 700;
  transition: color 0.18s var(--ease);
}
.mood-chip:hover .mood-chip-icon {
  color: rgba(231, 233, 238, 0.85);
}

.mood-chip-label {
  font-weight: 600;
}

/* Tertiary count badge — mirrors .shape-count so the chip systems
   read as one family. Hidden when empty so a chip without a count
   doesn't grow a phantom pill. */
.mood-chip-count {
  display: inline-flex;
  align-items: center;
  height: 17px;
  padding: 0 0.45rem;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 999px;
  font-size: 0.68rem;
  color: rgba(231, 233, 238, 0.6);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  margin-left: 0.1rem;
}
.mood-chip-count:empty { display: none; }
.mood-chip:hover .mood-chip-count {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(231, 233, 238, 0.75);
}
.mood-chip[aria-pressed="true"] .mood-chip-count {
  background: rgba(245, 197, 24, 0.14);
  color: var(--accent);
}

/* ---- Mood overflow toggle (mirrors the shape bar's "More shapes") ---- */

.moods-hidden {
  display: none !important;
}

.mood-more-btn[hidden] {
  display: none;
}

/* Neutral ghost toggle — quieter than the chips it reveals, so the rail's
   hierarchy stays: active mood > moods > overflow control. The :hover /
   :hover:active twins defend against the site theme's red button:hover
   (same idiom as .shapes-more-btn / .shape-chip). */
.mood-more-btn,
.mood-more-btn:hover,
.mood-more-btn:hover:active {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 30px;
  padding: 0 0.85rem;
  background: transparent;
  border: 1px dashed rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  color: rgba(231, 233, 238, 0.75) !important;
  box-shadow: none !important;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease);
}

.mood-more-btn:hover,
.mood-more-btn:hover:active {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.3);
  color: rgba(255, 255, 255, 0.95) !important;
}

.mood-more-count {
  display: inline-flex;
  align-items: center;
  height: 17px;
  padding: 0 0.45rem;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 999px;
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
  color: rgba(231, 233, 238, 0.6);
}
.mood-more-count:empty { display: none; }

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

/* ---- Shape overflow toggle (UI-3) ---- */

.shapes-hidden {
  display: none !important;
}

.shapes-more-btn[hidden] {
  display: none;
}

/* The :hover/:hover:active selectors are listed alongside the base rule on
   purpose — the site theme (assets/css/main.css) paints every button red on
   hover (`button:hover { color: #ce1b28 !important; box-shadow: inset ... }`)
   and gives it a red fill while pressed (`button:hover:active`). Touch
   devices keep :hover stuck on the button after a tap, so without these the
   label flashed red until the next tap elsewhere. Same idiom as .shape-chip. */
.shapes-more-btn,
.shapes-more-btn:hover,
.shapes-more-btn:hover:active {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 30px;
  padding: 0 0.7rem;
  background: rgba(245, 197, 24, 0.08);
  border: 1px solid rgba(245, 197, 24, 0.28);
  border-radius: 999px;
  /* White label like the other shape chips; the yellow border/background
     tint (and the count badge) stay as the "expandable" accent. */
  color: #FFFFFF !important;
  box-shadow: none !important;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease);
}

.shapes-more-btn:hover,
.shapes-more-btn:hover:active {
  background: rgba(245, 197, 24, 0.15);
  border-color: rgba(245, 197, 24, 0.45);
}

.shapes-more-count {
  display: inline-flex;
  align-items: center;
  height: 17px;
  padding: 0 0.4rem;
  background: rgba(245, 197, 24, 0.18);
  border-radius: 999px;
  font-size: 0.68rem;
  color: var(--accent);
}
.shapes-more-count:empty { display: none; }

/* ---- Modal drift note (NUI-3) ---- */

.modal-drift-note {
  font-size: 0.88rem;
  color: var(--muted);
  background: rgba(245, 197, 24, 0.06);
  border: 1px solid rgba(245, 197, 24, 0.18);
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.9rem;
  margin: 0;
}

/* ---- Data freshness (NUI-4) ---- */

.freshness-row {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
  font-size: 0.875rem;
  padding: 0.25rem 0;
}

.freshness-label {
  color: var(--muted);
  font-size: 0.82rem;
  min-width: 12rem;
}

.freshness-value {
  color: var(--text);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}

#changelogFreshness h3 {
  margin-bottom: 0.5rem;
}

/* ---- Shape touch tooltip (UI-4) ---- */

.shape-touch-tooltip {
  position: absolute;
  z-index: 9999;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-xs);
  color: var(--text);
  font-size: 0.82rem;
  font-family: inherit;
  padding: 0.4rem 0.7rem;
  pointer-events: none;
  max-width: 200px;
  box-shadow: var(--shadow-lift);
  animation: tooltip-in 0.12s var(--ease) both;
}

@keyframes tooltip-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---- Episode hover tip on modal curve (UI-5) ---- */

.curve-hover-tip {
  position: absolute;
  z-index: 10;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-xs);
  color: var(--text);
  font-size: 0.8rem;
  font-family: inherit;
  padding: 0.35rem 0.65rem;
  pointer-events: none;
  white-space: nowrap;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  box-shadow: var(--shadow-lift);
  transition: opacity 0.1s;
}

.curve-hover-tip[hidden] {
  display: none;
}

/* ---- Modal curve shape annotations (UI-2) ---- */

.curve-annotation-label {
  font-size: 10px;
  fill: rgba(245, 197, 24, 0.95);
  /* Dark halo so the yellow label stays readable when it lands over the
     yellow area-fill below the curve (e.g. "Dip" sitting under a rebound
     trough, where yellow-on-yellow would otherwise erase it). */
  stroke: rgba(11, 13, 18, 0.85);
  stroke-width: 3px;
  paint-order: stroke fill;
  stroke-linejoin: round;
  font-family: system-ui, sans-serif;
  font-weight: 600;
  pointer-events: none;
}

.curve-annotation-arrow {
  stroke: rgba(245, 197, 24, 0.75);
  stroke-width: 1.5;
  fill: none;
}

.curve-annotation-arrowhead {
  fill: rgba(245, 197, 24, 0.75);
}

.curve-annotation-bracket {
  stroke: rgba(245, 197, 24, 0.5);
  stroke-width: 1.5;
  stroke-dasharray: 4 2;
}

/* ===== UI improvements: chip bar, sticky filter, share canvas,
   sparkline min/max labels, annotation, shortcut popover ===== */

/* #1 Active-filter chip bar */
.active-filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
  margin: 0.5rem 0 0;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--border);
  background: var(--surface-glass);
  border-radius: var(--ctrl-radius);
}
.active-filter-bar[hidden] { display: none; }
.active-filter-bar .active-filter-label {
  font-size: 0.82rem;
  color: var(--text);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-right: 0.4rem;
}
.active-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.28rem 0.6rem 0.28rem 0.75rem;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--text);
  font-size: 0.88rem;
  cursor: pointer;
  line-height: 1;
}
.active-filter-chip:hover {
  border-color: var(--accent-strong);
  background: var(--accent-soft);
}
.active-filter-chip .chip-key {
  color: var(--accent);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 700;
}
.active-filter-chip .chip-val {
  color: var(--text);
  font-weight: 600;
}
.active-filter-chip .chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: var(--text);
  font-size: 0.95rem;
  font-weight: 700;
}
.active-filter-chip:hover .chip-x { background: var(--accent); color: var(--accent-ink); }
@media (max-width: 600px) {
  .active-filter-bar {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
  }
  .active-filter-bar > * { flex: 0 0 auto; }
}

/* #4 Mini sparkline min/max axis labels (cards + row sparklines)
   — small but deliberate: tabular digits, dark halo via text-shadow so
   they stay legible on top of the gold curve, brightness held below
   the Avg score so the eye reads Avg first.

   Rendered as HTML spans absolutely-positioned over a .curve-wrap rather
   than SVG <text>, because the parent SVG uses preserveAspectRatio="none"
   and would otherwise stretch inline text horizontally and squish it
   vertically on narrow mobile widths (the "9.2" rendered as a huge wide
   blob over the chart). HTML labels keep CSS font-size on every viewport. */
.curve-wrap {
  position: relative;
  display: block;
  width: 100%;
}
.curve-wrap > .curve { width: 100%; margin: 0; }
.spark-axis-labels {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.spark-axis-label {
  position: absolute;
  left: 0.3rem;
  font-size: 11px;
  line-height: 1;
  color: rgba(241, 243, 248, 0.82);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, "Inter", system-ui, sans-serif;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  /* Dark halo so the digits stay legible over the gold curve area. */
  text-shadow:
    0 0 2px rgba(8, 10, 16, 0.95),
    0 0 2px rgba(8, 10, 16, 0.95),
    0 0 3px rgba(8, 10, 16, 0.7);
}
/* JS sets `top: <yPct>%` for both labels so they line up with the actual
   y-coordinate of the max/min on the curve (drawCurve pads the Y range
   beyond min/max). translateY(-50%) centers the label vertically on the
   y line so it overlays the curve at the right height rather than
   overflowing above/below the wrap. */
.spark-axis-label-top,
.spark-axis-label-bot { top: 0; bottom: auto; transform: translateY(-50%); }

/* #5 Sticky filter bar — frosted-glass strip that drops in once the
   main filter section scrolls off. Echoes the main toolbar (dark
   glass, top-edge amber rim, gold-tinted chips on hover, soft amber
   wash on active) so it reads as the same control surface, compact. */
.sticky-filter-bar {
  position: fixed;
  top: var(--header-h, 3.25rem);
  left: 0;
  right: 0;
  z-index: 60;
  background: rgba(11, 13, 18, 0.78);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  padding: 0.55rem 1rem;
  box-shadow:
    0 -1px 0 rgba(245, 197, 24, 0.18) inset,
    0 8px 24px rgba(0, 0, 0, 0.35);
  transform: translateY(-110%);
  transition: transform 0.22s var(--ease), opacity 0.18s var(--ease);
  pointer-events: none;
  opacity: 0;
}
/* Top-edge amber hairline — same accent as .filter-row.primary so
   the sticky bar visually belongs to the same toolbar family. */
.sticky-filter-bar::before {
  content: '';
  position: absolute;
  left: 12%;
  right: 12%;
  top: -1px;
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(245, 197, 24, 0.45) 50%,
    transparent 100%);
  pointer-events: none;
}
.sticky-filter-bar.is-visible {
  transform: translateY(0);
  pointer-events: auto;
  opacity: 1;
}
.sticky-filter-bar[hidden] { display: none; }
.sticky-filter-inner {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  max-width: 1500px;
  margin: 0 auto;
}
.sticky-shape-row {
  display: flex;
  gap: 0.35rem;
  overflow-x: auto;
  flex: 1 1 auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
  /* Soft fade on the right so chips appear to scroll into the bar
     rather than being abruptly clipped at the search box. */
  mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 2rem), transparent 100%);
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 2rem), transparent 100%);
  padding-right: 1rem;
}
.sticky-shape-row::-webkit-scrollbar { height: 4px; }
.sticky-shape-row::-webkit-scrollbar-track { background: transparent; }
.sticky-shape-row::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.18);
  border-radius: 999px;
}
/* Sticky chips inherit shape-chip styling at a compact size. */
.sticky-shape-chip {
  flex: 0 0 auto;
  height: 28px;
  padding: 0 0.7rem;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.05);
  color: #FFFFFF !important;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.005em;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02) inset;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease), transform 0.18s var(--ease);
}
.sticky-shape-chip:hover {
  background: rgba(245, 197, 24, 0.06);
  border-color: rgba(245, 197, 24, 0.32);
  color: #FFFFFF !important;
  transform: translateY(-1px);
}
.sticky-shape-chip.is-active,
.sticky-shape-chip.is-active:hover {
  background: rgba(245, 197, 24, 0.14);
  border-color: rgba(245, 197, 24, 0.5);
  color: var(--accent) !important;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02) inset,
              0 0 0 1px rgba(245, 197, 24, 0.18) inset;
}
.sticky-shape-chip .sticky-shape-x {
  margin-left: 0.05rem;
  font-size: 0.95rem;
  line-height: 1;
  opacity: 0.7;
}
.sticky-shape-chip:hover .sticky-shape-x {
  opacity: 1;
}
.sticky-search input {
  height: 28px;
  padding: 0 0.7rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  font-size: 0.82rem;
  min-width: 0;
  width: 220px;
  max-width: 30vw;
  transition: border-color 0.18s var(--ease), background 0.18s var(--ease),
              box-shadow 0.18s var(--ease);
}
.sticky-search input:focus {
  outline: none;
  border-color: rgba(245, 197, 24, 0.5);
  background: rgba(255, 255, 255, 0.08);
  box-shadow: 0 0 0 3px rgba(245, 197, 24, 0.12);
}
.sticky-search input::placeholder {
  color: rgba(231, 233, 238, 0.45);
}
.sticky-jump {
  height: 28px;
  padding: 0 0.85rem;
  font-size: 0.78rem;
  font-weight: 600;
  flex: 0 0 auto;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.04);
  color: rgba(231, 233, 238, 0.85);
  cursor: pointer;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease);
}
.sticky-jump:hover {
  background: rgba(245, 197, 24, 0.08);
  border-color: rgba(245, 197, 24, 0.4);
  color: var(--accent);
}
@media (max-width: 600px) {
  .sticky-filter-bar { padding: 0.5rem 0.75rem; }
  .sticky-filter-inner { gap: 0.4rem; }
  .sticky-search input { width: 130px; }
  .sticky-jump { display: none; }
}

/* #6 Empty state — minimal, airy "no results" panel. Reads as a quiet
   helper, not a warning. Icon → headline → subtitle → optional chips →
   ghost reset; vertical gap on the column does the spacing work so
   each child stays unfussy. */
.empty-state {
  padding: 4.5rem 1.5rem 4.25rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
}
.empty-state .empty-icon {
  width: 48px;
  height: 48px;
  color: rgba(243, 244, 246, 0.4);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.25rem;
}
.empty-state .empty-icon svg {
  display: block;
  width: 100%;
  height: 100%;
}
.empty-state .empty-headline {
  color: #F3F4F6;
  font-size: 1.05rem;
  font-weight: 500;
  margin: 0;
  letter-spacing: -0.005em;
}
.empty-state .empty-sub {
  color: rgba(243, 244, 246, 0.55);
  font-size: 0.88rem;
  font-weight: 400;
  margin: 0;
}
.empty-state .empty-suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
  max-width: 540px;
  margin: 0.25rem 0 0;
}
.empty-state .empty-suggestion-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: rgba(243, 244, 246, 0.85) !important;
  padding: 0.32rem 0.85rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease), color 0.18s var(--ease);
}
.empty-state .empty-suggestion-btn:hover {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.2);
  color: #FFFFFF !important;
}
.empty-state .empty-suggestion-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.05em;
  height: 1.05em;
  color: rgba(243, 244, 246, 0.55);
  font-weight: 400;
  font-size: 0.95em;
}
.empty-state .empty-suggestion-btn:hover .empty-suggestion-icon {
  color: #FFFFFF;
}
/* Reset CTA: subtle ghost pill — quiet enough that it reads as a
   "tap to clear" affordance instead of a primary action. Uses
   !important on color so the global `button { color: #555 !important }`
   from assets/css/main.css can't drag it back to gray. */
.empty-state .empty-reset-btn {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: rgba(243, 244, 246, 0.88) !important;
  padding: 0.38rem 1rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 500;
  cursor: pointer;
  margin-top: 0.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease), color 0.18s var(--ease);
}
.empty-state .empty-reset-btn:hover {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.24);
  color: #FFFFFF !important;
}

/* #7 Modal curve annotation line */
.modal-curve-annotation {
  margin: 0.4rem 0 1rem;
  padding: 0.5rem 0.75rem;
  background: rgba(245, 197, 24, 0.06);
  border-left: 3px solid var(--accent-strong);
  border-radius: 4px;
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.45;
}
.modal-curve-annotation[hidden] { display: none; }
.modal-curve-annotation .ann-shape {
  color: var(--accent);
  font-weight: 600;
}

/* #10 Shortcut legend popover + button */
.shortcut-legend-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  line-height: 1;
}
.shortcut-legend-btn:hover,
.shortcut-legend-btn[aria-expanded="true"] {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}
.shortcut-legend {
  position: fixed;
  top: calc(var(--header-h, 3.25rem) + 4rem);
  right: 1.5rem;
  z-index: 70;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 1rem 1.1rem 0.85rem;
  box-shadow: var(--shadow-lift);
  width: min(320px, calc(100vw - 2rem));
}
.shortcut-legend[hidden] { display: none; }
.shortcut-legend-title {
  font-size: 0.9rem;
  margin: 0 0 0.6rem;
  color: var(--text);
}
.shortcut-legend-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.shortcut-legend-list li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.shortcut-legend-list kbd {
  display: inline-block;
  min-width: 1.4rem;
  padding: 0.1rem 0.35rem;
  text-align: center;
  font-size: 0.8rem;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.shortcut-legend-note {
  margin: 0.7rem 0 0;
  font-size: 0.75rem;
  color: var(--muted-2);
  line-height: 1.4;
}
@media (max-width: 600px) {
  .shortcut-legend {
    right: 0.75rem;
    top: calc(var(--header-h, 3.25rem) + 3.5rem);
  }
}

/* #2 Modal curve focus state for keyboard navigation */
.modal-curve:focus {
  outline: 2px solid var(--accent-strong);
  outline-offset: 4px;
  border-radius: 6px;
}
.modal-curve .curve-dots circle.is-active {
  fill: var(--accent);
  stroke: var(--accent-warm);
  stroke-width: 2;
  r: 7;
}

/* #9 Shape chip count: dim when showing intersection hover preview */
.shape-chip.is-hover-intersection .shape-count {
  background: var(--accent-soft);
  color: var(--accent);
}

/* ======= NEW FEATURES ======= */

/* Feature 2: on mobile the mood presets collapse behind a pill toggle
   (replaces the original horizontal scroll strip). The summary is styled
   as a pill that mirrors the quick-filters toggle; app.js collapses the
   <details> on narrow viewports. */
@media (max-width: 600px) {
  .mood-collapsible {
    text-align: center;
  }
  /* Chips inherit the base rule's justify-content: center so the wrapped
     rows stay centered under the centered toggle pill. */
  .mood-chip {
    white-space: normal;
    height: auto;
    min-height: 36px;
    padding: 0.4rem 0.85rem;
  }
  .mood-collapsible > summary {
    cursor: pointer;
    user-select: none;
  }
  /* Border-drawn chevron: points right when closed, down when open. */
  .mood-toggle-chevron {
    display: inline-block;
    width: 0.55rem;
    height: 0.55rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg) translateY(-1px);
    transition: transform 0.2s ease;
    flex-shrink: 0;
    margin-left: 0.15rem;
    opacity: 0.75;
  }
  .mood-collapsible[open] .mood-toggle-chevron {
    transform: rotate(45deg) translateY(-2px);
  }
  /* The heading becomes the toggle pill; hide its hairline rules. */
  .mood-presets-title {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    padding: 0.4rem 0.85rem;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.035);
    border: 1px solid var(--border);
    color: rgba(243, 244, 246, 0.88);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    margin-bottom: 0;
    transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
                color 0.15s var(--ease);
  }
  .mood-presets-title::before,
  .mood-presets-title::after {
    display: none;
  }
  .mood-collapsible > summary:hover {
    background: rgba(255, 255, 255, 0.06);
    border-color: var(--border-strong);
    color: #F3F4F6;
  }
  /* Open state: elevated neutral pill with a hairline accent border. The
     accent is a hint, not a fill, so the open toggle doesn't compete with
     active filter chips for attention. */
  .mood-collapsible[open] > summary {
    background: rgba(255, 255, 255, 0.07);
    border-color: rgba(245, 197, 24, 0.4);
    color: #F3F4F6;
  }
  .mood-collapsible[open] .mood-preset-chips {
    margin-top: 0.85rem;
  }
}

/* Feature 3: Copy link button in active-filter bar */
.copy-link-btn {
  height: var(--ctrl-h-sm);
  font-size: 0.78rem;
  padding: 0 0.75rem;
  margin-left: auto;
  white-space: nowrap;
  flex-shrink: 0;
}

/* Feature 5: More like this section in modal */
.modal-related {
  margin-top: 1.5rem;
}

/* main.css's HTML5 reset sets display:block on all <section> elements, which
   silently defeats the hidden attribute (author display beats the UA
   [hidden] rule). Both related sections rely on hidden for their empty
   state, so pin it here -- same pattern as .modal[hidden] above. */
.modal-related[hidden],
.show-related[hidden] {
  display: none;
}

.related-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
  margin-top: 0.75rem;
}

.related-row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.55rem 0.7rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease);
}
.related-row:hover {
  background: var(--surface-3);
  border-color: var(--border-strong);
}
.related-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.related-poster {
  width: 40px;
  height: 60px;
  flex-shrink: 0;
  border-radius: 4px;
  overflow: hidden;
  background: var(--surface-3);
}
.related-poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.related-poster-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 60px;
  border-radius: 4px;
  background: hsl(calc(var(--poster-hue, 220)) 30% 18%);
  font-size: 1.1rem;
  color: #fff;
  font-weight: 700;
}

.related-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.related-title {
  font-size: 0.83rem;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.related-season {
  font-size: 0.75rem;
  color: var(--muted);
}
.related-shapes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem;
}

.related-rating {
  flex-shrink: 0;
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 480px) {
  .related-grid {
    grid-template-columns: 1fr;
  }
}

/* Feature 6: Quick genre row in label-filters panel */
/* No group-level flex override: the genre group inherits the same wrap
   behavior as the decade group so the label sits on its own line and the
   chip row takes the full width (its overflow-x scroll then works on
   mobile instead of collapsing to a zero-width sliver). */
.genre-quick-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  min-width: 0;
}

@media (max-width: 600px) {
  /* The group must be pinned to the panel's width — as a content-sized
     flex item it otherwise grows to fit all 8 chips (≈740px), the row
     inside it never overflows, and the scroll lands on the panel (which
     clips). Pinning the group makes the row the overflowing element so
     its own overflow-x scroll actually works. */
  .genre-quick-group {
    width: 100%;
    min-width: 0;
  }
  .genre-quick-row {
    width: 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 0.3rem;
  }
  .genre-quick-row .genre-chip {
    flex-shrink: 0;
  }
}

/* Feature 9: Decade row in label-filters panel */
.decade-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

@media (max-width: 600px) {
  /* Same group-width pin as .genre-quick-group above — without it the
     decade chips can push the group past the panel width and the row's
     own scroll never engages. */
  .decade-group {
    width: 100%;
    min-width: 0;
  }
  .decade-row {
    width: 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 0.3rem;
  }
  .decade-row .label-chip {
    flex-shrink: 0;
  }
}

/* Show-modal related-shows section (Tasks C/D) */
.show-related {
  margin-top: 1.5rem;
}

.show-related-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
  margin-top: 0.75rem;
}

.show-related-row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.55rem 0.7rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease);
}
.show-related-row:hover {
  background: var(--surface-3);
  border-color: var(--border-strong);
}
.show-related-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.show-related-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.show-related-title {
  font-size: 0.83rem;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.show-related-meta {
  font-size: 0.75rem;
  color: var(--muted);
}

/* "N more" collapsible toggle for both season and show related sections */
.related-more-toggle {
  margin-top: 0.5rem;
  padding: 0.35rem 0.8rem;
  font-size: 0.82rem;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.related-more-toggle:hover {
  color: var(--text);
  border-color: var(--border-strong);
}

/* Hidden rows revealed by the toggle */
.related-row-extra,
.show-related-row-extra {
  display: none;
}
.related-extra-expanded .related-row-extra,
.show-related-extra-expanded .show-related-row-extra {
  display: flex;
}

@media (max-width: 480px) {
  .show-related-grid {
    grid-template-columns: 1fr;
  }
}

/* External-link arrow on linked episode rows — the same trailing "→" the
   IMDb/TVDB action buttons carry, so a row that opens imdb.com reads as an
   outbound link. Absolutely positioned at the row's right edge (survives
   every breakpoint's grid-template override); the extra right padding keeps
   the rating/votes column clear of it. Rows without an episode tt id get
   no arrow (no .has-link class). Declared after the responsive blocks so
   the padding-right wins over their `padding` shorthands. */
.modal-episodes li.has-link {
  padding-right: 2.05rem;
}
.modal-episodes li.has-link::after {
  content: '→';
  position: absolute;
  right: 0.7rem;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  pointer-events: none;
  color: rgba(231, 233, 238, 0.45);
  font-size: 0.85rem;
  line-height: 1;
  transition: color 0.15s var(--ease), transform 0.15s var(--ease);
}
.modal-episodes li.has-link:hover::after,
.modal-episodes li.has-link:focus-within::after {
  color: var(--accent);
  transform: translateY(-50%) translateX(2px);
}

/* --- Show Finder --- */

.mode-hidden { display: none !important; }

.mode-switch {
  display: inline-flex;
  gap: 4px;
  margin: 0 auto 18px;
  padding: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
}

.mode-btn {
  appearance: none;
  border: 1px solid transparent !important;
  background: transparent !important;
  color: var(--muted) !important;
  font: inherit;
  font-weight: 600;
  padding: 8px 18px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.mode-btn:hover {
  background: var(--surface-3) !important;
  color: var(--text) !important;
  border-color: var(--border-strong) !important;
}

.mode-btn[aria-pressed="true"] {
  background: linear-gradient(135deg, var(--accent-warm), var(--accent) 55%, #e0b00f) !important;
  color: var(--accent-ink) !important;
  border-color: var(--accent) !important;
}

.finder {
  margin-top: 8px;
}

.finder-chips,
.finder-seg {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.finder-chip,
.finder-seg-btn {
  appearance: none;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: 999px;
  cursor: pointer;
  background: var(--surface-2) !important;
  color: var(--muted) !important;
  border: 1px solid var(--border) !important;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.finder-chip:hover,
.finder-seg-btn:hover {
  background: var(--surface-3) !important;
  color: var(--text) !important;
  border-color: var(--border-strong) !important;
}

.finder-chip[aria-pressed="true"],
.finder-seg-btn[aria-pressed="true"] {
  background: var(--accent-soft) !important;
  color: var(--accent) !important;
  border-color: var(--accent) !important;
}

.finder-seg {
  border-radius: 999px;
}

.finder-count {
  margin: 16px 2px 10px;
  font-weight: 700;
  color: var(--text);
}

.finder-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.finder-table thead th {
  text-align: left;
  padding: 12px 14px;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-2) !important;
  background: var(--surface-2) !important;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  user-select: none;
  transition: background 0.12s, color 0.12s;
}

.finder-table thead th:hover,
.finder-table thead th:focus-visible {
  color: var(--text) !important;
  background: var(--surface-3) !important;
  outline: none;
}

.finder-table thead th[aria-sort="ascending"],
.finder-table thead th[aria-sort="descending"] {
  color: var(--text) !important;
}

.finder-th-arrow {
  font-size: 0.7rem;
}

.finder-table th:not(.finder-col-show),
.finder-table td:not(.finder-col-show) {
  text-align: right;
  white-space: nowrap;
}

.finder-row {
  cursor: pointer;
  transition: background 0.12s;
}

.finder-row:hover,
.finder-row:focus-visible {
  background: var(--surface-2);
  outline: none;
}

.finder-row td {
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.finder-row:last-child td { border-bottom: none; }

.finder-show-title {
  display: block;
  font-weight: 600;
}

.finder-genre-line {
  display: block;
  margin-top: 2px;
  font-size: 0.78rem;
  color: var(--muted-2);
}

/* Poster thumbnail in the Show column, mirroring the Seasons list row.
   Smaller than .row-poster (44px) so the table row height stays tight. */
.finder-col-show {
  vertical-align: middle;
}
.finder-show-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.finder-row-poster {
  width: 44px;
  flex: 0 0 auto;
}
.finder-show-text {
  min-width: 0;
}

.finder-row td.finder-gap-pos { color: var(--good); font-weight: 700; }
.finder-row td.finder-gap-neg { color: var(--danger); font-weight: 700; }

.finder-empty {
  padding: 40px 20px;
  text-align: center;
  color: var(--muted);
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}

/* The list/table view is a single full-width table inside the results grid. */
.finder-results.finder-list-view { display: block; }
.finder-results.finder-list-view .finder-table { width: 100%; }

/* Grid-view card stats: show-level metrics in the same chip-grid spirit as
   the Seasons card .card-stats, reusing .card / .card-stats structure. */
.finder-card-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px 12px;
}

.finder-card-stats .stat-gap.finder-gap-pos { color: var(--good); font-weight: 700; }
.finder-card-stats .stat-gap.finder-gap-neg { color: var(--danger); font-weight: 700; }

/* Per-show season-average sparkline. Colors pinned explicitly so the sitewide
   main.css (which bleeds red/gray onto generic selectors) cannot override. */
.finder-spark .curve-line {
  stroke: #f5c518 !important;
  fill: none !important;
}
.finder-spark .curve-area { fill: rgba(245, 197, 24, 0.18) !important; }
.finder-spark .finder-spark-dot circle { fill: #f5c518 !important; }

/* Single-season shows: distinct amber/orange hue + a visible centered dot,
   since one point has no line to draw. */
.finder-spark--single .finder-spark-dot circle {
  fill: #ff8519 !important;
  stroke: rgba(11, 13, 18, 0.85) !important;
  stroke-width: 1.5 !important;
}
.finder-spark--single .curve-line { stroke: #ff8519 !important; }
.finder-spark--single .curve-area { fill: rgba(255, 133, 25, 0.16) !important; }

/* List-view Trend column: compact sparkline sized for a table row. */
.finder-table th.finder-col-trend,
.finder-table td.finder-col-trend {
  text-align: left;
  white-space: nowrap;
  width: 132px;
}

.finder-row-spark {
  display: block;
  width: 120px;
  height: 36px;
}

@media (max-width: 640px) {
  .finder-table,
  .finder-table tbody,
  .finder-table tr {
    display: block;
  }

  .finder-table thead { display: none; }

  .finder-row {
    margin-bottom: 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    overflow: hidden;
  }

  .finder-row td {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    text-align: right !important;
    white-space: normal !important;
    padding: 10px 14px;
  }

  .finder-row td.finder-col-show {
    flex-direction: row;
    align-items: center;
    background: var(--surface-2);
    text-align: left !important;
  }

  .finder-row td:not(.finder-col-show)::before {
    content: attr(data-label);
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--muted);
    text-transform: none;
    letter-spacing: 0;
  }

  .finder-table td.finder-col-trend {
    width: auto;
    align-items: center;
  }
}
