/* ---------- Title detail ----------
   A pushed screen over the tab shell, not a tab: it owns the whole viewport,
   scrolls internally, and slides in from the right. */
.detail-layer {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background: var(--color-bg);
  opacity: 0;
  visibility: hidden;
  transform: translate3d(2rem, 0, 0);
  pointer-events: none;
  transition:
    opacity var(--duration-base) var(--ease-standard),
    transform var(--duration-base) var(--ease-standard),
    visibility 0s linear var(--duration-base);
}

.detail-layer.is-open {
  opacity: 1;
  visibility: visible;
  transform: translate3d(0, 0, 0);
  pointer-events: auto;
  transition:
    opacity var(--duration-base) var(--ease-standard),
    transform var(--duration-base) var(--ease-standard),
    visibility 0s linear 0s;
}

.detail-view {
  position: absolute;
  inset: 0;
}

.dt-body {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  padding-bottom: var(--space-8);
}

/* Floating back button — the only chrome over the backdrop. */
.dt-back {
  position: absolute;
  top: calc(env(safe-area-inset-top) + var(--space-3));
  inset-inline-start: var(--container-padding);
  z-index: 3;
  background: rgba(8, 9, 11, 0.55);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--color-border);
}

/* Share + Downloads + Follow — same row as the back button, opposite corner. */
.dt-top-actions {
  position: absolute;
  top: calc(env(safe-area-inset-top) + var(--space-3));
  inset-inline-end: var(--container-padding);
  z-index: 3;
  display: flex;
  gap: var(--space-2);
}

.dt-top-actions .icon-btn {
  background: rgba(8, 9, 11, 0.55);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--color-border);
}

.dt-follow.is-following {
  color: var(--color-accent-strong);
  border-color: rgba(236, 194, 127, 0.4);
  background: rgba(215, 163, 92, 0.14);
}

/* ---------- Hero ---------- */
.dt-hero {
  position: relative;
  padding-bottom: var(--space-4);
}

.dt-hero-media {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 46vh;
  overflow: hidden;
  background: var(--color-bg-elevated);
}

.dt-hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 28%;
  opacity: 0;
  filter: brightness(1.06) saturate(1.04);
  transition: opacity var(--duration-slow) var(--ease-standard);
}

.dt-hero-media img.is-loaded {
  opacity: 1;
}

/* Poster fallback when a title has no landscape backdrop. */
.dt-hero-media img.is-poster {
  object-position: center 18%;
  filter: brightness(1.02) saturate(1.02) blur(2px);
}

.dt-hero-media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(6, 7, 10, 0.55) 0%,
    rgba(6, 7, 10, 0.1) 30%,
    rgba(6, 7, 10, 0.55) 72%,
    var(--color-bg) 100%
  );
}

/* Poster + title overlap the bottom of the backdrop. */
.dt-hero-content {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  gap: var(--space-4);
  margin-top: clamp(-5rem, -14vw, -3rem);
  padding-inline: var(--container-padding);
}

.dt-poster {
  flex-shrink: 0;
  width: clamp(5.5rem, 26vw, 9rem);
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  box-shadow: var(--shadow-elevated);
}

.dt-poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-standard);
}

.dt-poster img.is-loaded {
  opacity: 1;
}

.dt-info {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-bottom: var(--space-1);
}

.dt-title {
  font-size: var(--fs-xl);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.12;
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.5);
}

.dt-fa-title {
  font-size: var(--fs-sm);
  color: var(--color-text-secondary);
  text-align: start;
}

.dt-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  font-size: var(--fs-sm);
  color: var(--color-text-secondary);
}

.dt-rating {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: 700;
  color: var(--color-rating);
}

.dt-rating svg {
  width: 15px;
  height: 15px;
}

.dt-votes {
  font-weight: 500;
  color: var(--color-text-tertiary);
}

.dt-type {
  padding: 1px var(--space-2);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border-strong);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.dt-genres {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Was previously the shared `.pill` class, which was orphaned when the hero was
   rewritten — scoped here now so it can't silently lose its styles again. */
.dt-genre {
  padding: 3px var(--space-3);
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--color-border-strong);
  font-size: var(--fs-xs);
  color: var(--color-text-secondary);
  text-transform: capitalize;
}

/* ---------- Main column ---------- */
/* ---------- Last Watched card ----------
   A horizontal rectangular card sitting between the hero and the action
   row — its own component (js/components/detail/lastWatched.js), distinct
   from the vertical rail card (.card / .progress-track in components.css)
   since the title-page spec calls for a wider layout with more metadata. */
.lw-card {
  display: flex;
  align-items: stretch;
  gap: var(--space-3);
  width: calc(100% - 2 * var(--container-padding));
  margin: var(--space-4) var(--container-padding) 0;
  padding: var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  text-align: start;
  transition: background-color var(--duration-fast) var(--ease-standard);
}

.lw-card:hover,
.lw-card:focus-visible {
  background: var(--color-surface-hover);
}

.lw-card:active {
  transform: scale(0.995);
}

.lw-poster {
  position: relative;
  flex-shrink: 0;
  width: 4.5rem;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--color-bg-elevated);
}

.lw-poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-standard);
}

.lw-poster img.is-loaded {
  opacity: 1;
}

.lw-play-badge {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(4, 5, 7, 0.32);
  color: #fff;
}

.lw-play-badge svg {
  width: 20px;
  height: 20px;
}

.lw-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
}

.lw-eyebrow {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-accent-strong);
}

.lw-name {
  font-size: var(--fs-base);
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lw-meta {
  display: flex;
  gap: var(--space-2);
  font-size: var(--fs-xs);
  color: var(--color-text-secondary);
  font-variant-numeric: tabular-nums;
}

/* "mm:ss / mm:ss" is pure digits/punctuation — no strong-direction character
 * for the bidi algorithm to anchor on inside RTL flow, so the two halves
 * visually swap around the "/" without this (same class of bug as
 * front/CLAUDE.md gotcha #33's "@username" case). Always reads elapsed-then-total. */
.lw-time {
  direction: ltr;
  unicode-bidi: embed;
}

.lw-progress-track {
  margin-top: var(--space-2);
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--color-bg-elevated);
  overflow: hidden;
}

.lw-progress-fill {
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--color-accent-strong);
}

.dt-main,
.dt-slot > .dt-section {
  padding-inline: var(--container-padding);
}

.dt-main {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding-top: var(--space-5);
}

.dt-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.dt-actions .btn {
  min-height: var(--tap-min);
  flex: 1 1 auto;
}

.dt-actions .btn svg {
  width: 18px;
  height: 18px;
}

/* `.dt-actions .btn` above is two classes; a plain `.dt-watch` is one, so
   its flex-basis was silently losing the cascade to the rule above
   regardless of source order (higher specificity always wins) — Watch Now
   was sharing a row with Download instead of getting its own full-width
   line on mobile as intended. Matching `.dt-actions .btn`'s specificity
   fixes it; the media-query override below needs the same bump. */
.dt-actions .dt-watch {
  flex-basis: 100%;
}

.dt-actions .btn[disabled] {
  opacity: 0.45;
  pointer-events: none;
}

.dt-watchlist.is-saved {
  color: var(--color-accent-strong);
  border-color: rgba(236, 194, 127, 0.4);
  background: rgba(215, 163, 92, 0.14);
}

/* ---------- Synopsis ---------- */
.dt-synopsis-text {
  font-size: var(--fs-base);
  color: var(--color-text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.dt-synopsis.is-expanded .dt-synopsis-text {
  -webkit-line-clamp: unset;
}

.dt-synopsis-toggle {
  margin-top: var(--space-2);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--color-accent-strong);
  min-height: var(--tap-min);
}

/* ---------- Sections ---------- */
.dt-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding-top: var(--space-6);
}

.dt-section-title {
  position: relative;
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: -0.01em;
  padding-inline-start: 10px;
}

.dt-section-title::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  top: 0.15em;
  bottom: 0.15em;
  width: 3px;
  border-radius: var(--radius-full);
  background: var(--color-accent);
}

.dt-note {
  font-size: var(--fs-sm);
  color: var(--color-text-tertiary);
  padding: var(--space-4);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-md);
}

/* Rails inside sections manage their own gutter, so cancel the section's. */
.dt-section-body > .row-wrap {
  margin-inline: calc(var(--container-padding) * -1);
}

/* ---------- Cast ----------
   Exactly four top-billed actors in one row at every width — the row is a fixed
   4-column grid rather than a wrapping list, so it never reflows to 2+2. */
.dt-cast {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
}

.dt-cast-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-1) 0;
  border-radius: var(--radius-md);
  transition: transform var(--duration-fast) var(--ease-standard);
}

.dt-cast-item:active {
  transform: scale(0.95);
}

.dt-cast-photo {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  max-width: 4.5rem;
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  transition: border-color var(--duration-base) var(--ease-standard),
    box-shadow var(--duration-base) var(--ease-standard);
}

.dt-cast-item:hover .dt-cast-photo,
.dt-cast-item:focus-visible .dt-cast-photo {
  border-color: rgba(236, 194, 127, 0.55);
  box-shadow: 0 0 0 3px rgba(215, 163, 92, 0.16);
}

/* The photo is layered over the initial, so a failed/missing portrait simply
   leaves the initial showing instead of a blank circle. */
.dt-cast-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-standard);
}

.dt-cast-photo img.is-loaded {
  opacity: 1;
}

.dt-cast-initial {
  font-size: var(--fs-md);
  font-weight: 800;
  color: var(--color-accent-strong);
}

.dt-cast-name {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--color-text-secondary);
  text-align: center;
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---------- Trailers ---------- */
.dt-trailer {
  flex: 0 0 auto;
  width: clamp(11rem, 46vw, 15rem);
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  transition: transform var(--duration-fast) var(--ease-standard);
}

.dt-trailer:active {
  transform: scale(0.97);
}

.dt-trailer-thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}

.dt-trailer-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-standard);
}

.dt-trailer-thumb img.is-loaded {
  opacity: 1;
}

.dt-trailer-play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #fff;
  background: rgba(6, 7, 10, 0.28);
}

.dt-trailer-play svg {
  width: 34px;
  height: 34px;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.7));
}

.dt-trailer-name {
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dt-trailer-meta {
  font-size: var(--fs-xs);
  color: var(--color-text-tertiary);
}

/* ---------- Subtitles ---------- */
.dt-subs {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  background: var(--color-bg-elevated);
  overflow: hidden;
}

.dt-sub {
  padding: var(--space-3) var(--space-4);
}

.dt-sub + .dt-sub {
  border-top: 1px solid var(--color-border);
}

.dt-sub-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.dt-sub-name {
  font-size: var(--fs-sm);
  font-weight: 600;
  word-break: break-all;
}

.dt-sub-meta {
  font-size: var(--fs-xs);
  color: var(--color-text-tertiary);
}

.dt-more-btn {
  align-self: flex-start;
  min-height: var(--tap-min);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--color-accent-strong);
}

/* ---------- Skeleton / states ---------- */
.dt-hero-skeleton .dt-hero-media {
  background: var(--color-surface);
}

.dt-skeleton-lines {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5) var(--container-padding) 0;
}

.dt-skeleton-lines .skeleton-line {
  height: 14px;
  border-radius: 5px;
}

.dt-skeleton-row {
  display: flex;
  gap: var(--space-3);
  overflow: hidden;
  padding: var(--space-6) var(--container-padding) 0;
}

.dt-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: 70vh;
  padding: var(--space-6);
  text-align: center;
}

.dt-state-icon svg {
  width: 34px;
  height: 34px;
  color: var(--color-text-tertiary);
}

.dt-state-title {
  font-size: var(--fs-md);
  font-weight: 700;
}

.dt-state-msg {
  font-size: var(--fs-sm);
  color: var(--color-text-secondary);
  max-width: 34ch;
}

.dt-state .btn {
  margin-top: var(--space-3);
}

.dt-state .btn svg {
  width: 16px;
  height: 16px;
}

/* ---------- Wider screens ---------- */
@media (min-width: 768px) {
  .dt-hero-media {
    max-height: 56vh;
  }

  .dt-hero-content {
    max-width: var(--container-max);
    margin-inline: auto;
    gap: var(--space-5);
  }

  .dt-title {
    font-size: var(--fs-2xl);
  }

  .dt-main,
  .dt-slot > .dt-section {
    max-width: var(--container-max);
    margin-inline: auto;
  }

  .dt-actions .btn {
    flex: 0 0 auto;
  }

  /* Same specificity as the base fix above, so this correctly wins here too. */
  .dt-actions .dt-watch {
    flex-basis: auto;
  }

  .dt-synopsis-text {
    max-width: 70ch;
  }
}

@media (prefers-reduced-motion: reduce) {
  .detail-layer {
    transition: opacity var(--duration-fast) linear, visibility 0s;
    transform: none;
  }
}

/* The tab bar belongs to the tabs — a pushed detail screen owns the viewport. */
.app-shell.has-detail .bottom-nav {
  opacity: 0;
  transform: translateY(120%);
  pointer-events: none;
  transition: opacity var(--duration-base) var(--ease-standard),
    transform var(--duration-base) var(--ease-standard);
}

/* ---------- Actor profile ---------- */
.ac-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: calc(env(safe-area-inset-top) + 4.5rem) var(--container-padding) 0;
}

.ac-photo {
  position: relative;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: clamp(5rem, 24vw, 7.5rem);
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  box-shadow: var(--shadow-elevated);
}

.ac-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-standard);
}

.ac-photo img.is-loaded {
  opacity: 1;
}

.ac-initial {
  font-size: var(--fs-2xl);
  font-weight: 800;
  color: var(--color-accent-strong);
}

.ac-head-lines {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  flex: 1;
}

.ac-meta {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.ac-name {
  font-size: var(--fs-xl);
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.15;
}

.ac-fa-name {
  font-size: var(--fs-base);
  color: var(--color-text-secondary);
  text-align: start;
}

.ac-birth {
  font-size: var(--fs-sm);
  color: var(--color-text-tertiary);
}

.ac-main {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5) var(--container-padding) 0;
}

/* Houses the type-filter pills (when present) and the sort trigger on one
   row — the trigger's own `margin-inline-start: auto` (not `justify-content:
   space-between` on this wrapper) is what pushes it to the trailing edge
   regardless of whether the filter pills are present, since space-between
   with a single child has nothing to space against. */
.ac-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.ac-toolbar .ac-filters {
  flex: 1 1 auto;
  min-width: 0;
}

.ac-toolbar .ex-sort-trigger {
  flex: 0 0 auto;
  margin-inline-start: auto;
}

.ac-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.ac-filter {
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  background: var(--color-bg-elevated);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--color-text-secondary);
  transition: background-color var(--duration-base) var(--ease-standard),
    border-color var(--duration-base) var(--ease-standard),
    color var(--duration-base) var(--ease-standard);
}

.ac-filter.is-active {
  background: linear-gradient(180deg, rgba(215, 163, 92, 0.26), rgba(215, 163, 92, 0.12));
  border-color: rgba(236, 194, 127, 0.5);
  color: var(--color-accent-strong);
}

/* Filmography is a vertical list, reusing Explore's row so the two stay
   visually identical (thumb, titles, rating/year/type). */
.ac-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* ---------- Reveal-on-scroll ----------
   Rows start hidden and are revealed by an IntersectionObserver as they reach
   the viewport, staggered so a batch reads as one wave rather than a pop. */
.ac-list .ac-row {
  opacity: 0;
  transform: translateY(14px);
}

.ac-list .ac-row.is-in {
  animation: ac-row-in 360ms var(--ease-standard) both;
  animation-delay: var(--stagger, 0ms);
}

@keyframes ac-row-in {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to { opacity: 1; transform: none; }
}

/* A single warm light sweeping across the row as it lands. `overflow: hidden`
   keeps the gradient inside the rounded corners; an element's own outline isn't
   clipped by it, so the focus ring survives. */
.ac-list .ac-row {
  position: relative;
  overflow: hidden;
}

.ac-list .ac-row::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    105deg,
    transparent 32%,
    rgba(236, 194, 127, 0.20) 47%,
    rgba(255, 255, 255, 0.12) 53%,
    transparent 68%
  );
  transform: translateX(-120%);
}

.ac-list .ac-row.is-in::after {
  animation: ac-sweep 620ms var(--ease-standard) both;
  animation-delay: calc(var(--stagger, 0ms) + 70ms);
}

@keyframes ac-sweep {
  0% { transform: translateX(-120%); opacity: 0; }
  18% { opacity: 1; }
  100% { transform: translateX(120%); opacity: 0; }
}

/* Reduced motion: rows are revealed by JS instead, with no movement or sweep. */
@media (prefers-reduced-motion: reduce) {
  .ac-list .ac-row,
  .ac-list .ac-row.is-in {
    opacity: 1;
    transform: none;
    animation: none;
  }

  .ac-list .ac-row::after {
    display: none;
  }
}

.ac-more {
  align-self: center;
  min-height: var(--tap-min);
  margin-top: var(--space-2);
}

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

@media (min-width: 768px) {
  .ac-head,
  .ac-main {
    max-width: var(--container-max);
    margin-inline: auto;
  }

  .ac-name {
    font-size: var(--fs-2xl);
  }
}

/* ---------- Share sheet ----------
   A small bottom sheet with explicit share choices. Slides up from the
   bottom on phones (the standard share-sheet position); becomes a small
   centered card on wider screens where a full-width bottom sheet would look
   like an accident. */
.sh-layer {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-overlay) + 2);
  pointer-events: none;
}

.sh-scrim {
  position: absolute;
  inset: 0;
  background: rgba(4, 5, 7, 0.62);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-standard);
}

.sh-panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--color-bg-elevated);
  border-top: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: 0 -24px 64px rgba(0, 0, 0, 0.55);
  padding: var(--space-4) var(--space-4) calc(var(--space-5) + env(safe-area-inset-bottom));
  transform: translate3d(0, 100%, 0);
  transition: transform var(--duration-base) var(--ease-standard);
}

.sh-layer.is-open {
  pointer-events: auto;
}

.sh-layer.is-open .sh-scrim {
  opacity: 1;
}

.sh-layer.is-open .sh-panel {
  transform: translate3d(0, 0, 0);
}

.sh-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-3);
}

.sh-title {
  font-size: var(--fs-md);
  font-weight: 800;
  letter-spacing: -0.01em;
}

.sh-options {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.sh-option {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-2);
  border-radius: var(--radius-md);
  color: var(--color-text);
  text-align: start;
  transition: background-color var(--duration-fast) var(--ease-standard);
}

.sh-option:hover,
.sh-option:focus-visible {
  background: var(--color-surface-hover);
}

.sh-option:active {
  transform: scale(0.99);
}

.sh-option-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}

.sh-option-icon svg {
  width: 1.15rem;
  height: 1.15rem;
}

.sh-option-label {
  font-size: var(--fs-base);
  font-weight: 600;
}

@media (min-width: 640px) {
  .sh-panel {
    inset-inline-start: auto;
    bottom: auto;
    top: 50%;
    inset-inline-end: var(--container-padding);
    width: min(100%, 22rem);
    border-radius: var(--radius-lg);
    border: 1px solid var(--color-border-strong);
    transform: translate3d(0, -46%, 0);
  }

  .sh-layer.is-open .sh-panel {
    transform: translate3d(0, -50%, 0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .sh-panel {
    transition: transform var(--duration-fast) linear;
  }
}

/* ---------- Collection details page ----------
   Reuses .dt-hero/.dt-main/.dt-section (same pushed-screen shell as title/
   actor) — only the grid of titles below the hero is collection-specific. */
.cl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: var(--space-3) var(--space-2);
}

/* `createTitleCard()` returns a `.card-tile` wrapper (the watchlist heart has
   to be a sibling of the card button, not a child — see card.js) — that
   wrapper is what carries the rail width, so it's the one to unpin here so
   grid tracks size the tiles instead. */
.cl-grid .card-tile,
.cl-grid .card {
  width: auto;
}

@media (min-width: 768px) {
  .cl-grid {
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: var(--space-4);
  }
}
