/* ==========================================================================
   modal.css — detail modal
   --------------------------------------------------------------------------
   Figma: section 9633:295441 "Modal", three tab states of one component
     9633:291237  Detail — Rail Modal (Show)   -> Cast & Crew
     9633:291321  Detail — List Modal (Show)   -> Episodes
     9633:291460  Detail — More Like This      -> poster grid

   The frame is 1004 wide, centred in the 1440 artboard (218px either side),
   on a #1a1a1a surface with an 8px radius. Inside, a single column:
   hero -> meta -> segmented control -> tab body. A 40px close button floats
   at the top right of the dialog.

   DELIBERATE DEPARTURES, all of them legibility (the brief asked for it):

   1. The synopsis text node in the design is 1868px wide inside a 1004px
      frame, so on the canvas it runs off the edge mid-word. Here it wraps
      inside the 884px content column.
   2. The episode synopsis has the same problem on one variant (790px in a
      551px box). Wrapped.
   3. Tag chips are 10px in the design, which is below the smallest size
      used anywhere else on the screen. Set to 12px.
   4. The season blurb resolves to 12px from the design's inline fallback;
      the variable collection puts body/s at 14px, which is what is used.
   5. The segmented control is sticky inside the scroller. The design is a
      static frame, but the real content is taller than any viewport, and a
      tab bar you cannot get back to is not a tab bar.
   ========================================================================== */

.modal {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  justify-content: center;
  padding: 64px 24px;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: rgba(10, 10, 10, 0.72);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-modal-out) var(--ease-morph-out);
}

.modal.is-open {
  opacity: 1;
  pointer-events: auto;
  transition: opacity var(--dur-modal-in) var(--ease-morph-in);
}

/* The dialog arrives from slightly below and under-scale, the same
   "leaves from rest, arrives at rest" curve the hover previews use, so the
   two pieces of motion in this prototype feel like one hand. */
.modal__dialog {
  position: relative;
  width: min(1004px, 100%);
  max-width: 1004px;
  align-self: flex-start;
  background: var(--color-surface-container);        /* #1a1a1a */
  border-radius: var(--radius-s);                    /* 8px */
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.55),
              0 8px 24px rgba(0, 0, 0, 0.4);
  opacity: 0;
  transform: translateY(24px) scale(0.972);
  transform-origin: center top;
  transition: opacity var(--dur-modal-out) var(--ease-morph-out),
              transform var(--dur-modal-out) var(--ease-morph-out);
}

.modal.is-open .modal__dialog {
  opacity: 1;
  transform: none;
  transition: opacity var(--dur-modal-in) var(--ease-morph-in),
              transform var(--dur-modal-in) var(--ease-morph-in);
}

/* Deliberately NOT `overflow: hidden`. An overflow ancestor is a scroll
   container, and `position: sticky` resolves against the nearest scrolling
   ancestor — a wrapper here would trap the tab bar in a box that never
   scrolls, and it would simply never stick. The hero rounds its own top
   corners instead; the bottom two are the dialog's own radius over a solid
   ground, with nothing painting into them. */
.modal__scroll { border-radius: inherit; }

/* ---- Close ---------------------------------------------------------------
   Sits on the dialog, not in the scroller, so it never travels off the top
   of a long page. Its own ground is opaque enough to hold a 24px glyph over
   whatever the artwork happens to be doing behind it. */
.modal__close {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: rgba(26, 26, 26, 0.8);
  cursor: pointer;
  transition: background-color var(--dur-ring) ease;
}

.modal__close:hover,
.modal__close:focus-visible {
  background: rgba(26, 26, 26, 0.95);
  outline: none;
  box-shadow: 0 0 0 2px var(--accent-ring-hover);
}

.modal__close img { display: block; width: 24px; height: 24px; max-width: none; }

/* ---- Hero ---------------------------------------------------------------- */

/* 16:9, not the design's 1200/420 letterbox. The frame has to hold a
   trailer, and cropping a 16:9 clip into a 2.86:1 band throws away a third
   of the picture. Every backdrop in the set is natively 16:9 too, so this
   is also the shape they were framed for. */
.modal__hero {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius-s) var(--radius-s) 0 0;
  background: #000;
}

/* The image drifts as the dialog scrolls, the same idea as the page hero.
   The over-scale is what makes it safe: the plate is 12% larger than the
   frame, so a drift of up to half that can never uncover an edge. */
.modal__backdrop,
.modal__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;          /* never `fill` */
  max-width: none;
  display: block;
  transform: translate3d(0, var(--modal-parallax, 0px), 0) scale(1.12);
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .modal__backdrop,
  .modal__video { transform: scale(1.12); }
}

/* Same rhythm as the page hero: the still is read first, the clip resolves
   in over it, and it resolves back before the clip runs out — so the frame
   always comes to rest on a picture. */
.modal__video {
  opacity: 0;
  transition: opacity var(--dur-hero-video-out) ease;
}

.modal__hero.video-ready .modal__video {
  opacity: 1;
  transition: opacity var(--dur-hero-video-in) ease;
}

.modal__hero-scrim {
  position: absolute;
  inset: 0;
  background-image: linear-gradient(to bottom,
    rgba(0, 0, 0, 0) 28.125%, var(--color-surface-container) 100%);
}

.modal__hero-inner {
  position: absolute;
  left: 60px;
  bottom: 25.4px;
  width: 600px;
  max-width: calc(100% - 120px);
  display: flex;
  flex-direction: column;
  gap: var(--space-l);                               /* 16px */
}

.modal__logo {
  width: 360px;
  max-width: 100%;
  height: 103px;
  display: block;
  transform-origin: left bottom;
  transition: transform var(--dur-cinema) var(--ease-morph-in),
              opacity var(--dur-cinema) var(--ease-morph-in);
}

/* While the trailer is genuinely showing and the pointer has gone quiet,
   the lockup steps back so the picture has the frame. The actions stay —
   they are what the panel is for. */
.modal__hero.is-cinema .modal__logo { transform: scale(0.62); }

/* The scrim is NOT faded while the trailer plays. It used to drop to 0.75
   to show more of the clip, but this gradient is also the seam between the
   hero and the panel below it — at 0.75 the bottom stops reaching
   --color-surface-container and a hard edge appears across the join, with
   video bleeding into it. The gradient is already clear for its first
   28%, which is where the picture is; cinema mode earns its room from the
   logo shrinking instead. */

.modal__logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: left bottom;
  max-width: none;
  filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.7));
}

/* A title with no lockup in the asset set is set, rather than faked. */
.modal__logo--text {
  display: flex;
  align-items: flex-end;
  height: auto;
  min-height: 56px;
  color: var(--color-on-surface);
  font-size: 44px;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.7);
}

.modal__cta-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-m);                               /* 12px */
}

.modal__play {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-m);
  width: 230px;
  height: 44px;
  padding: var(--space-m) var(--space-l);
  border: 0;
  border-radius: var(--radius-s);
  background: var(--color-primary);
  color: var(--color-on-primary);
  font-family: inherit;
  font-size: var(--type-body-m-size);                /* 16px */
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  transition: filter var(--dur-ring) ease;
}

.modal__play:hover,
.modal__play:focus-visible { filter: brightness(1.08); outline: none; }
.modal__play img { display: block; width: 20px; height: 20px; max-width: none; }

.modal__round {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-m);                           /* 12px */
  border: 1px solid var(--color-primary-30);
  border-radius: var(--radius-full);
  background: var(--color-surface);                  /* #282828 */
  cursor: pointer;
  transition: border-color var(--dur-ring) ease,
              background-color var(--dur-ring) ease;
}

.modal__round:hover,
.modal__round:focus-visible {
  border-color: var(--accent-ring-hover);
  background: #2f2f2f;
  outline: none;
}

.modal__round img { display: block; width: 20px; height: 20px; max-width: none; }

/* Audio, bottom right of the frame — clear of the lockup and the actions,
   and only present when the title actually has a clip. */
.modal__mute {
  position: absolute;
  right: 24px;
  bottom: 24px;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: var(--radius-full);
  background: rgba(18, 18, 18, 0.45);
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
  cursor: pointer;
  transition: background-color var(--dur-ring) ease, border-color var(--dur-ring) ease;
}

.modal__mute:hover,
.modal__mute:focus-visible {
  background: rgba(18, 18, 18, 0.7);
  border-color: var(--accent-ring-hover);
  outline: none;
}

.modal__mute img { display: block; width: 22px; height: 22px; max-width: none; opacity: 0.9; }

/* ---- Meta strip ---------------------------------------------------------- */

.modal__meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--space-s) 60px var(--space-2xl);     /* 8px 60 24 */
}

.modal__facts {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  color: var(--color-white-80);
  font-size: var(--type-body-m-size);                /* 16px */
  line-height: 21px;
  letter-spacing: -0.0094em;
}

/* Departure 1: the design's node is 1868px wide in a 1004px frame. */
.modal__synopsis {
  max-width: 884px;
  color: var(--color-on-surface);
  font-size: var(--type-body-m-size);                /* 16px */
  line-height: var(--type-line-height);
}

/* ---- Segmented control --------------------------------------------------- */

/* NOT sticky. It was, so the bar could scroll up over the episode list and
   sit across the artwork — content passing behind a floating strip, which
   is worse than having to scroll back for it. It stays where it belongs in
   the column. */
.modal__tabs {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  height: 48px;
  padding: 0 64px;
  background: var(--color-surface-container);
  border-bottom: 0.5px solid var(--color-outline);
  overflow-x: auto;
  scrollbar-width: none;
}

.modal__tabs::-webkit-scrollbar { display: none; }

.modal__tab {
  position: relative;
  flex: 0 0 auto;
  padding: var(--space-m) var(--space-l);            /* 12px 16px */
  border: 0;
  background: none;
  color: var(--color-on-surface-variant);            /* #989898 */
  font-family: inherit;
  font-size: var(--type-body-m-size);                /* 16px */
  font-weight: 500;
  line-height: 24px;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--dur-ring) ease;
}

.modal__tab:hover { color: var(--color-on-surface); }
.modal__tab:focus-visible { outline: none; color: var(--color-on-surface); }
.modal__tab:focus-visible::before {
  content: '';
  position: absolute;
  inset: 4px 6px;
  border-radius: var(--radius-xs);
  box-shadow: 0 0 0 2px var(--accent-ring-hover);
}

.modal__tab[aria-selected="true"] {
  color: var(--color-on-surface);
  font-weight: 700;
}

/* The indicator is one element that slides between tabs, rather than a
   bar appearing under whichever tab happens to be current. */
.modal__tab-rule {
  position: absolute;
  /* `left: 0` is load-bearing. Without it the rule takes its static
     position — last item in the flex row, i.e. after the final tab — and
     every translateX then lands one tab-strip too far right. */
  left: 0;
  bottom: 0;
  height: 3px;
  background: var(--color-primary);
  border-radius: 9999px 9999px 0 0;
  transition: transform var(--dur-tab) var(--ease-morph-in),
              width var(--dur-tab) var(--ease-morph-in);
}

/* ---- Tab bodies ---------------------------------------------------------- */

.modal__body { padding: 32px 60px; }

.modal__panel[hidden] { display: none; }

/* Switching tabs used to swap one panel for another of a different
   height, so the dialog snapped to its new size and the whole page under
   the pointer jumped. The body now animates between the two heights while
   the outgoing panel fades out and the incoming one fades up in its
   place — one move, not a cut plus a resize.

   The height is set in px by js/modal.js for the length of the change and
   then released back to `auto`, so nothing is pinned afterwards. */
.modal__body {
  position: relative;
  overflow: hidden;
  transition: height var(--dur-tab-swap) var(--ease-morph-in);
}

.modal__body.is-swapping { will-change: height; }

/* First paint of a panel — no swap in progress, so it simply resolves. */
.modal__panel {
  animation: modal-panel-in var(--dur-chrome) var(--ease-meta-in) both;
}

/* The outgoing panel is taken out of the flow so the two never stack and
   double the body's height mid-swap. */
.modal__panel--leaving {
  position: absolute;
  inset: 32px 60px auto;
  pointer-events: none;
  animation: modal-panel-out var(--dur-tab-out) var(--ease-morph-out) both;
}

/* The incoming panel waits for the outgoing one to finish leaving. Both
   used to run at once, which is what made the two sets read as
   overlapping — one was still at half opacity while the other was already
   at half opacity on top of it. `both` holds it at opacity 0 through the
   delay, so there is genuinely nothing there until the old set has gone. */
.modal__panel--entering {
  animation: modal-panel-in var(--dur-tab-in) var(--ease-meta-in)
             var(--dur-tab-out) both;
}

/* REVERTED from a sideways slide (see source.md §29). Travelling in from
   the dialog's edge was too harsh for a panel this size — it read as the
   content being thrown in rather than changing. Back to the soft vertical
   resolve, which the height animation carries anyway. */
@keyframes modal-panel-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

@keyframes modal-panel-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-8px); }
}

.modal__h3 {
  margin: 0 0 var(--space-2xl);                      /* 24px */
  color: var(--color-on-surface);
  font-size: 24px;
  font-weight: 700;
  line-height: 36px;
  letter-spacing: 0.0029em;
}

.modal__section + .modal__section { margin-top: 48px; }

/* Cast & Crew ------------------------------------------------------------
   A grid of faces, not a list of names. On a detail page the question is
   usually "who is that?", and a text row does not answer it. The frames
   are circular so a 2:3 headshot crops to the face; anyone without a
   portrait on file gets an initialled circle, which is a designed state
   rather than a hole. */

.modal__people {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--space-2xl) var(--space-l);
}

.modal__person {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-m);
}

.modal__person-photo {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  border-radius: var(--radius-full);
  overflow: hidden;
  background: var(--color-surface);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}

/* object-position and scale are set per portrait by js/modal.js — see
   FRAMING there, and tools/portraits.html for how they were arrived at.
   transform-origin sits ON the eye rule, so a zoom resizes the head
   without moving the eyes off the line. */
.modal__person-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 34%;
  transform-origin: center 40%;
  max-width: none;
  display: block;
}

.modal__person-photo--initial {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-on-surface-variant);
  font-size: 22px;
  font-weight: var(--type-weight-semi-bold);
  letter-spacing: 0.04em;
}

.modal__person-name {
  color: var(--color-on-surface);
  font-size: var(--type-body-s-size);          /* 14px */
  font-weight: var(--type-weight-semi-bold);
  line-height: 1.35;
}

.modal__person-role {
  color: var(--color-white-80);
  font-size: 13px;
  line-height: 1.35;
}

/* Episodes ---------------------------------------------------------------- */

.modal__season {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2xl);
  margin-bottom: 32px;
}

.modal__season-text {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  width: 603px;
  max-width: 100%;
}

.modal__season-title {
  color: var(--color-on-surface);
  font-size: var(--type-body-xl-size);               /* 20px */
  font-weight: 700;
  line-height: 30px;
  letter-spacing: -0.0225em;
}

/* Departure 4: 14px, not the design's 12px inline fallback. */
.modal__season-blurb {
  color: var(--color-on-surface);
  font-size: var(--type-body-s-size);
  line-height: var(--type-line-height);
}

.modal__dropdown {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  width: 261px;
  flex: 0 0 auto;
  padding: var(--space-s) var(--space-s) var(--space-s) var(--space-l);
  border: 0;
  border-radius: var(--radius-s);
  background: var(--color-surface);
  color: var(--color-on-surface);
  font-family: inherit;
  font-size: var(--type-body-m-size);
  font-weight: var(--type-weight-semi-bold);
  line-height: 24px;
  cursor: pointer;
  transition: background-color var(--dur-ring) ease;
}

.modal__dropdown:hover,
.modal__dropdown:focus-visible { background: #333; outline: none; }
.modal__dropdown img { display: block; width: 20px; height: 20px; max-width: none; }

.modal__episodes { display: flex; flex-direction: column; }

.modal__episode {
  display: flex;
  align-items: center;
  gap: var(--space-xl);                              /* 20px */
  padding: 24px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  background: none;
  border-left: 0; border-right: 0; border-bottom: 0;
  width: 100%;
  text-align: left;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.modal__episodes .modal__episode:first-child { border-top: 0; padding-top: 0; }

.modal__episode-art {
  position: relative;
  flex: 0 0 auto;
  width: 313px;
  height: 176.063px;
  border-radius: var(--radius-s);
  overflow: hidden;
}

.modal__episode-art::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px solid var(--card-outline);
  border-radius: var(--radius-s);
  transition: border-color var(--dur-outline) ease;
}

.modal__episode:hover .modal__episode-art::after,
.modal__episode:focus-visible .modal__episode-art::after {
  border-color: var(--card-outline-hover);
}

.modal__episode:focus-visible { outline: none; }

.modal__episode-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  max-width: none;
  display: block;
  transition: transform var(--dur-zoom) var(--ease-zoom);
}

.modal__episode:hover .modal__episode-art img { transform: scale(var(--card-zoom)); }

.modal__episode-play {
  position: absolute;
  left: 10px;
  bottom: 9.86px;
  width: 48px;
  height: 48px;
  z-index: 2;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.7));
}

.modal__episode-play img { width: 48px; height: 48px; max-width: none; display: block; }

.modal__episode-text {
  flex: 1 0 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}

.modal__episode-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-l);
}

.modal__episode-title {
  color: var(--color-on-surface);
  font-size: var(--type-body-m-size);                /* 16px */
  font-weight: 700;
  line-height: 20px;
}

.modal__episode-runtime {
  flex: 0 0 auto;
  color: var(--color-white-80);
  font-size: var(--type-body-s-size);                /* 14px */
  line-height: 21px;
  letter-spacing: -0.0107em;
}

.modal__tags { display: flex; align-items: center; gap: var(--space-s); }

/* Departure 3: 12px, not 10px. */
.modal__tag {
  padding: var(--space-xs) var(--space-s);           /* 4px 8px */
  border: 1px solid var(--color-outline);
  border-radius: var(--radius-xs);
  background: var(--color-background);
  color: var(--color-on-surface);
  font-size: 12px;
  font-weight: var(--type-weight-semi-bold);
  line-height: var(--type-line-height);
  white-space: nowrap;
}

/* Departure 2: wraps in its column instead of running past the frame. */
.modal__episode-desc {
  color: var(--color-white-80);
  font-size: var(--type-body-m-size);                /* 16px */
  line-height: 21px;
  letter-spacing: -0.0107em;
}

/* Trailers & Extras — Figma 9633:291750 lays this out exactly like the
   episode list, so it reuses that row wholesale and only differs in the
   thumbnail size and the line under the title. */

.modal__episode--extra .modal__episode-art { width: 268px; height: 151px; }

.modal__extra-line {
  display: flex;
  align-items: center;
  gap: var(--space-m);
}

.modal__extra-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  max-width: none;
  display: block;
  opacity: 0;
  transition: opacity var(--dur-video-fade) ease;
}

.modal__episode-art.is-playing .modal__extra-video { opacity: 1; }
.modal__episode-art.is-playing .modal__episode-play { opacity: 0; }
.modal__episode-play { transition: opacity 0.25s ease; }

/* More Like This ----------------------------------------------------------- */

.modal__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  column-gap: 25px;
  row-gap: var(--space-xl);                          /* 20px */
}

.modal__grid-card {
  position: relative;
  aspect-ratio: 202.168 / 303.252;
  border-radius: var(--radius-s);
  overflow: hidden;
  cursor: pointer;
}

.modal__grid-card::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border: 1px solid var(--card-outline);
  border-radius: var(--radius-s);
  transition: border-color var(--dur-outline) ease;
}

.modal__grid-card:hover::after,
.modal__grid-card:focus-visible::after { border-color: var(--card-outline-hover); }
.modal__grid-card:focus-visible { outline: none; }

.modal__grid-card img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  max-width: none;
  display: block;
  transition: transform var(--dur-zoom) var(--ease-zoom);
}

.modal__grid-card:hover img { transform: scale(var(--card-zoom)); }

/* A note in place of an empty tab, so a thin section reads as a fact about
   the catalogue rather than as a broken panel. */
.modal__empty {
  color: var(--color-on-surface-variant);
  font-size: var(--type-body-m-size);
  line-height: var(--type-line-height);
}

/* ---- Narrower viewports -------------------------------------------------- */

@media (max-width: 1100px) {
  .modal { padding: 32px 16px; }
  .modal__hero-inner { left: 32px; bottom: 20px; max-width: calc(100% - 64px); }
  .modal__meta,
  .modal__body { padding-left: 32px; padding-right: 32px; }
  .modal__tabs { padding: 0 32px; }
  .modal__grid { grid-template-columns: repeat(3, 1fr); }
  .modal__people { grid-template-columns: repeat(4, 1fr); }
  .modal__episode-art { width: 240px; height: 135px; }
  .modal__season { flex-direction: column; gap: var(--space-l); }
}

@media (prefers-reduced-motion: reduce) {
  .modal,
  .modal__dialog { transition: opacity 0.2s ease; }
  .modal__dialog { transform: none; }
  .modal.is-open .modal__dialog { transform: none; }
  .modal__panel,
  .modal__panel--entering,
  .modal__panel--leaving { animation: none; }
  .modal__panel--leaving { display: none; }
  .modal__body { transition: none; }
  .modal__logo { transition: none; }
  .modal__hero.is-cinema .modal__logo { transform: none; }
  .modal__tab-rule { transition: none; }
  .modal__episode-art img,
  .modal__grid-card img { transition: none; }
  .modal__episode:hover .modal__episode-art img,
  .modal__grid-card:hover img { transform: none; }
}

/* Logo lockup — mirrors .hero__logo--lockup (css/hero.css).
   A title whose subtitle is part of its logo has only the wordmark as an
   exported asset. The hero typesets the rest beneath it; the modal has to
   do the same, or opening a card shows a different logo from the slide it
   was opened from. Proportions are the hero's, scaled to this slot. */
.modal__logo--lockup {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-end;
  gap: 2px;
}

.modal__logo--lockup img {
  width: auto;
  height: 62px;
  object-position: left bottom;
}

.modal__logo-sub {
  padding-left: 3px;
  color: var(--color-on-surface);
  font-size: 15px;
  font-weight: var(--type-weight-regular);
  letter-spacing: 0.42em;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0.92;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.7);
}

@media (max-width: 599px) {
  .modal__logo--lockup img { height: 44px; }
  .modal__logo-sub { font-size: 11px; letter-spacing: 0.3em; }
}
