/* ==========================================================================
   app.css — one component set for six screens
   --------------------------------------------------------------------------
   TEN-FOOT INTERFACE. There is not one :hover rule in this file and there
   must not be one. A television has no pointer, so a hover state is a state
   nobody can reach — and worse, a state that looks like the focus state and
   so teaches the reviewer the wrong thing about how the interface works.

   EVERY LENGTH IS A REAL FIGMA PIXEL at 1920 x 1080. The frame is scaled
   once, by app.js, so no rule here has to know the window size and any
   number here can be checked against the design file directly.
   ========================================================================== */

/* ==========================================================================
   1. stage and frame
   ========================================================================== */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: #000;
  overflow: hidden;
}

body {
  font-family: var(--font-family-normal-font-family);
  -webkit-font-smoothing: antialiased;
  color: var(--paywall-page-colours-paywall-page-text-colour);
}

.stage {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
}

/* The wrapper carries the SCALED size so the stage can centre it. A
   transform alone leaves the layout box at full 1080 and the frame sits
   off-centre with the page scrolled. */
.fit {
  width:  calc(1920px * var(--s, 1));
  height: calc(1080px * var(--s, 1));
}

.tv {
  position: relative;
  width: 1920px;
  height: 1080px;
  overflow: hidden;
  transform: scale(var(--s, 1));
  transform-origin: top left;
  background: var(--full-screen-content-screen-frame-background-colour);
}

/* The one vertical scroll container. Keeping the scroll inside the frame
   rather than on the document is what lets the focus engine measure
   offsets against something whose coordinate space it controls. */
.page {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
}
.page::-webkit-scrollbar { display: none; }
.page__end { height: 160px; }

/* The keyboard hint sits OUTSIDE the frame, on the stage. It is a review
   aid, not part of the design, and putting it inside the 16:9 would be
   putting it into the design. */
.hint {
  position: fixed;
  left: 0; right: 0; bottom: 14px;
  text-align: center;
  font: 500 12px/1 var(--font-family-normal-font-family);
  letter-spacing: 0.08em;
  color: #ffffff3d;
  pointer-events: none;
}
.hint b { color: #ffffff70; font-weight: 600; }

/* The hint retires. At exactly 16:9 there is no letterbox for it to sit in,
   so it lies over the foot of the design — which on the programme page is the
   tab row. It only has to be read once per screen.

   `forwards` is correct here, unlike in the entrance schedule: the end state
   IS the intent, and no class ever needs to override it. */
@keyframes hint-retire { to { opacity: 0; } }
.hint { animation: hint-retire 900ms var(--ease-screen) 6s forwards; }

/* ---- the mode switch ----------------------------------------------------
   Review furniture, on the stage and outside the 16:9, like the hint. It does
   NOT retire: which build you are looking at is the one thing a reviewer
   needs on screen at all times, or they will report the faithful build's flat
   focus ring as a bug in the concept one. */
.modeswitch {
  position: fixed;
  top: 12px; right: 14px;
  z-index: 100;
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: 7px;
  background: #ffffff0f;
  font: 500 11px/1 var(--font-family-normal-font-family);
}
.modeswitch__btn {
  padding: 5px 11px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: #ffffff59;
  font: inherit;
  letter-spacing: 0.06em;
  cursor: pointer;
}
.modeswitch__btn.is-on { background: #ffffff1f; color: #ffffffd9; }

/* ==========================================================================
   2. FOCUS — and THE PARTITION
   --------------------------------------------------------------------------
   Everything below this line exists twice.

   FAITHFUL is what the frames draw, and only that. Card focus is a flat 4px
   white border. A button's focus is the fill swapping to cream with dark ink.
   A nav item and a tab get their pill. A field gets its outline. Nothing
   moves, nothing lifts, nothing grows.

   CONCEPT is that, plus a layer, and every rule in it is additive and scoped
   to `[data-mode="concept"]`. Three things are added, all chosen because they
   cost nothing to read from three metres and none of them is colour: SIZE
   (the card grows, and overshoots before it settles), DEPTH (a shadow lifts
   it off the ground), and SEPARATION (a dark halo outside the ring, so the
   ring survives being drawn over pale artwork, which a plain white border
   does not).

   Why the partition is scoped this way rather than kept in two files: a
   second copy of the component set would drift the first time either side
   was edited, and then "faithful" would quietly stop being faithful. One set
   of rules, one additive override, and the faithful build is what you get by
   deleting the overrides.

   WHAT IS NOT PARTITIONED: focus mechanics. Spatial navigation, revealing to
   the safe area, scrolling, the flat history. Those are how the prototype
   works at all, they are identical in both builds, and the frames do not
   specify them because a frame cannot.

   Two states are in BOTH because the design draws them: the on-screen
   keyboard's focused key, which the frame plainly scales up into a white
   tile, and the player's timeline, which the frame draws taller in its
   Timeline Focus state.
   ========================================================================== */

[data-focus] { outline: none; }

/* FAITHFUL FOCUS SNAPS. The app moves focus from one element to the next with
   no tween, and the frames are states rather than a timeline — so easing
   between them is something this build invented. Concept keeps the tween. */
[data-mode="concept"] [data-focus],
[data-mode="concept"] .card__art,
[data-mode="concept"] .btn {
  transition: transform var(--dur-focus) var(--ease-focus),
              box-shadow var(--dur-focus) var(--ease-focus),
              background-color var(--dur-focus) var(--ease-focus),
              color var(--dur-focus) var(--ease-focus);
}

/* Pressing into a wall does something. Silence reads as a dropped press on
   a remote and the viewer presses again, harder. */
@keyframes edge-x {
  0%, 100% { translate: 0; }
  40%      { translate: var(--edge, 10px); }
}
@keyframes edge-y {
  0%, 100% { translate: 0; }
  40%      { translate: 0 var(--edge, 10px); }
}
[data-mode="concept"] .is-edge-left  { --edge: -14px; animation: edge-x 220ms var(--ease-focus); }
[data-mode="concept"] .is-edge-right { --edge:  14px; animation: edge-x 220ms var(--ease-focus); }
[data-mode="concept"] .is-edge-up    { --edge: -10px; animation: edge-y 220ms var(--ease-focus); }
[data-mode="concept"] .is-edge-down  { --edge:  10px; animation: edge-y 220ms var(--ease-focus); }

/* Select. A short, small squash — the acknowledgement that the press
   landed, before the screen changes under it. */
[data-mode="concept"] .is-press { transform: scale(0.97) !important; }

/* ==========================================================================
   3. navigation — 1920 x 128
   ========================================================================== */

/* THE NAVIGATION BAR SCROLLS AWAY. It is not fixed, and that is the single
   biggest thing the build had wrong about movement.

   Every scrolled frame in the file shows it gone: 15951:250646 opens on a
   half rail with no bar above it, 16256:174628 puts the promotional band at
   y=82 with nothing over it, and 241:53503 shows the tab row at the very top
   of the screen. The bar is the first row of the page, not a chrome layer
   over it — you leave it behind by moving down, and you get it back by
   moving up, which is also what makes "up" mean something on a remote.

   It therefore lives inside the scroll container, in flow. The focus engine
   needs no special case: a nav item sits at offsetTop 0, so revealing it
   scrolls the page home. */
.nav {
  position: relative;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width:  var(--navigation-size-padding-and-gap-size-navigation-bar-navigation-bar-width-size);
  height: var(--navigation-size-padding-and-gap-size-navigation-bar-navigation-bar-height-size);
  padding:
    var(--navigation-size-padding-and-gap-padding-navigation-bar-navigation-bar-top-and-bottom-padding)
    var(--navigation-size-padding-and-gap-padding-navigation-bar-navigation-bar-left-and-right-padding);
  background: var(--navigation-colours-navigation-bar-navigation-frame-background-colour);
}

.nav__items {
  display: flex;
  align-items: center;
  gap: var(--navigation-size-padding-and-gap-gap-navigation-bar-items-gap);
}

.nav__icon,
.nav__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--navigation-size-padding-and-gap-size-navigation-item-text-navigation-item-height-size);
  border: 0;
  border-radius: var(--navigation-corner-radius-navigation-item-corner-radius);
  background: transparent;
  color: var(--navigation-colours-navigation-item-default-state-default-navigation-item-text-colour);
  font: var(--navigation-text-regular);
  cursor: default;
  /* Home is an <a> and the other three are <button>s, because only Home has
     a screen to go to. Without this the anchor carried the browser's default
     underline and the buttons did not, so the current item was the one item
     drawn differently — by accident, in the exact place 8740:153307 draws no
     underline at all. The pill is the whole of the state. */
  text-decoration: none;
}

.nav__icon {
  width: var(--navigation-size-padding-and-gap-size-navigation-item-icon-navigation-icon-width-size);
  padding: 0;
}
.nav__icon svg { width: 40px; height: 40px; display: block; }

.nav__item {
  padding: 0 var(--navigation-size-padding-and-gap-padding-navigation-item-navigation-item-left-and-right-padding);
}

/* ONE PILL AT A TIME. The bar has a single highlighted state and uses it both
   for "you are here" and for focus — so while focus is inside the bar, the
   current page's pill has to stand down, or Home and New are both highlighted
   and neither means anything. 8740:153307 draws exactly one pill.

   `:not(:has(.is-focus))` on the bar is the whole rule: the active pill shows
   while focus is elsewhere on the screen, which is what that frame draws, and
   yields to the focused item the moment focus enters the bar. */
.nav:not(:has(.is-focus)) .nav__item.is-active {
  background: var(--navigation-colours-navigation-item-active-state-active-navigation-item-background);
  color: var(--navigation-colours-navigation-item-active-state-active-navigation-item-text);
}

/* THE NAVIGATION BAR DOES NOT USE THE BUTTON'S FOCUS FILL, and the build had
   assumed it did on the reasoning that one treatment should mean "you are
   here" everywhere.

   16219:118928 — "A-Z Page - Nav item in focus" — says otherwise. Sampled off
   that frame the focused item's ground is rgb(50,62,64), which is exactly
   #f8f4eb33 composited over #001015, and its text stays cream. That is the
   collection's own Active-Navigation-Item-Background and
   Active-Navigation-Item-Text — the bar has ONE highlighted state and uses it
   for focus, not the cream fill with dark ink that buttons and tabs use.

   The collection names no separate focus state for navigation, which is
   consistent: there is nothing to name. */
.nav__icon.is-focus,
.nav__item.is-focus {
  background: var(--navigation-colours-navigation-item-active-state-active-navigation-item-background);
  color: var(--navigation-colours-navigation-item-active-state-active-navigation-item-text);
}
[data-mode="concept"] .nav__icon.is-focus,
[data-mode="concept"] .nav__item.is-focus {
  background: var(--buttons-colours-primary-focus-primary-focus-button-background-colour);
  color: var(--buttons-colours-primary-focus-primary-focus-button-text-and-icon-colour);
  transform: scale(1.04);
}

.nav__mark { width: 190px; height: auto; display: block; }

/* The scrolled-state ground the bar used to grow is gone with it: a bar that
   scrolls away never has a list underneath it to go muddy against. */

/* ==========================================================================
   4. buttons — 379 x 86, radius 8
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--buttons-size-padding-and-gap-gap-normal-button-button-icon-and-text-gap);
  min-width: var(--buttons-size-padding-and-gap-size-large-button-width-size);
  height:    var(--buttons-size-padding-and-gap-size-large-button-height-size);
  padding:
    var(--buttons-size-padding-and-gap-padding-large-button-top-and-bottom-padding)
    var(--buttons-size-padding-and-gap-padding-large-button-sides-padding);
  border: 0;
  border-radius: var(--buttons-corner-radius-button-corner-radius);
  background: var(--buttons-colours-primary-default-primary-default-button-background-colour);
  color:      var(--buttons-colours-primary-default-primary-default-button-text-and-icon-colour);
  font: var(--button-text-bold);
  text-decoration: none;
  cursor: default;
}

.btn__icon { width: 34px; height: 34px; display: block; flex: none; }

/* The fill swap IS the design's focus state for a button — faithful. The
   lift and the 3% are the concept layer. */
.btn.is-focus {
  background: var(--buttons-colours-primary-focus-primary-focus-button-background-colour);
  color:      var(--buttons-colours-primary-focus-primary-focus-button-text-and-icon-colour);
}
[data-mode="concept"] .btn.is-focus {
  transform: scale(1.03);
  box-shadow: var(--focus-lift);
}

/* Disabled carries the collection's own colours at reduced opacity. The
   collection gives disabled the SAME two values as default, which cannot be
   right — the frame plainly draws it dimmer — so the opacity is measured off
   163:36777 rather than invented, and recorded in source.md §4. */
.btn.is-disabled {
  background: var(--buttons-colours-primary-disabled-primary-disabled-button-background-colour);
  color:      var(--buttons-colours-primary-disabled-primary-disabled-button-text-and-icon-colour);
  opacity: 0.42;
}

/* ==========================================================================
   5. rails and cards — T1, 322 x 187
   ========================================================================== */

/* 72 from the cards above to this rail's title, measured off 16256:174628
   (Continue Watching cards end at 645, "Top Picks for You" box top ~718). */
.rail { margin-top: 72px; }

.rail__title {
  margin: 0 0 var(--rails-size-padding-and-gap-gap-rail-cards-title-and-cards-gap);
  padding-left: var(--safe-area-x);
  color: var(--rails-colours-text-rail-cards-title-text-colour);
  font: var(--rail-title);
  letter-spacing: var(--rail-title-letter-spacing);
}

.rail__track {
  position: relative;      /* the focus engine measures offsetLeft against this */
  display: flex;
  gap: var(--rail-card-gap);
  padding: 24px var(--safe-area-x);
  margin: -24px 0;         /* so a focused card's lift is not clipped */
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
}
.rail__track::-webkit-scrollbar { display: none; }

.card {
  flex: none;
  display: block;
  width: var(--cards-size-padding-and-gap-t1-size-t1-cards-image-width-size);
  text-decoration: none;
  color: inherit;
}

.card__art {
  position: relative;
  display: block;
  width:  var(--cards-size-padding-and-gap-t1-size-t1-cards-image-width-size);
  height: var(--cards-size-padding-and-gap-t1-size-t1-cards-image-height-size);
  border-radius: var(--cards-corner-radius-cards-image-corner-radius-size);
  overflow: hidden;
  background: #04222c;
  transform-origin: center bottom;
}

.card__img { width: 100%; height: 100%; object-fit: cover; display: block; }

.card__premier {
  position: absolute;
  top: 0; right: 0;
  display: grid;
  place-items: center;
  width:  var(--tags-size-padding-and-gap-size-premier-premier-tag-width-size);
  height: var(--tags-size-padding-and-gap-size-premier-premier-tag-height-size);
  padding:
    var(--tags-size-padding-and-gap-padding-premier-tag-premier-tag-top-and-bottom-padding)
    var(--tags-size-padding-and-gap-padding-premier-tag-premier-tag-sides-padding);
  border-radius: var(--tags-corner-radius-tags-premier-tag-corner-radius);
  background: var(--tags-colours-premier-tag-premier-tag-background-colour);
  color: #e9c977;
}
.card__premier svg { width: 22px; height: 22px; display: block; }

/* Inset from all three edges, per 16256:174628 — not a full-width bar welded
   to the bottom of the artwork. */
.card__progress {
  position: absolute;
  left:   var(--card-progress-inset);
  right:  var(--card-progress-inset);
  bottom: var(--card-progress-inset);
  display: block;
  height: var(--card-progress-height);
  background: #f8f4eb40;
}
.card__progress-fill {
  display: block;
  height: 100%;
  background: var(--britbox-primary-white);
}

/* Portrait rail — "Top Picks for You". Same component, a different shape on
   the datum, which is the whole contract: screens differ by data. */
.card--portrait,
.card--portrait .card__art {
  width: var(--card-portrait-width);
}
.card--portrait .card__art {
  height: var(--card-portrait-height);
}

/* THE FOCUS TREATMENT. Ring, lift, scale — see §2. */
/* THE POP. Focus overshoots and settles rather than easing straight to its
   resting size. A card that simply grows by 6% is a size difference; a card
   that springs past it and comes back is an EVENT, and an event is what
   catches an eye that is three metres away and not looking directly at the
   thing it just moved to.

   The animation's last keyframe is the resting transform, and it runs
   WITHOUT a fill mode, so when it finishes the declared transform below
   takes over seamlessly and nothing is left asserting a stale value. */
@keyframes card-pop {
  0%   { transform: scale(1); }
  55%  { transform: scale(1.115); }
  100% { transform: scale(var(--focus-scale-card)); }
}

/* FAITHFUL: the flat ring the frames draw, and nothing else. */
.card.is-focus .card__art {
  box-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring-colour);
}

/* CONCEPT: the ring keeps its width and colour, and gains size, depth and a
   dark halo that survives being drawn over pale artwork. */
[data-mode="concept"] .card.is-focus .card__art {
  transform: scale(var(--focus-scale-card));
  animation: card-pop 340ms var(--ease-focus);
  box-shadow:
    0 0 0 var(--focus-ring-width) var(--focus-ring-colour),
    0 0 0 calc(var(--focus-ring-width) + 3px) #00101599,
    var(--focus-lift);
}

/* The same event at the shapes the other tiles use. */
@keyframes tile-pop {
  0%   { transform: scale(1); }
  55%  { transform: scale(1.09); }
  100% { transform: scale(var(--focus-scale-tile)); }
}
[data-mode="concept"] .ep.is-focus .ep__art { animation: tile-pop 340ms var(--ease-focus); }

/* ==========================================================================
   6. hero — H2 peaking, 1690 x 563
   ========================================================================== */

/* The hero begins 10px below the navigation bar — 8740:153307 puts the nav's
   foot at 128 and the hero image at 138. */
.hero { margin-top: 10px; }

/* A RAIL, not a bespoke carousel. It carries the same data-scroll-x contract
   as every other track on the screen, so the focus engine reveals a hero card
   to the safe-area inset exactly as it reveals a 322px card — same travel,
   same easing, same peek. The hero used to be one focusable element that
   swapped its own contents on a private keydown handler, which meant the top
   of the screen moved by different rules from everything under it. */
.hero__track {
  position: relative;
  display: flex;
  gap: var(--hero-peek-gap);
  padding: 0 var(--safe-area-x);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
}
.hero__track::-webkit-scrollbar { display: none; }

.hero__card {
  position: relative;
  flex: none;
  width:  var(--cards-size-padding-and-gap-hero-size-image-h2-peaking-h2-hero-image-width-size);
  height: var(--cards-size-padding-and-gap-hero-size-image-h2-peaking-h2-hero-image-height-size);
  text-decoration: none;
  color: inherit;
}

.hero__art {
  position: absolute;
  inset: 0;
  display: block;
  overflow: hidden;
  background: #04222c;
}

.hero__img { width: 100%; height: 100%; object-fit: cover; display: block; }

.hero__art::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, #001015e6 0%, #00101599 38%, transparent 68%);
}

/* 114 from the slide's own left edge, measured off 8740:153307 — the lockup
   sits at x=225 on a slide that begins at 116. */
.hero__body {
  position: absolute;
  left: 112px;
  bottom: 56px;
  z-index: 2;
  width: var(--cards-size-padding-and-gap-hero-size-text-frame-h1-hero-text-frame-width-size);
}

/* The collection's own H1 hero logo box, 721 x 240. It was being clamped to
   460 x 168, which rendered the title treatment at roughly three quarters of
   the size the frame draws it. */
.hero__logo {
  display: block;
  max-width: var(--cards-size-padding-and-gap-hero-size-content-title-logo-h1-hero-logo-width-size);
  max-height: var(--cards-size-padding-and-gap-hero-size-content-title-logo-h1-hero-logo-height-size);
  width: auto; height: auto;
  object-fit: contain;
  object-position: left bottom;
}

.hero__name { margin: 0; font: var(--headings-h1); }

/* Centred under the title treatment, the same lockup rule the programme page
   follows — on 8740:153307 the mark spans 385-665 under a title spanning
   225-828, which is a shared centre, not a shared left edge. */
.hero__original {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: var(--cards-size-padding-and-gap-hero-size-content-title-logo-h1-hero-logo-width-size);
  max-width: 100%;
  margin-top: var(--cards-size-padding-and-gap-hero-gap-h1-hero-text-frame-contenttag-and-tagline-gap);
}
.hero__originalmark { width: 118px; height: auto; display: block; }
.hero__originalword {
  font: 500 22px/1 var(--font-family-normal-font-family);
  letter-spacing: 0.14em;
  color: #f8f4ebcc;
  padding-left: 12px;
  border-left: 1px solid #f8f4eb59;
}

.hero__tagline {
  margin: var(--cards-size-padding-and-gap-hero-gap-h1-hero-text-frame-contenttag-and-tagline-gap) 0 0;
  color: var(--cards-colours-h1-hero-text-h1-hero-tagline-colour);
  font: var(--hero-h1-tagline);
}

/* AN INSET BOX-SHADOW PAINTS UNDER THE ELEMENT'S OWN CHILDREN, which is
   fine on an empty box and useless on this one. `.hero__art` contains a
   static <img> filling it edge to edge, so the ring was painted and then
   covered by the artwork in the same frame — the hero had no focus state at
   all, on the largest and most important target on the screen.

   `outline` is painted over the content, and `outline-offset: -4px` puts it
   in exactly the band the inset shadow was aiming at, so the geometry the
   file draws is unchanged: 4px, on the inside of the edge. Measured off
   8740:153307, where the hero's ring and 15951:250646's card ring are the
   same width to within a tenth of a pixel.

   The same bug, for the same reason, was hiding the promotional band's ring
   below. The card and episode rings were never affected: those are outer
   shadows, which paint outside the box and over nothing. */
.hero__card.is-focus .hero__art {
  outline: var(--focus-ring-width) solid var(--cards-colours-h1-hero-outline-h1-hero-focus-outline-colour);
  outline-offset: calc(var(--focus-ring-width) * -1);
}
[data-mode="concept"] .hero__card.is-focus .hero__art {
  box-shadow: var(--focus-lift);
}

/* ==========================================================================
   7. promotional band — PR1, 1690 x 233
   ========================================================================== */

.promo {
  position: relative;
  display: block;
  width:  var(--cards-size-padding-and-gap-pr1-promotional-banner-pr1-promotional-banner-width-size);
  height: var(--cards-size-padding-and-gap-pr1-promotional-banner-pr1-promotional-banner-height-size);
  /* 80 below the hero, measured off 8740:153307 (hero ends 695, band at 775).
     It used to be 56 plus the height of a dots row that should not have been
     there — removing the dots without re-measuring would have closed the gap
     the frame actually draws. */
  margin: 80px auto 0;
  overflow: hidden;
  background: #04222c;
  text-decoration: none;
  color: inherit;
  /* It is a <button> now, not an <a>, because the band promotes a collection
     and there is no collection screen — so the user agent's button padding
     and border have to go, or the artwork sits inset inside a chrome bevel. */
  border: 0;
  padding: 0;
  cursor: default;
}
.promo__img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The scrim and the two lines of type that stood here are gone with the
   overlay they carried — see ui.promo(). The band is a finished banner in
   both builds now, and a scrim over finished lettering only dims it. */

/* Outline, not an inset shadow — the banner image covered the ring. See the
   hero rule in §6 for the whole of it. */
.promo.is-focus {
  outline: var(--focus-ring-width) solid var(--focus-ring-colour);
  outline-offset: calc(var(--focus-ring-width) * -1);
}
[data-mode="concept"] .promo.is-focus {
  box-shadow: var(--focus-lift);
  transform: scale(1.012);
}

/* ==========================================================================
   8. splash — 1735:246174
   ========================================================================== */

/* Measured off 1735:246174 at 1920: wordmark 511 x 123 centred with its foot
   at y=601, spinner 70 x 70 with its head at y=680 — so 79 between them. */
body[data-screen="splash"] .tv {
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 79px;
  background: var(--full-screen-content-screen-frame-background-colour);
}

.splash__logo { width: 511px; height: auto; display: block; }

/* THE SPINNER IS #a6e2f5, NOT Chevron Light Blue. Sampled off the frame at
   1920: rgb(166, 226, 245). It had been given `BritBox Primary/Chevron Light
   Blue` (#00b6e6) on the assumption that the brightest brand blue was the
   brand blue — and #00b6e6 is a markedly greener, more saturated cyan that
   reads as a different brand beside the wordmark.

   #a6e2f5 is the pale cyan the rest of the system already uses: it is the
   programme page's release tag, and the value the companion build settled on.

   The shape is a solid rhombus, 70 x 70, confirmed by mapping the frame's
   pixels rather than by eye. It turns because a splash that does not move
   reads as a frozen app. */
.splash__spinner {
  width: 70px; height: 70px;
  background: var(--colour-splash-spinner);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  animation: splash-spin 1.4s linear infinite;
}
@keyframes splash-spin {
  from { transform: rotate(0deg)   scale(1);    opacity: 1; }
  50%  { transform: rotate(180deg) scale(0.68); opacity: 0.55; }
  to   { transform: rotate(360deg) scale(1);    opacity: 1; }
}

[data-mode="concept"] body.is-leaving .tv { opacity: 0; transition: opacity 320ms var(--ease-screen); }

/* ==========================================================================
   9. paywall — 1930:135511
   ========================================================================== */

/* One image at the frame's own size, so nothing is cropped at all — see
   screens.js, SCREENS.paywall. The grid of 280x285 boxes that stood here is
   gone with the posters it held. */
.wall {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.wall__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* One ramp, nothing stacked on it — see tokens.css, where the alpha was
   solved against the frame's own render rather than guessed. The flat scrim
   that used to be layered under this is gone; it was making the top of the
   wall about three times darker than the design draws it. */
.wall__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: var(--paywall-wall-ramp);
}

.paywall__mark {
  position: absolute;
  top: 48px; right: var(--safe-area-x);
  z-index: 3;
  width: 190px; height: auto;
  display: block;
}

/* 130 off the foot. The buttons' own bottom edge is y950 on 1930:135511,
   which puts the 354-tall content frame the collection names at y596 — the
   build had it 18px high. */
.paywall {
  position: absolute;
  left: 50%;
  bottom: 130px;
  z-index: 3;
  transform: translateX(-50%);
  width: var(--paywall-page-size-padding-and-gap-size-normal-paywall-page-content-frame-normal-paywall-page-content-width-size);
  text-align: center;
}

.paywall__title {
  margin: 0;
  font: var(--headings-h1);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}

.paywall__sub {
  margin: var(--paywall-page-size-padding-and-gap-gap-normal-paywall-page-normal-paywall-page-title-and-subtitle-gap) 0 0;
  font: var(--paywall-landing-subtitle);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}

.paywall__buttons {
  display: flex;
  justify-content: center;
  gap: var(--paywall-page-size-padding-and-gap-gap-buttons-paywall-page-buttons-gap);
  margin-top: var(--paywall-page-size-padding-and-gap-gap-normal-paywall-page-normal-paywall-page-title-and-button-gap);
}

/* ==========================================================================
   10. sign in — 163:36777 and the keyboard at 190:28898
   ========================================================================== */

body[data-screen="signin"] .tv {
  background: var(--full-screen-form-screens-frame-background-colour);
}

/* 60, measured off 163:36777 — the H1's glyphs start at y74 and a 64/68 box
   puts its top at 60. It had been using the nav bar's 32px padding token,
   which is a different measurement that happened to be nearby. */
.form__head {
  position: absolute;
  top: 60px;
  left: var(--safe-area-x);
  height: var(--navigation-size-padding-and-gap-size-navigation-bar-navigation-bar-title-and-subtitle-height-size);
}
.form__title { margin: 0; font: var(--headings-h1); }
.form__sub {
  margin: 12px 0 0;
  font: var(--navigation-subtitle-regular);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}
.form__mark {
  position: absolute;
  top: 48px; right: var(--safe-area-x);
  width: 190px; height: auto;
  display: block;
}

.form {
  position: absolute;
  top: 220px;
  left: var(--safe-area-x);
  width: var(--credentials-input-frame-size-padding-and-gap-size-sign-in-full-frame-sign-in-credentials-input-frame-width);
  display: flex;
  flex-direction: column;
  gap: var(--credentials-input-frame-size-padding-and-gap-gap-credentials-input-frame-gap);
}

.field {
  display: flex;
  align-items: center;
  width:  var(--input-field-size-padding-and-gap-size-email-input-field-email-width-size);
  height: var(--input-field-size-padding-and-gap-size-email-input-field-email-height-size);
  padding:
    var(--input-field-size-padding-and-gap-padding-input-field-top-and-bottom-size)
    var(--input-field-size-padding-and-gap-padding-input-field-sides-padding-size);
  border-radius: var(--input-field-corner-radius-input-field-corner-radius-size);
  background: var(--input-field-colours-background-input-field-background-colour);
  font: var(--input-field-text);
  color: var(--input-field-colours-text-input-field-text-entry-colour);
  cursor: default;
}
.field.is-empty { color: var(--input-field-colours-text-input-field-no-text-colour); }
.field__value { display: block; }

/* 4px, inset — the cream runs x115..118 inside a field box that starts at
   115 and is 1234 wide, so the outline eats into the field rather than
   growing it. */
.field.is-focus {
  box-shadow: inset 0 0 0 var(--focus-ring-width) var(--input-field-colours-outline-input-field-focussed-outline-colour);
}
[data-mode="concept"] .field.is-focus { box-shadow:
  inset 0 0 0 var(--focus-ring-width) var(--input-field-colours-outline-input-field-focussed-outline-colour),
  var(--focus-lift); }

.form__pwrow {
  display: flex;
  gap: var(--input-field-size-padding-and-gap-gap-input-field-password-field-and-showicon-gap);
}
.field--password {
  width: calc(
    var(--input-field-size-padding-and-gap-size-password-input-field-input-field-password-width-size)
    - var(--input-field-size-padding-and-gap-size-password-show-password-show-password-toggle-width-size)
    - var(--input-field-size-padding-and-gap-gap-input-field-password-field-and-showicon-gap)
  );
}

.field__eye {
  display: grid;
  place-items: center;
  flex: none;
  width:  var(--input-field-size-padding-and-gap-size-password-show-password-show-password-toggle-width-size);
  height: var(--input-field-size-padding-and-gap-size-password-show-password-show-password-toggle-height-size);
  border: 0;
  border-radius: var(--input-field-corner-radius-input-field-corner-radius-size);
  background: var(--input-field-colours-show-password-show-hide-fill-bg);
  color: var(--input-field-colours-text-input-field-text-entry-colour);
  cursor: default;
}
.field__eye svg { width: 36px; height: 36px; display: block; }
.field__eye.is-focus {
  background: var(--buttons-colours-primary-focus-primary-focus-button-background-colour);
  color: var(--buttons-colours-primary-focus-primary-focus-button-text-and-icon-colour);
}
[data-mode="concept"] .field__eye.is-focus { transform: scale(1.04); }

.form__actions {
  display: flex;
  gap: var(--paywall-page-size-padding-and-gap-gap-buttons-paywall-page-buttons-gap);
  margin-top: calc(
    var(--input-field-size-padding-and-gap-gap-form-and-buttons-input-field-form-buttons-gap)
    - var(--credentials-input-frame-size-padding-and-gap-gap-credentials-input-frame-gap)
  );
}
.form__actions .btn { min-width: 274px; }

/* ---- the keyboard ----
   Over a blurred, dimmed copy of the form. The blur is on the frame, not on
   a duplicate of the page, so there is exactly one DOM for the form and
   focus returns to the element it left. */

body.has-keyboard .form,
body.has-keyboard .form__head,
body.has-keyboard .form__mark {
  filter: blur(14px);
  opacity: 0.35;
}
[data-mode="concept"] body.has-keyboard .form,
[data-mode="concept"] body.has-keyboard .form__head,
[data-mode="concept"] body.has-keyboard .form__mark {
  transition: filter var(--dur-fade) var(--ease-screen), opacity var(--dur-fade) var(--ease-screen);
}

.kb {
  position: absolute;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: start center;
  background: #00060acc;
}
[data-mode="concept"] .kb { animation: kb-in var(--dur-fade) var(--ease-screen); }
@keyframes kb-in { from { opacity: 0; } to { opacity: 1; } }

.kb__panel {
  width: 1480px;
  padding-top: 150px;
  text-align: center;
}

.kb__title {
  margin: 0;
  font: 600 44px/1.1 var(--font-family-normal-font-family);
  color: #f8f4eb;
}

.kb__preview {
  display: flex;
  align-items: center;
  width: 740px;
  height: 64px;
  margin: 34px auto 0;
  padding: 0 24px;
  border-radius: var(--input-field-corner-radius-input-field-corner-radius-size);
  background: #ffffff14;
}
.kb__previewtext {
  font: 400 28px/1 var(--font-family-normal-font-family);
  color: #f8f4eb;
  letter-spacing: 0.02em;
}
.kb__previewtext.is-empty::after { content: "|"; opacity: 0.5; }

.kb__row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  margin-top: 34px;
}
.kb__row--digits { margin-top: 18px; }

.kb__key {
  display: grid;
  place-items: center;
  min-width: 46px;
  height: 52px;
  padding: 0 6px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #f8f4ebb2;
  font: 400 34px/1 var(--font-family-normal-font-family);
  cursor: default;
}
.kb__key--wide {
  min-width: 86px;
  background: #ffffff1f;
  font-size: 22px;
  border-radius: 6px;
}

/* A key in focus is a white tile with dark ink — the frame's own treatment,
   and the clearest possible mark on a row of 26 near-identical glyphs. */
.kb__key.is-focus {
  background: #fff;
  color: #000;
  transform: scale(1.22);
  box-shadow: 0 8px 22px #000000a6;
}

/* BOTH OF THESE MUST BE BLOCK-LEVEL. The panel is text-align:center and
   .btn is display:inline-flex, so an inline-flex .kb__modes and the Next
   button lay out on the SAME LINE — which looks like a spacing bug and is
   really a focus bug: down from the case switch lands nowhere, because
   there is nothing below it. */
.kb__modes {
  display: flex;
  width: max-content;
  gap: 4px;
  margin: 26px auto 0;
  padding: 6px;
  border-radius: 10px;
  background: #ffffff14;
}
.kb__mode {
  min-width: 78px;
  height: 44px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: #f8f4eb8c;
  font: 600 24px/1 var(--font-family-normal-font-family);
  cursor: default;
}
.kb__mode.is-on { color: #f8f4eb; }
.kb__mode.is-focus { background: #fff; color: #000; transform: scale(1.06); }

.kb__next { display: flex; margin: 42px auto 0; min-width: 232px; }

/* ==========================================================================
   10b. device linking — 143:24348
   --------------------------------------------------------------------------
   The screen a television actually signs you in with: a QR code, a short
   code, and your phone. The keyboard screen is the way out of it, not the
   way in — which is why the frame carries "Want to sign in using remote?" in
   a footer band rather than putting the keyboard first.
   ========================================================================== */

.link__head {
  position: absolute;
  top: 40px;
  left: var(--safe-area-x);
  right: var(--safe-area-x);
}
.link__title { margin: 0; font: var(--headings-h1); }
.link__sub {
  margin: 18px 0 0;
  font: var(--paywall-landing-subtitle);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}
.link__mark {
  position: absolute;
  top: 56px; right: var(--safe-area-x);
  width: 190px; height: auto;
  display: block;
}

.link__body {
  position: absolute;
  top: 330px;
  left: var(--safe-area-x);
  width: 900px;
}
.link__lede {
  margin: 0;
  font: var(--body-large-body-regular);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}
.link__url {
  display: block;
  margin: 8px 0 0;
  font: 600 40px/1.2 var(--font-family-normal-font-family);
  color: var(--device-link-link-colour);
}
.link__instruction {
  margin: 40px 0 0;
  font: var(--body-large-body-regular);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}

.link__code {
  display: flex;
  gap: var(--device-link-code-box-gap);
  margin-top: 28px;
}
.link__digit {
  display: grid;
  place-items: center;
  width:  var(--device-link-code-box-width);
  height: var(--device-link-code-box-height);
  background: var(--device-link-footer-ground);
  border: 1px solid #f8f4eb26;
  border-radius: 4px;
  font: 600 48px/1 var(--font-family-normal-font-family);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}

.link__refresh {
  margin: 44px 0 0;
  font: var(--body-large-body-regular);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}

/* The QR plate is a cream square with the code inside it — the one place in
   the whole build where a light ground is correct, because a QR code has to
   be scannable and a phone camera needs the quiet zone. */
.link__qr {
  position: absolute;
  top: 346px;
  left: 1090px;
  display: grid;
  place-items: center;
  width:  var(--device-link-qr-size);
  height: var(--device-link-qr-size);
  border-radius: 24px;
  background: var(--buttons-colours-primary-focus-primary-focus-button-background-colour);
}
.link__qr svg { display: block; width: 236px; height: 236px; }

.link__phone {
  position: absolute;
  top: 240px;
  left: 1505px;
  width: 270px;
  height: 570px;
  border-radius: 38px;
  border: 7px solid #2b3539;
  background: linear-gradient(180deg, #0b3246 0%, #06202e 100%);
  overflow: hidden;
}
.link__phonebar {
  height: 54px;
  display: grid;
  place-items: center;
  background: #0a1a24;
  font: 500 13px/1 var(--font-family-normal-font-family);
  color: #f8f4eb8c;
}
.link__phonebody { padding: 22px 18px; text-align: center; }
.link__phonescreen {
  height: 108px;
  display: grid;
  place-items: center;
  border-radius: 6px;
  background: #000;
}
.link__phonescreen img { width: 92px; height: auto; display: block; }
.link__phonetitle {
  margin: 18px 0 0;
  font: 600 19px/1.2 var(--font-family-normal-font-family);
  color: #fff;
}
.link__phonecopy {
  margin: 8px 0 0;
  font: 400 11px/1.35 var(--font-family-normal-font-family);
  color: #f8f4ebb2;
}
.link__phonecode { display: flex; justify-content: center; gap: 4px; margin-top: 14px; }
.link__phonecode span {
  display: grid;
  place-items: center;
  width: 26px; height: 24px;
  border-radius: 3px;
  background: #ffffff1f;
  font: 500 12px/1 var(--font-family-normal-font-family);
  color: #f8f4eb;
}
.link__phonecta {
  margin-top: 14px;
  padding: 8px;
  border-radius: 4px;
  background: var(--device-link-link-colour);
  font: 600 12px/1 var(--font-family-normal-font-family);
  color: #04222c;
}

/* The footer band carries the escape hatch. Its ground is a shade lighter
   than the page, which is what separates "the way in" from "the other way". */
.link__footer {
  position: absolute;
  left: 0; right: 0;
  top: var(--device-link-footer-top);
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 40px;
  background: var(--device-link-footer-ground);
}
.link__footer p {
  margin: 0;
  font: 600 32px/1 var(--font-family-normal-font-family);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}

/* ==========================================================================
   11. programme page — 241:53113
   ========================================================================== */

/* The hero art runs under the navigation bar — 241:53113 draws it full bleed
   from y=0 with the bar over it. The bar is in flow now, so the hero is
   pulled back up by the bar's own height to sit beneath it. Both still
   scroll away together, which is the behaviour the Episodes state shows. */
.show {
  position: relative;
  height: 1080px;
  margin-top: calc(-1 * var(--navigation-size-padding-and-gap-size-navigation-bar-navigation-bar-height-size));
}

.show__art { position: absolute; inset: 0; }
.show__img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: right top;
  display: block;
}
.show__ramp {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(94deg, #001015f2 0%, #001015d9 30%, #00101559 58%, transparent 78%),
    linear-gradient(to top, #001015 2%, transparent 34%);
}

/* 280, measured off 241:53113 — the "britbox | ORIGINAL" mark sits at y=285
   and the title treatment below it. It was at 236, which floated the whole
   lockup a clear 45px above where the frame puts it. */
.show__lockup {
  position: absolute;
  left: var(--safe-area-x);
  top: 280px;
  z-index: 2;
  width: var(--individual-programme-and-movie-page-size-padding-and-gap-size-hero-logo-hero-logo-width-size);
}

/* CENTRED over the title treatment, not ranged left with it. On 241:53113 the
   mark spans 310-510 and the BLUE LIGHTS lockup 140-690 — the two share a
   centre, which is what makes it read as one lockup rather than two
   left-aligned things stacked. */
.show__original {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 14px;
}
.show__originalmark { width: 108px; height: auto; display: block; }
.show__originalword {
  font: 500 20px/1 var(--font-family-normal-font-family);
  letter-spacing: 0.14em;
  color: #f8f4ebcc;
  padding-left: 12px;
  border-left: 1px solid #f8f4eb59;
}
.show__logo {
  display: block;
  width: 100%;
  max-height: var(--individual-programme-and-movie-page-size-padding-and-gap-size-hero-logo-hero-logo-height-size);
  object-fit: contain;
  object-position: left center;
}

/* 140 off the foot, not 196. The block's own height was right — 352 against
   the frame's 350 — but the whole of it sat 57px high, which walked the
   availability line, all three buttons, the description, the tags and the
   disclaimer up together. One number, six elements. */
.show__lower {
  position: absolute;
  left: var(--safe-area-x);
  bottom: 140px;
  z-index: 2;
  width: var(--individual-programme-and-movie-page-size-padding-and-gap-size-content-frame-individual-programme-movie-full-content-frame-width);
}

.show__availability {
  margin: 0 0 var(--individual-programme-and-movie-page-size-padding-and-gap-gap-content-frame-individual-programme-movie-content-frame-gap);
  font: var(--hero-individual-page-availability-text);
  letter-spacing: 0.06em;
  color: var(--individual-programme-and-movie-page-colours-tags-release-date-individual-programme-movie-release-tag-text-colour);
}

.show__cols {
  display: grid;
  grid-template-columns: var(--buttons-size-padding-and-gap-size-large-button-width-size) 1fr;
  /* 74 between the button column and the description, off 241:53113. */
  gap: 72px;
  align-items: start;
}

.show__ctas {
  display: flex;
  flex-direction: column;
  gap: var(--individual-programme-and-movie-page-size-padding-and-gap-gap-tabs-top-menu-individual-programme-movie-menu-bar-items-gap);
}

/* The CTA labels are CENTRED in the frame, not ranged left. On 241:53113 the
   "▶ Watch S1 E1" group shares its centre with the 379-wide button. Ranging
   them left was an assumption about how a play button ought to look. */

.show__description {
  margin: 0;
  font: var(--body-large-body-medium);
  color: var(--individual-programme-and-movie-page-colours-show-description-individual-programme-movie-show-description-text-colour);
}

.show__tags {
  display: flex;
  align-items: center;
  gap: 14px;
  height: var(--individual-programme-and-movie-page-size-padding-and-gap-size-tags-individual-programme-movie-hero-tag-height-size);
  margin-top: var(--individual-programme-and-movie-page-size-padding-and-gap-gap-content-frame-individual-programme-movie-content-frame-gap);
}
.show__tag {
  font: var(--tags-individual-page-tags);
  color: var(--individual-programme-and-movie-page-colours-tags-hero-tags-individual-programme-movie-hero-tag-text-colour);
}
.show__dot { color: #f8f4eb; font-size: 24px; }
.show__uhd {
  margin-left: 10px;
  padding: 4px 12px;
  border: 2px solid var(--individual-programme-and-movie-page-colours-tags-rating-individual-programme-movie-rating-tag-outline-colour);
  border-radius: 4px;
  font: var(--tags-indivudual-rating-tags);
  line-height: 32px;
  color: var(--individual-programme-and-movie-page-colours-tags-rating-individual-programme-movie-rating-tag-text-colour);
}

.show__disclaimer {
  margin: var(--individual-programme-and-movie-page-size-padding-and-gap-gap-content-frame-individual-programme-movie-tags-and-disclaimer-gap) 0 0;
  height: var(--individual-programme-and-movie-page-size-padding-and-gap-size-disclaimer-individual-programme-movie-disclaimer-height-size);
  font: 400 24px/32px var(--font-family-normal-font-family);
  color: var(--individual-programme-and-movie-page-colours-disclaimer-individual-programme-movie-disclaimer-text-colour);
}

/* ---- tabs ---- */

.tabs {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: var(--individual-programme-and-movie-page-size-padding-and-gap-gap-tabs-top-menu-individual-programme-movie-menu-bar-items-gap);
  height: var(--individual-programme-and-movie-page-size-padding-and-gap-size-tabs-top-bar-individual-programme-movie-menu-bar-height-size);
  padding: 0 var(--individual-programme-and-movie-page-size-padding-and-gap-padding-tabs-top-menu-individual-programme-movie-menu-bar-sides-padding);
  margin-top: -100px;
}

/* THE TAB ROW IS THE ONE FIXED THING ON THE DETAIL PAGE, and it sticks to
   the TOP OF THE SCREEN, not under the navigation bar.

   241:53503 is unambiguous: the Episodes state shows the tab row occupying
   the first 100px of the frame with the episode list directly beneath, and
   no navigation bar anywhere. So the hero scrolls away, the nav scrolls away
   with it, and the tabs stop at zero and stay — which is the correct answer
   to "what moves with the viewer": on this page, the tabs do, because they
   are what the panel belongs to.

   It used to stick at 128 under a permanently fixed bar, which pinned two
   rows to the top of a screen the design pins one row to. */
.page--programme .tabs {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--full-screen-content-screen-frame-background-colour);
  /* The ground is painted past the join. The whole frame sits inside a
     fractional transform: scale(), so an edge that is exact in frame pixels
     lands on a fraction of a device pixel and lets a thin band of the list
     slide through. */
  box-shadow: 0 -12px 0 0 var(--full-screen-content-screen-frame-background-colour);
}

/* Padding 32, not 28. Derived rather than eyeballed: the frame's tab text
   blocks sit 82 apart, and only a 32 padding leaves the 16 the collection's
   own Menu-Bar-Items-Gap names between the pills. */
.tab {
  height: 68px;
  padding: 0 32px;
  border: 0;
  border-radius: var(--individual-programme-and-movie-page-corner-radius-tabs-individual-programme-secondary-nav-tab-corner-radius);
  background: transparent;
  color: var(--individual-programme-and-movie-page-colours-tabs-top-menu-text-individual-programme-movie-menu-tab-default-text-colour);
  font: var(--tab-items);
  cursor: default;
}

/* THE SELECTED TAB KEEPS A GREY PILL, and an earlier pass was wrong to remove
   it. 251:22332 shows Episodes in a grey pill while Cast & Crew carries the
   cream focus pill — two states, visibly different, both on screen at once.

   The reasoning that removed it came from 241:53113, where no tab has a pill
   at all; but on that frame focus is still up in the hero and the tab row has
   not been entered yet. "Nothing selected until you go there" and "selection
   has no state" are different claims, and only the first is true. */
.tab.is-selected {
  background: var(--navigation-colours-navigation-item-active-state-active-navigation-item-background);
}
.tab.is-focus {
  background: var(--buttons-colours-primary-focus-primary-focus-button-background-colour);
  color: var(--buttons-colours-primary-focus-primary-focus-button-text-and-icon-colour);
}
[data-mode="concept"] .tab.is-focus { transform: scale(1.04); }

/* ---- panels ---- */

.panel { padding: 90px var(--safe-area-x) 0; min-height: 700px; }

.panel__lede {
  margin: 0 0 40px;
  max-width: 1690px;
  font: 600 32px/1.3 var(--font-family-normal-font-family);
  color: var(--individual-programme-and-movie-page-colours-show-description-individual-programme-movie-show-description-text-colour);
}

/* Seasons column 343 wide with 56 to the episode list — 246:35877. */
.panel__split { display: grid; grid-template-columns: 343px 1fr; gap: 56px; }

.seasons { display: flex; flex-direction: column; gap: 24px; }
.season {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 50px;
  padding: 0 20px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #f8f4eb;
  font: 500 28px/1 var(--font-family-normal-font-family);
  text-align: left;
  cursor: default;
}
.season.is-on { background: #f8f4eb1f; }
.season__tick { width: 28px; height: 28px; display: none; flex: none; }
.season.is-focus .season__tick { display: block; }
.season.is-focus {
  background: var(--buttons-colours-primary-focus-primary-focus-button-background-colour);
  color: var(--buttons-colours-primary-focus-primary-focus-button-text-and-icon-colour);
}
[data-mode="concept"] .season.is-focus { transform: scale(1.03); }

/* EPISODE STILLS ARE 435 x 250, not the 322 x 187 of a rail card — they are
   their own size on 246:35877 and were being built from the T1 token. */
.eps { display: flex; flex-direction: column; gap: 53px; }

.ep {
  display: grid;
  grid-template-columns: var(--episode-still-width) 1fr;
  gap: 18px;
  align-items: start;
  text-decoration: none;
  color: inherit;
}
.ep__art {
  position: relative;
  display: block;
  width:  var(--episode-still-width);
  height: var(--episode-still-height);
  overflow: hidden;
  background: #04222c;
}
.ep__img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The play mark is translucent until the episode is focused, when it goes
   solid white — 498:107410 against 246:35877. */
.ep__play {
  position: absolute;
  left: 13px; bottom: 13px;
  display: grid;
  place-items: center;
  width: 80px; height: 80px;
  border-radius: 50%;
  background: #f8f4eb73;
  color: #04222c;
}
.ep__play svg { width: 36px; height: 36px; display: block; }
.ep.is-focus .ep__play { background: #ffffff; }

.ep__title { margin: 0; font: 600 32px/1.2 var(--font-family-normal-font-family); }
.ep__meta { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.ep__mins { font: 500 22px/1 var(--font-family-normal-font-family); color: #f8f4ebb2; }
.ep__tag {
  padding: 4px 10px;
  border: 2px solid var(--individual-programme-and-movie-page-colours-tags-rating-individual-programme-movie-rating-tag-outline-colour);
  border-radius: 4px;
  font: 600 20px/1 var(--font-family-normal-font-family);
  color: var(--individual-programme-and-movie-page-colours-tags-rating-individual-programme-movie-rating-tag-text-colour);
}
.ep__synopsis {
  margin: 14px 0 0;
  max-width: 1080px;
  font: 400 24px/1.4 var(--font-family-normal-font-family);
  color: #f8f4ebd9;
}

/* THE RING GOES ROUND THE STILL, not round the row. 498:107410 draws a white
   border on the episode's picture and leaves its title and synopsis alone —
   the build was ringing the whole 1690-wide row, which at three metres reads
   as a box drawn around a paragraph. */
.ep.is-focus .ep__art {
  box-shadow: 0 0 0 var(--focus-ring-width) var(--focus-ring-colour);
}
[data-mode="concept"] .ep.is-focus .ep__art { transform: scale(var(--focus-scale-tile)); }

/* ---- credits — 246:3797 ----
   Two columns of 640 with 119 between them, each a heading over rows of
   name ranged left and character or role ranged right. 56px row pitch. */

.credits__cols { display: flex; gap: 119px; }
.credits { width: 640px; }
.credits__title {
  margin: 0 0 24px;
  font: 600 32px/1.2 var(--font-family-normal-font-family);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}
.credits__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  height: 56px;
  font: 600 28px/56px var(--font-family-normal-font-family);
  color: var(--paywall-page-colours-paywall-page-text-colour);
}
.credits__role { text-align: right; }

/* ---- more like this — 241:55281 ----
   A five-across wrapping grid of T1 cards, 20 between columns and 23 between
   rows. It was a horizontal rail. */

.more {
  display: grid;
  grid-template-columns: repeat(5, var(--cards-size-padding-and-gap-t1-size-t1-cards-image-width-size));
  column-gap: var(--rail-card-gap);
  row-gap: 23px;
}

/* ==========================================================================
   12. trailer player — 251:20927 then 4666:336327
   ========================================================================== */

body[data-screen="trailer"] .tv { background: #000; }

.player__video {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* ---- ratings and warnings ---- */

.rating {
  position: absolute;
  top: 72px;
  left: var(--safe-area-x);
  z-index: 10;
}
[data-mode="concept"] .rating { transition: opacity 520ms var(--ease-screen); }
.rating.is-gone { opacity: 0; }

.rating__bar { display: flex; align-items: center; gap: 18px; }
.rating__rule {
  width: 6px; height: 46px;
  background: var(--britbox-primary-chevron-light-blue);
  display: block;
}
.rating__code {
  margin: 0;
  font: 600 42px/1 var(--font-family-normal-font-family);
  letter-spacing: 0.02em;
}
.rating__warning {
  margin: 18px 0 0;
  font: 400 28px/1.3 var(--font-family-normal-font-family);
  color: #f8f4ebe6;
}

/* ---- controls ---- */

.player__chrome {
  position: absolute;
  inset: 0;
  z-index: 20;
  opacity: 0;
  pointer-events: none;
}
[data-mode="concept"] .player__chrome { transition: opacity var(--dur-fade) var(--ease-screen); }
.player__chrome.is-on { opacity: 1; }

.player__ramp {
  position: absolute;
  inset: 0;
  background: var(--player-chrome-ramp);
}

.player__info {
  position: absolute;
  left: var(--safe-area-x);
  bottom: 196px;
}
.player__kicker {
  display: block;
  font: 600 26px/1 var(--font-family-normal-font-family);
  color: #f8f4ebb2;
}
.player__title {
  margin: 10px 0 0;
  font: 700 52px/1.05 var(--font-family-normal-font-family);
  letter-spacing: -0.5px;
}

.player__side {
  position: absolute;
  right: var(--safe-area-x);
  bottom: 212px;
  display: flex;
  gap: 18px;
}
.player__iconbtn {
  display: grid;
  place-items: center;
  width: 72px; height: 72px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: #f8f4eb;
  cursor: default;
}

/* HEIGHT ONLY. The two player glyphs come straight out of the design file and
   are 40 x 40 and 32.2 x 40 — not the same width. Setting both axes to 40
   stretched the audio-description mark sideways into an oval, which is how a
   correct icon still manages to look wrong. Fixing the height and letting the
   width follow keeps each at the proportion it was drawn at. */
.player__iconbtn svg { height: 40px; width: auto; display: block; }
.player__iconbtn.is-focus {
  background: var(--buttons-colours-primary-focus-primary-focus-button-background-colour);
  color: var(--buttons-colours-primary-focus-primary-focus-button-text-and-icon-colour);
}
[data-mode="concept"] .player__iconbtn.is-focus { transform: scale(1.06); }

.scrub {
  position: absolute;
  left: var(--safe-area-x);
  right: var(--safe-area-x);
  bottom: 120px;
  padding: 14px 0;
  border-radius: 8px;
}
.scrub__track {
  position: relative;
  height: var(--player-scrubber-height);
  border-radius: 4px;
  background: var(--player-scrubber-track);
}
.scrub__fill {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 0;
  border-radius: 4px;
  background: var(--player-scrubber-fill);
}
.scrub__head {
  position: absolute;
  top: 50%;
  left: 0;
  width: 10px; height: 10px;
  margin-left: -5px;
  border-radius: 50%;
  background: var(--player-scrubber-fill);
  transform: translateY(-50%) scale(0);
}
[data-mode="concept"] .scrub__head { transition: transform var(--dur-focus) var(--ease-focus); }
.scrub__times {
  position: relative;
  height: 24px;
  margin-top: 14px;
  font: 600 24px/1 var(--font-family-normal-font-family);
  color: #f8f4ebd9;
}
/* `--at` is the playhead's position along the bar, set on every timeupdate —
   see screens.js. Clamped so the label cannot hang off either end. */
.scrub__now {
  position: absolute;
  left: clamp(0%, var(--at, 0%), 100%);
  transform: translateX(-50%);
  white-space: nowrap;
}
.scrub__left { position: absolute; right: 0; }

/* The timeline in focus grows and shows its head — the frame's
   "Timeline Focus" state, which is a taller bar and a visible scrubber.
   16px, measured off 4666:336327 row by row rather than estimated: the bar
   there occupies about eight and a half rows of a 1024-wide render, which is
   16 at 1920. It was 14. */
.scrub.is-focus .scrub__track { height: 16px; }
.scrub.is-focus .scrub__head { transform: translateY(-50%) scale(2.6); }
.scrub.is-focus .scrub__times { color: #fff; }

/* ==========================================================================
   13. the entrance
   --------------------------------------------------------------------------
   One schedule per screen, declared in CSS so nothing in JS has to know what
   a given screen contains. `.is-live` goes on the body one frame after the
   content is in the DOM.

   Nothing here hides content that has no animation to reveal it: every rule
   below animates FROM a state and lands at the element's natural one, so a
   failure to run leaves the screen correct rather than blank.

   FILL MODE IS `backwards`, NOT `both`, AND THAT IS NOT A DETAIL.
   `both` holds the final keyframe forever, and an animation's filled value
   beats an ordinary declaration in the cascade. So an element whose
   entrance ends at opacity 1 can never afterwards be hidden by a class —
   which is exactly what happened to the player's ratings card: `.is-gone`
   set opacity 0, the finished `rise` animation went on asserting opacity 1,
   and the card sat over the trailer for the whole of its running time.

   `backwards` applies the FROM state during the delay and then gets out of
   the way, which is all an entrance was ever for. Nothing below needs its
   end state held, because every one of them ends at the element's natural
   state anyway.
   ========================================================================== */

@keyframes rise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes settle { from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: none; } }
@keyframes resolve { from { opacity: 0; transform: scale(1.03); } to { opacity: 1; transform: none; } }

[data-mode="concept"] body.is-live .nav              { animation: settle  380ms var(--ease-screen) backwards; }
[data-mode="concept"] body.is-live .hero__card       { animation: resolve 760ms var(--ease-screen) backwards; }
[data-mode="concept"] body.is-live .promo            { animation: rise    420ms var(--ease-screen) 380ms backwards; }
[data-mode="concept"] body.is-live .rail             { animation: rise    460ms var(--ease-screen) 460ms backwards; }
[data-mode="concept"] body.is-live .rail:nth-of-type(2) { animation-delay: 540ms; }
[data-mode="concept"] body.is-live .rail:nth-of-type(3) { animation-delay: 600ms; }

[data-mode="concept"] body.is-live .wall             { animation: resolve 900ms var(--ease-screen) backwards; }
[data-mode="concept"] body.is-live .paywall__title   { animation: rise 460ms var(--ease-screen) 180ms backwards; }
[data-mode="concept"] body.is-live .paywall__sub     { animation: rise 460ms var(--ease-screen) 260ms backwards; }
[data-mode="concept"] body.is-live .paywall__buttons { animation: rise 460ms var(--ease-screen) 340ms backwards; }
[data-mode="concept"] body.is-live .paywall__mark    { animation: settle 420ms var(--ease-screen) 120ms backwards; }

[data-mode="concept"] body.is-live .link__head       { animation: settle 420ms var(--ease-screen) backwards; }
[data-mode="concept"] body.is-live .link__body       { animation: rise 460ms var(--ease-screen) 140ms backwards; }
[data-mode="concept"] body.is-live .link__qr         { animation: resolve 620ms var(--ease-screen) 200ms backwards; }
[data-mode="concept"] body.is-live .link__phone      { animation: rise 520ms var(--ease-screen) 280ms backwards; }
[data-mode="concept"] body.is-live .link__footer     { animation: rise 420ms var(--ease-screen) 380ms backwards; }
[data-mode="concept"] body.is-live .form__head       { animation: settle 420ms var(--ease-screen) backwards; }
[data-mode="concept"] body.is-live .form             { animation: rise   460ms var(--ease-screen) 140ms backwards; }

[data-mode="concept"] body.is-live .show__art        { animation: resolve 900ms var(--ease-screen) backwards; }
[data-mode="concept"] body.is-live .show__lockup     { animation: rise 480ms var(--ease-screen) 200ms backwards; }
[data-mode="concept"] body.is-live .show__lower      { animation: rise 480ms var(--ease-screen) 300ms backwards; }
[data-mode="concept"] body.is-live .tabs             { animation: rise 420ms var(--ease-screen) 420ms backwards; }

[data-mode="concept"] body.is-live .splash__logo     { animation: resolve 700ms var(--ease-screen) backwards; }
[data-mode="concept"] body.is-live .rating           { animation: rise 520ms var(--ease-screen) 240ms backwards; }

/* ==========================================================================
   14. reduced motion
   --------------------------------------------------------------------------
   Everything that moves stops. The focus TREATMENT is kept — the ring, the
   fill and the scale are the interaction model, not decoration, and taking
   them away would leave an interface with no visible focus at all. What
   goes is the transition between states, not the states.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .splash__spinner { animation: none; opacity: 0.8; }
}
