/* ==========================================================================
   seaquest.tv — sonar / deep-ocean theme
   Redesign source: docs/design-ideas/website/ — porthole hero, depth gauge,
   caustic/reflection ambience, breadcrumb + detail-hero page pattern.
   ========================================================================== */

:root {
  color-scheme: dark;
  --abyss: #070b1e;         /* page background — deep ocean */
  --depth: #10173a;         /* panel background */
  --depth-2: #16204a;       /* raised panel / hover */
  --line: rgba(240, 185, 62, 0.18); /* hairline borders */
  --sonar: #f0b93e;         /* primary accent — sonar gold */
  --sonar-dim: #a87a24;
  --signal: #8fa8e0;        /* secondary accent — signal blue, tags/badges */
  --foam: #e9edfb;          /* primary text */
  --mist: #8b93be;          /* secondary text */
  --mono: "Share Tech Mono", "Cascadia Code", "Consolas", monospace;
  --display: "Orbitron", sans-serif;
  --sans: "Inter", system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scrollbar-color: var(--sonar-dim) var(--depth);
}

::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--depth); }
::-webkit-scrollbar-thumb {
  background: var(--sonar-dim);
  border-radius: 6px;
  border: 2px solid var(--depth);
}
::-webkit-scrollbar-thumb:hover { background: var(--sonar); }

body {
  margin: 0;
  font-family: var(--sans);
  line-height: 1.65;
  color: var(--foam);
  background-color: var(--abyss);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  /* The blanket rule above snaps an animation to its *end* state, which is
     wrong for the ambience: caustic-drift ends translated -200px/120px, which
     would slide the fixed full-viewport grid off-register and leave bare
     edges. These want their start state, so cancel them outright. */
  .caustics, .porthole::before { animation: none !important; }
}

::selection { background: var(--sonar); color: var(--abyss); }

a { color: var(--sonar); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
a:focus-visible, button:focus-visible { outline: 2px solid var(--sonar); outline-offset: 3px; }

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.01em;
}

h1 { font-size: clamp(1.9rem, 4.4vw, 2.7rem); margin: 0 0 1rem; }
h2 { font-size: clamp(1.3rem, 3.2vw, 1.7rem); margin: 2.5rem 0 1rem; }
h3 { font-size: 1.05rem; }

p { margin: 0 0 1rem; }

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

/* Available to assistive tech, absent from the visual layout. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Ambient background — vertical gradient + HUD grid + drifting caustics.
   All CSS, no canvas; frozen entirely under prefers-reduced-motion via the
   rule above. Keep this cheap: these layers are fixed and full-viewport, so
   anything that animates here is repainting the whole screen forever. Only
   the caustics move, they move by transform alone, and they are promoted to
   their own compositor layer so the drift never touches the main thread.
   -------------------------------------------------------------------------- */

.depth-wash {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(ellipse at 50% -10%, rgba(240, 185, 62, 0.08), transparent 60%),
    linear-gradient(180deg, #0a0f26 0%, #070b1e 30%, #04071a 70%, #02040b 100%),
    linear-gradient(rgba(240, 185, 62, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(240, 185, 62, 0.05) 1px, transparent 1px);
  background-size: auto, auto, 48px 48px, 48px 48px;
}

.caustics {
  position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: 0.05;
  background-image: repeating-linear-gradient(115deg, transparent 0 40px, var(--sonar) 40px 41px);
  animation: caustic-drift 18s linear infinite;
  will-change: transform;
}
@keyframes caustic-drift { from { transform: translate(0, 0); } to { transform: translate(-200px, 120px); } }

/* darkens as the page scrolls, simulating descent */
.depth-shade { position: fixed; inset: 0; z-index: 0; pointer-events: none; background: #00050a; opacity: 0; }

/* --------------------------------------------------------------------------
   Depth gauge — thin top bar, fills left-to-right with scroll position.
   Overlays the header's top edge rather than pushing it down, so it costs no
   layout space (no body padding, no reserved sidebar) on any viewport.
   -------------------------------------------------------------------------- */

#depth-gauge {
  position: fixed; left: 0; top: 0; width: 100%; height: 3px; z-index: 50;
  background: rgba(240, 185, 62, 0.12);
}
#depth-gauge .fill {
  height: 100%; width: 0%;
  background: var(--sonar); box-shadow: 0 0 8px var(--sonar);
  transition: width 0.1s linear;
}

/* --------------------------------------------------------------------------
   Layout shell
   -------------------------------------------------------------------------- */

.wrap {
  position: relative; z-index: 1;
  width: min(1080px, 100% - 2.5rem);
  margin-inline: auto;
}

main.wrap {
  flex: 1;
  padding-block: 2.5rem 4rem;
}

/* --------------------------------------------------------------------------
   Header / nav
   -------------------------------------------------------------------------- */

/* No backdrop-filter here, and none on #depth-gauge either — a backdrop blur
   is recomputed whenever anything painted behind it changes, and the caustics
   drift behind both of these forever, so the two blurs cost a full re-blur
   every frame on every page even at idle. What sits behind the header is a
   smooth gradient plus 5%-opacity hairlines; a near-opaque fill is close to
   indistinguishable from a blur of that. */
.site-header {
  position: sticky; top: 0; z-index: 40;
  border-bottom: 1px solid var(--line);
  background: rgba(7, 11, 30, 0.94);
  transition: padding 0.25s ease, box-shadow 0.25s ease;
}

.site-header.is-compact {
  box-shadow: 0 8px 24px -12px rgba(0, 0, 0, 0.6);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-block: 0.9rem;
  transition: padding 0.25s ease;
}

.site-header.is-compact .header-inner { padding-block: 0.5rem; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
  font-family: var(--display);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--foam);
}
.brand:hover { text-decoration: none; }

/* sonar ping mark */
.brand-mark {
  width: 1.05rem;
  height: 1.05rem;
  border-radius: 50%;
  border: 2px solid var(--sonar);
  position: relative;
  flex-shrink: 0;
}
/* The expanding ring is a scaled pseudo-element, not an animated box-shadow
   spread: box-shadow is a paint property, so the shadow version repainted the
   header on every frame, forever. transform + opacity stay on the compositor. */
.brand-mark::after {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  background: var(--sonar);
}
.brand-mark::before {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  background: rgba(240, 185, 62, 0.55);
  animation: ping 2.4s ease-out infinite;
}
@keyframes ping {
  0% { transform: scale(1); opacity: 1; }
  70% { transform: scale(5.2); opacity: 0; }
  100% { transform: scale(5.2); opacity: 0; }
}

.brand-dot { color: var(--sonar); }

.brand-text-group { display: flex; flex-direction: column; }
.brand-tagline {
  font-family: var(--mono); font-weight: 400; font-size: 9px; color: var(--mist);
  letter-spacing: 2px; text-transform: uppercase;
  overflow: hidden; max-height: 1.4em; opacity: 1;
  transition: opacity 0.2s ease, max-height 0.2s ease, margin 0.2s ease;
}
.site-header.is-compact .brand-tagline { opacity: 0; max-height: 0; }

.site-nav {
  display: flex;
  gap: 1.5rem;
}

.site-nav a {
  font-family: var(--mono);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--mist);
  padding-block: 0.25rem;
  border-bottom: 2px solid transparent;
}

.site-nav a:hover {
  color: var(--foam);
  text-decoration: none;
}

.site-nav a[aria-current="page"] {
  color: var(--sonar);
  border-bottom-color: var(--sonar);
}

/* --------------------------------------------------------------------------
   Shared components
   -------------------------------------------------------------------------- */

.eyebrow {
  font-family: var(--mono);
  color: var(--sonar);
  font-size: 11px;
  letter-spacing: 3px;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 0.5rem;
}
.eyebrow::before { content: ""; width: 24px; height: 1px; background: var(--sonar); }

.kicker {
  font-family: var(--mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--signal);
  margin: 0 0 0.5rem;
}

.lead {
  font-size: 1.1rem;
  color: var(--mist);
  max-width: 60ch;
}

.panel {
  background: var(--depth);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 1.25rem 1.4rem;
}

.readout-card {
  background: var(--depth);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 1.4rem 1.5rem;
  position: relative;
  overflow: hidden;
}
.readout-card::before {
  content: "REC"; position: absolute; top: 10px; right: 14px;
  font-family: var(--mono); font-size: 9px; color: var(--signal); letter-spacing: 2px;
}
.readout-card h3 {
  font-family: var(--mono); font-size: 12px; color: var(--sonar);
  letter-spacing: 2px; text-transform: uppercase; margin-bottom: 0.9rem;
}
.stat-row {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: 0.6rem 0; border-bottom: 1px dashed rgba(233, 237, 251, 0.12);
  font-family: var(--mono); font-size: 0.8rem;
}
.stat-row:last-child { border-bottom: none; }
.stat-row span:first-child { color: var(--mist); }
.stat-row span:last-child { color: var(--foam); text-align: right; }

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1.25rem;
  padding: 0;
  margin: 1.5rem 0;
  list-style: none;
}

.card {
  background: var(--depth);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 1.2rem 1.3rem;
  transition: transform 0.2s ease, border-color 0.15s ease, background 0.15s ease, box-shadow 0.2s ease;
}

.card:hover {
  border-color: var(--sonar);
  background: var(--depth-2);
  transform: translateY(-3px);
  box-shadow: 0 12px 30px -14px rgba(240, 185, 62, 0.3);
}

.card h3 { margin: 0 0 0.35rem; font-size: 1.05rem; }
.card h3 a { color: var(--foam); }
.card h3 a:hover { color: var(--sonar); text-decoration: none; }
.card p { margin: 0.4rem 0 0; font-size: 0.92rem; color: var(--mist); }

.meta {
  font-family: var(--mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--sonar-dim);
}

/* --------------------------------------------------------------------------
   Card component (list / grid / hero formats) — src/_includes/partials/card.njk

   Every card carries "card card--<format>" plus, where a caller still needs
   one-off rules, its own modifier class (.roster-card, .season-column,
   .featured-episode-card, ...). Most existing modifier rules need no
   changes: same-specificity overrides win by appearing later in this file,
   which every modifier block already does relative to .card. Where a
   modifier must cancel something .card:hover introduces (lift/box-shadow/
   border-color it didn't have before), that cancellation is written as an
   explicit compound selector rather than left to rely on source order.
   -------------------------------------------------------------------------- */

.card--hero:has(.card-image) {
  display: grid;
  grid-template-columns: minmax(0, 300px) 1fr;
  gap: 1.5rem;
  align-items: center;
}

.tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--signal);
  border: 1px solid rgba(143, 168, 224, 0.4);
  border-radius: 3px;
  padding: 0.1rem 0.45rem;
  white-space: nowrap;
}

.back-link {
  font-family: var(--mono);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

.btn {
  display: inline-block;
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.12em; text-transform: uppercase;
  padding: 0.75rem 1.4rem; border-radius: 3px; cursor: pointer;
  border: 1px solid var(--sonar); background: transparent; color: var(--sonar);
  transition: all 0.2s ease;
}
.btn:hover { background: var(--sonar); color: var(--abyss); text-decoration: none; box-shadow: 0 0 24px rgba(240, 185, 62, 0.35); }
.btn.ghost { border-color: rgba(233, 237, 251, 0.3); color: var(--mist); }
.btn.ghost:hover { background: rgba(233, 237, 251, 0.08); color: var(--foam); box-shadow: none; }

/* --------------------------------------------------------------------------
   Homepage hero — porthole with sonar sweep
   -------------------------------------------------------------------------- */

.hero {
  padding-block: 3rem 1rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.hero.hero-wide { max-width: 100%; }

.porthole {
  position: relative;
  width: min(60vw, 300px);
  aspect-ratio: 1;
  border-radius: 50%;
  margin-bottom: 2rem;
  background:
    radial-gradient(circle at 42% 38%, rgba(240, 185, 62, 0.14), transparent 55%),
    radial-gradient(circle, var(--depth-2) 0%, #030712 78%);
  border: 8px solid #223066;
  box-shadow:
    0 0 0 3px #0a0f26,
    inset 0 0 60px rgba(0, 0, 0, 0.7),
    0 30px 80px -20px rgba(0, 0, 0, 0.8);
}
.porthole::before {
  content: ""; position: absolute; inset: -8px; border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0deg, rgba(240, 185, 62, 0.5) 8deg, transparent 16deg);
  animation: sweep 4s linear infinite;
  -webkit-mask: radial-gradient(circle, transparent 62%, black 63%);
  mask: radial-gradient(circle, transparent 62%, black 63%);
  /* the conic gradient under a mask is an expensive surface to rasterize —
     promote it so the rotation reuses one raster instead of redrawing it */
  will-change: transform;
}
@keyframes sweep { to { transform: rotate(360deg); } }

.hull-mark {
  font-family: var(--mono); color: var(--sonar-dim); font-size: 11px;
  letter-spacing: 4px; text-transform: uppercase; margin-bottom: 0.9rem;
}

.hero h1 {
  font-size: clamp(2.1rem, 6vw, 3.4rem);
  max-width: 22ch;
  background: linear-gradient(180deg, #fff3d6 0%, var(--sonar) 60%, var(--sonar-dim) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  text-shadow: 0 0 40px rgba(240, 185, 62, 0.2);
}

.hero .lead { margin-inline: auto; }

.hero-ctas { display: flex; gap: 0.9rem; margin-top: 1.6rem; flex-wrap: wrap; justify-content: center; }

.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.25rem;
  margin-block: 2.5rem;
}

.feature-grid .card { display: block; color: inherit; }
.feature-grid .card:hover { text-decoration: none; }

/* --------------------------------------------------------------------------
   Episode of the day
   -------------------------------------------------------------------------- */

.eotd-card {
  display: block;
  background: linear-gradient(135deg, var(--depth-2), var(--depth));
  border: 1px solid rgba(240, 185, 62, 0.3);
  border-radius: 6px;
  padding: 1.75rem;
  position: relative;
  overflow: hidden;
  transition: border-color 0.2s ease, transform 0.2s ease;
}
.eotd-card:hover {
  border-color: var(--sonar);
  transform: translateY(-2px);
  text-decoration: none;
  /* Cancel .card:hover's flat background and box-shadow, which would
     otherwise cover the gradient and add an unwanted glow. */
  background: linear-gradient(135deg, var(--depth-2), var(--depth));
  box-shadow: none;
}
.eotd-card::before {
  content: ""; position: absolute; top: 0; right: 0; width: 200px; height: 200px;
  background: radial-gradient(circle, rgba(240, 185, 62, 0.15), transparent 70%);
  pointer-events: none;
}
.eotd-card h3 { font-size: 1.4rem; margin: 0.4rem 0; color: var(--foam); }
.eotd-card p { color: var(--mist); font-size: 0.95rem; max-width: 60ch; margin-top: 0.6rem; }

/* --------------------------------------------------------------------------
   Breadcrumb + detail-page hero pattern (episode / character detail pages)
   -------------------------------------------------------------------------- */

.breadcrumb {
  font-family: var(--mono); font-size: 11px; letter-spacing: 1.5px;
  color: var(--mist); margin-bottom: 1.5rem; text-transform: uppercase;
}
.breadcrumb a { color: var(--sonar-dim); }
.breadcrumb a:hover { color: var(--sonar); }

.detail-hero {
  padding-bottom: 1.75rem;
  margin-bottom: 2.25rem;
  border-bottom: 1px solid var(--line);
}
.detail-tag {
  font-family: var(--mono); font-size: 11px; color: var(--signal); letter-spacing: 2px;
  text-transform: uppercase;
  border: 1px solid rgba(143, 168, 224, 0.4);
  display: inline-block; padding: 5px 10px; border-radius: 2px; margin-bottom: 1rem;
}
.detail-hero h1 { margin-bottom: 0.5rem; }
.detail-hero .subline {
  color: var(--sonar); font-family: var(--mono); font-size: 12px; letter-spacing: 1.5px;
  text-transform: uppercase;
}

.detail-body {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 3rem;
  align-items: start;
}
@media (max-width: 800px) { .detail-body { grid-template-columns: 1fr; } }
.detail-body .readout-card + .readout-card { margin-top: 1.25rem; }

.related-tags { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.25rem; }
.related-tags a {
  font-family: var(--mono); font-size: 10.5px; color: var(--sonar);
  border: 1px solid rgba(240, 185, 62, 0.3); border-radius: 2px; padding: 6px 10px;
}
.related-tags a:hover { background: rgba(240, 185, 62, 0.1); text-decoration: none; }

.detail-nav {
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  margin-top: 3rem; padding-top: 1.25rem; border-top: 1px solid var(--line);
}
.detail-nav a {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase;
  border: 1px solid var(--line); border-radius: 3px; padding: 0.7rem 1rem;
  transition: border-color 0.2s ease, color 0.2s ease;
}
.detail-nav a:hover { border-color: var(--sonar); color: var(--sonar); text-decoration: none; }

/* --------------------------------------------------------------------------
   Episode guide
   -------------------------------------------------------------------------- */

/* Season hub — three tall clickable columns at the top of the guide. */
.season-hub {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.25rem;
  margin-block: 2.5rem 3.5rem;
}

.season-column {
  display: flex;
  flex-direction: column;
  background: var(--depth);
  border: 1px solid var(--line);
  border-top: 3px solid var(--sonar-dim);
  border-radius: 6px;
  overflow: hidden;
  color: inherit;
  transition: transform 0.2s ease, border-color 0.15s ease, background 0.15s ease, box-shadow 0.2s ease;
  /* Overrides .card's padding — this card is full-bleed (the image runs to
     the edges) with every child spacing itself via its own margins. */
  padding: 0;
}

.season-column:hover {
  border-color: var(--sonar);
  border-top-color: var(--sonar);
  background: var(--depth-2);
  transform: translateY(-3px);
  box-shadow: 0 14px 34px -16px rgba(240, 185, 62, 0.32);
  text-decoration: none;
}

.season-column img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

.season-column h2 {
  margin: 1.1rem 1.2rem 0.4rem;
  font-size: 1.15rem;
}

.season-column .meta { margin: 0 1.2rem; }

.season-column .card-body-text {
  margin: 0.9rem 1.2rem 1.2rem;
  font-size: 0.92rem;
  color: var(--mist);
}

/* Pinned to the bottom so the three columns line up however long the intros run. */
.season-column .card-cta {
  margin-top: auto;
  padding: 0.85rem 1.2rem;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--sonar);
}

@media (max-width: 860px) {
  .season-hub { grid-template-columns: 1fr; }
}

/* Featured voyage — re-picked at random by the inline script on /episodes/. */
.featured-episode { margin-block: 2rem; }

.featured-episode-card {
  display: grid;
  grid-template-columns: minmax(0, 300px) 1fr;
  gap: 1.5rem;
  align-items: center;
  background: var(--depth);
  border: 1px solid var(--line);
  border-left: 3px solid var(--sonar);
  border-radius: 6px;
  padding: 1.25rem;
  color: inherit;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.featured-episode-card:hover {
  background: var(--depth-2);
  text-decoration: none;
  /* Cancel .card:hover's lift/box-shadow/border-color — this card only
     ever swapped its background on hover. */
  border-color: var(--line);
  transform: none;
  box-shadow: none;
}

.featured-episode-card img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 4px;
}

.featured-episode-card h2 { margin: 0.35rem 0 0.4rem; font-size: 1.4rem; }
.featured-episode-card p { margin: 0; color: var(--mist); }
.featured-episode-card .meta { margin: 0 0 0.6rem; }

@media (max-width: 620px) {
  .featured-episode-card { grid-template-columns: 1fr; }
}

.season-block { margin-block: 2.75rem; }

.season-block > h2 a { color: inherit; }
.season-block > h2 a:hover { color: var(--sonar); text-decoration: none; }

.season-block > h2 {
  border-bottom: 1px solid var(--line);
  padding-bottom: 0.5rem;
}

.season-intro { color: var(--mist); max-width: 70ch; }

.episode-list {
  list-style: none;
  margin: 1.25rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}

.card--list {
  display: grid;
  grid-template-columns: 160px 1fr;
  gap: 0.35rem 1.1rem;
  align-items: start;
  border-left: 3px solid var(--sonar-dim);
  border-radius: 4px;
  padding: 0.8rem 1.1rem;
}

.card--list:hover {
  background: var(--depth-2);
  /* Cancel .card:hover's all-sides border-color/lift/box-shadow — this
     format only ever brightened the left accent on hover. */
  border-color: var(--line);
  border-left-color: var(--sonar);
  transform: none;
  box-shadow: none;
  text-decoration: none;
}

.episode-thumb {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 3px;
}

/* Eyebrow, title, and body used to be three separate grid cells, with the
   thumbnail spanning both rows — CSS Grid then grew the (short) header row
   to help satisfy the spanning thumbnail's height, leaving a gap between
   the header and the body row that sat below it. Grouping them into one
   flex column keeps the thumbnail to a single, non-spanning column so its
   aspect-ratio can no longer push the header row taller than its own text. */
.card-list-content {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 1.1rem;
}

.card-list-content .card-body-text {
  flex-basis: 100%;
  margin: 0;
  font-size: 0.92rem;
  color: var(--mist);
}

.episode-code {
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--signal);
  white-space: nowrap;
}

@media (max-width: 720px) {
  .card--list { grid-template-columns: 96px 1fr; }
}

.episode-header .meta { margin-bottom: 0.75rem; }

.episode-detail .synopsis-full {
  max-width: 70ch;
  font-size: 1.05rem;
}

/* 4:3, like every surface that shows a frame from the show — captures are
   cropped to the broadcast ratio by scripts/crop-captures.js, and a 16/9 slot
   would cover-crop a quarter of their height back off. */
.episode-still {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: 6px;
  margin-bottom: 1.5rem;
}

.episode-credits,
.episode-gallery { margin-top: 2.5rem; }

.episode-credits h2,
.episode-gallery h2 {
  font-size: 1.05rem;
  margin: 0 0 1rem;
  padding-bottom: 0.45rem;
  border-bottom: 1px solid var(--line);
}

.episode-gallery h2 .meta { margin-left: 0.5rem; font-size: 0.7rem; }

.credit-list {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.5rem 1.25rem;
  margin: 0;
  max-width: 70ch;
}

.credit-list dt {
  font-family: var(--mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--sonar-dim);
  white-space: nowrap;
}

.credit-list dd { margin: 0; font-size: 0.95rem; }

.capture-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 0.85rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.capture-grid img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: 4px;
  transition: border-color 0.15s ease;
}

.capture-grid figure:hover img { border-color: var(--sonar-dim); }

.capture-grid figcaption {
  margin-top: 0.4rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--mist);
}

/* --------------------------------------------------------------------------
   Characters
   -------------------------------------------------------------------------- */

/* The whole card is the click target: the name link is stretched over it with
   a pseudo-element, which keeps the real link text for screen readers rather
   than wrapping the card in a giant anchor. */
.roster-card {
  position: relative;
  display: flex;
  gap: 1rem;
  align-items: flex-start;
}

.roster-card h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.roster-card:hover { cursor: pointer; }
.roster-card:hover h3 a { color: var(--sonar); }

/* Focus lands on the invisible stretched link, so show it on the card. */
.roster-card:focus-within {
  border-color: var(--sonar);
  outline: 2px solid var(--sonar);
  outline-offset: 2px;
}

.roster-card h3 a:focus-visible { outline: none; }

.roster-portrait {
  flex: 0 0 76px;
  width: 76px;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: 4px;
}

/* Lets long names wrap instead of blowing out the 260px grid track. */
.roster-card-body { min-width: 0; }

.roster-card .card-body-text { color: var(--mist); font-size: 0.9rem; margin: 0; }

/* --------------------------------------------------------------------------
   Roster views (grid / by season / list)

   Three views switched by the radios in .view-switch — no script. The radios
   are visually hidden but still focusable, so the group keeps native
   radiogroup semantics and arrow-key navigation.
   -------------------------------------------------------------------------- */

.roster-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 1.75rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--line);
}

.roster-count {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--sonar-dim);
}

.view-switch {
  display: flex;
  gap: 0;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 4px;
  overflow: hidden;
}

.view-switch input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.view-switch label {
  padding: 0.45rem 0.85rem;
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--mist);
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}

.view-switch label + input + label { border-left: 1px solid var(--line); }
.view-switch label:hover { color: var(--foam); background: var(--depth); }

.view-switch input:checked + label {
  color: var(--abyss);
  background: var(--sonar);
}

.view-switch input:focus-visible + label {
  outline: 2px solid var(--sonar);
  outline-offset: -2px;
}

/* Which view is showing. Default (grid) needs no rule — it's the base state. */
.roster:has(#view-season:checked) .roster-flat { display: none; }
.roster:not(:has(#view-season:checked)) .roster-grouped { display: none; }

/* List view: one full-width row per person, portrait shrunk to a chip. */
.roster:has(#view-list:checked) .roster-flat {
  grid-template-columns: 1fr;
  gap: 0.6rem;
}

.roster:has(#view-list:checked) .roster-card {
  align-items: center;
  padding: 0.7rem 1rem;
}

.roster:has(#view-list:checked) .roster-card:hover { transform: none; }

.roster:has(#view-list:checked) .roster-portrait {
  flex-basis: 40px;
  width: 40px;
  aspect-ratio: 1;
}

.roster:has(#view-list:checked) .roster-card-body {
  display: flex;
  flex: 1;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.15rem 0.9rem;
}

.roster:has(#view-list:checked) .roster-card h3 { margin: 0; font-size: 0.98rem; }
.roster:has(#view-list:checked) .roster-card .meta { margin: 0; }

/* Description wraps to its own full-width line rather than being squeezed
   against the name/meta — squeezing it there works for the roster's short
   "actor · seasons" line but shoves longer prose (e.g. technology summaries)
   flush right once it wraps. */
.roster:has(#view-list:checked) .roster-card .card-body-text { flex-basis: 100%; }

.roster-grouped .season-block:first-child { margin-top: 1.5rem; }
.roster-grouped .season-block > h2 .meta { font-size: 0.7rem; }

@media (max-width: 720px) {
  .roster-bar { justify-content: flex-start; }
}

/* --------------------------------------------------------------------------
   Science & Technology
   -------------------------------------------------------------------------- */

/* Tech thumbnails are square; crew portraits stay 3:4. */
.tech-card .roster-portrait { aspect-ratio: 1; }

.featured-tech { margin-block: 2rem; }

.featured-tech-card {
  display: grid;
  grid-template-columns: minmax(0, 280px) 1fr;
  gap: 1.5rem;
  align-items: center;
  background: var(--depth);
  border: 1px solid var(--line);
  border-left: 3px solid var(--sonar);
  border-radius: 6px;
  padding: 1.25rem;
  color: inherit;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.featured-tech-card:hover {
  background: var(--depth-2);
  text-decoration: none;
  /* Cancel .card:hover's lift/box-shadow/border-color — this card only
     ever swapped its background on hover. */
  border-color: var(--line);
  transform: none;
  box-shadow: none;
}

.featured-tech-card img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 4px;
}

.featured-tech-card h2 { margin: 0.3rem 0 0.5rem; font-size: 1.4rem; }
.featured-tech-card p { color: var(--mist); margin: 0; }
.featured-tech-card .meta { margin: 0; }

.tech-figure {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: 6px;
  margin-bottom: 1.25rem;
}

.tech-copy { max-width: 70ch; }
.tech-copy h2 {
  font-size: 1.15rem;
  margin: 2rem 0 0.75rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--line);
}
.tech-copy h2:first-child { margin-top: 0; }
.tech-copy em { color: var(--mist); }

.seen-in { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.seen-in a { display: flex; gap: 0.7rem; align-items: baseline; color: var(--foam); }
.seen-in a:hover { color: var(--sonar); text-decoration: none; }

.panel-empty { margin: 0; font-size: 0.85rem; color: var(--mist); }

@media (max-width: 620px) {
  .featured-tech-card { grid-template-columns: 1fr; }
}

.character-header { margin-bottom: 1.5rem; }
.character-header .meta { display: block; margin-top: 0.35rem; }

.character-bio { max-width: 70ch; }

.fact-list {
  list-style: none;
  padding: 0;
  margin: 1.25rem 0;
  display: grid;
  gap: 0.4rem;
  font-size: 0.92rem;
}

.fact-list strong {
  font-family: var(--mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--sonar-dim);
  margin-right: 0.5rem;
}

/* --------------------------------------------------------------------------
   Gallery
   -------------------------------------------------------------------------- */

.gallery-section { margin-block: 2.5rem; }

/* Entry point from /gallery/ into the paginated capture archive. */
.archive-card {
  display: grid;
  grid-template-columns: minmax(0, 300px) 1fr;
  gap: 1.5rem;
  align-items: center;
  background: var(--depth);
  border: 1px solid var(--line);
  border-left: 3px solid var(--sonar);
  border-radius: 6px;
  padding: 1.25rem;
  margin-block: 2rem;
  color: inherit;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.archive-card:hover { background: var(--depth-2); text-decoration: none; }
.archive-card h2 { margin: 0.3rem 0 0.5rem; font-size: 1.3rem; }
.archive-card p { margin: 0; color: var(--mist); }
.archive-card .meta { margin: 0; }

.archive-card-strip {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.4rem;
}

.archive-card-strip img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 3px;
}

.archive-card-cta {
  display: inline-block;
  margin-top: 0.9rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--sonar);
}

/* One section per episode inside the archive. content-visibility lets the
   browser skip layout and paint for off-screen sections; contain-intrinsic-size
   keeps the scrollbar honest while they're skipped, and `auto` lets it
   remember the real height once a section has been rendered. */
.capture-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 420px;
}

.capture-section h2 {
  margin: 0 0 0.9rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--line);
  font-size: 1rem;
}

.capture-section h2 a {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  color: var(--foam);
}

.capture-section h2 a:hover { color: var(--sonar); text-decoration: none; }

/* Pager */
.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}

.pager-step {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.7rem 1rem;
  white-space: nowrap;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.pager-step:hover { border-color: var(--sonar); color: var(--sonar); text-decoration: none; }
.pager-step[aria-disabled="true"] { opacity: 0.35; pointer-events: none; }

.pager-pages {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pager-pages a {
  display: block;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  color: var(--mist);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.4rem 0.55rem;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.pager-pages a:hover { border-color: var(--sonar-dim); color: var(--foam); text-decoration: none; }

.pager-pages a[aria-current="page"] {
  background: var(--sonar);
  border-color: var(--sonar);
  color: var(--abyss);
}

@media (max-width: 620px) {
  .archive-card { grid-template-columns: 1fr; }
  .pager { justify-content: center; }
}

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1rem;
  margin-top: 1.25rem;
}

.gallery-item {
  background: var(--depth);
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  transition: border-color 0.15s ease;
}

.gallery-item:hover { border-color: var(--sonar-dim); }

.gallery-item img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
}

/* The grid macro serves both the curated index and the capture archive. Only
   the archive holds cropped 4:3 frames; the curated sections are 16:10 SVGs
   and keep the tile above. */
.capture-section .gallery-item img {
  aspect-ratio: 4 / 3;
}

.gallery-item figcaption {
  padding: 0.6rem 0.85rem;
  font-size: 0.85rem;
  color: var(--mist);
}

figure { margin: 0; }

/* --------------------------------------------------------------------------
   Lightbox — the viewer behind every gallery thumbnail.

   A native <dialog>, so it lands in the top layer and needs no z-index at all;
   #depth-gauge (50) and .site-header (40) are underneath it for free. Two
   constraints from the ambience rules above carry over: the overlay is a
   near-opaque fill rather than a backdrop blur, and only transform animates.
   -------------------------------------------------------------------------- */

/* The click target inside .gallery-item / .capture-grid. Both grids style
   their images by class, so this wrapper must not become a box of its own. */
.gallery-trigger {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: zoom-in;
}

dialog.lightbox {
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
  /* The fill lives on the dialog rather than only on ::backdrop, which Chrome
     composites too lightly to hide a bright page underneath. Near-opaque, not
     a backdrop blur — see the ambience rules above. */
  background: rgba(7, 11, 30, 0.97);
  color: var(--foam);
}

dialog.lightbox[open] {
  display: flex;
  flex-direction: column;
}

.lightbox::backdrop { background: rgba(7, 11, 30, 0.97); }

/* Nothing above these layers is visible while the viewer is up, and they are
   fixed and full-viewport — leaving them running would repaint the whole
   screen forever behind an opaque overlay. */
html.lightbox-open .caustics,
html.lightbox-open .porthole::before { animation-play-state: paused; }

.lightbox-stage {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(0.5rem, 2.5vw, 2rem);
  overflow: hidden;
  touch-action: none; /* the gestures below are ours, not the browser's */
}

/* Fills the stage in both directions, scaling a small source *up* rather than
   stranding it in the middle of a full-screen overlay. The element box is the
   whole stage and the picture is letterboxed inside it, so the pan clamping and
   the tap-to-close hit test can't use this element's rect directly — measure()
   in lightbox.js reconstructs the painted rect from naturalWidth/Height.
   (height:100% + width:auto + max-width:100% looks like it would keep the box
   tight instead, but Chrome caps the width without re-deriving the height and
   the frame comes out stretched.) */
.lightbox-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform-origin: 0 0;
  will-change: transform;
  transition: transform 0.18s ease, opacity 0.15s ease;
  user-select: none;
  -webkit-user-drag: none;
}

/* Mid-gesture the transform is driven frame by frame; a transition on top of
   that just adds lag. */
.lightbox-stage.is-interacting .lightbox-img { transition: none; }
.lightbox-stage.is-loading .lightbox-img { opacity: 0.65; }
.lightbox-stage.is-zoomed { cursor: grab; }
.lightbox-stage.is-zoomed.is-interacting { cursor: grabbing; }

.lightbox-close,
.lightbox-step {
  position: absolute;
  display: grid;
  place-items: center;
  background: rgba(16, 23, 58, 0.85);
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--foam);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, opacity 0.15s ease;
}

.lightbox-close:hover,
.lightbox-step:hover:not(:disabled) { border-color: var(--sonar); color: var(--sonar); }

.lightbox-step:disabled { opacity: 0.25; cursor: default; }

.lightbox-close {
  top: 0.9rem;
  right: 0.9rem;
  width: 2.6rem;
  height: 2.6rem;
  font-size: 1.6rem;
  line-height: 1;
}

.lightbox-step {
  top: 50%;
  transform: translateY(-50%);
  width: 2.8rem;
  height: 4.4rem;
  font-size: 2rem;
  line-height: 1;
}

.lightbox-prev { left: 0.9rem; }
.lightbox-next { right: 0.9rem; }

.lightbox-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem 1.25rem;
  flex-wrap: wrap;
  padding: 0.85rem clamp(0.9rem, 3vw, 1.6rem);
  border-top: 1px solid var(--line);
  background: var(--depth);
}

.lightbox-caption {
  flex: 1 1 12rem;
  min-width: 0;
  margin: 0;
  font-size: 0.9rem;
  color: var(--foam);
}

.lightbox-count {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  color: var(--sonar-dim);
  white-space: nowrap;
}

.lightbox-zoom {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: 3px;
  overflow: hidden;
}

.lightbox-zoom button {
  min-width: 2.4rem;
  padding: 0.4rem 0.6rem;
  background: none;
  border: 0;
  font-family: var(--mono);
  font-size: 0.9rem;
  color: var(--mist);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.lightbox-zoom button:hover { background: var(--depth-2); color: var(--foam); }

.lightbox-zoom .lightbox-level {
  min-width: 4.2rem;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  border-inline: 1px solid var(--line);
  color: var(--sonar);
}

@media (max-width: 620px) {
  .lightbox-close { top: 0.5rem; right: 0.5rem; }
  .lightbox-prev { left: 0.35rem; }
  .lightbox-next { right: 0.35rem; }
  .lightbox-step { width: 2.4rem; height: 3.6rem; }
  .lightbox-caption { flex-basis: 100%; font-size: 0.82rem; }
}

/* The steppers are a mouse affordance; on touch the swipe is the gesture and
   the buttons only sit in the way of the image. */
@media (hover: none) and (pointer: coarse) {
  .lightbox-step { display: none; }
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.site-footer {
  position: relative; z-index: 1;
  border-top: 1px solid var(--line);
  padding-block: 1.75rem 2.25rem;
  margin-top: auto;
}

.site-footer p {
  margin: 0 0 0.35rem;
  font-family: var(--mono);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--sonar-dim);
}

.site-footer .fine-print {
  font-family: var(--sans);
  text-transform: none;
  letter-spacing: normal;
  font-size: 0.8rem;
  color: var(--mist);
  max-width: 72ch;
}
