/*
    The story player, and everything it draws.

    Moved out of the App at CF-16 along with the components themselves, and the move is the whole
    reason it is a separate file. The studio has to render the *real* player for an editor to
    preview a story, and a component tree without its stylesheet is a page of unstyled boxes that
    passes every test and looks broken to a person — a failure nothing in CI can see.

    What lives here is what the player and its parts draw: scene artwork, the hero media element,
    the quiz option, the player chrome itself, the eight interaction types, and the scene media
    view. Nothing else from `app.css` came with it, because nothing else is rendered inside a
    story.

    Served at `_content/ScriptureJourney.UI/css/player.css`, so both applications link one copy and
    neither owns it.
*/
/* ------------------------------------------------------- scene artwork */

.sj-scene {
  display: block;
  width: 100%;
  margin-inline: auto;
}

.sj-scene__art {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--sj-radius-lg);
}

.sj-scene--sm {
  max-width: 12rem;
}

.sj-scene--md {
  max-width: 20rem;
}

.sj-scene--lg {
  max-width: 100%;
}

/* ----------------------------------------------------------------- hero */

.sj-hero {
  --sj-hero-surface: var(--sj-color-accent-soft);
  --sj-hero-wash: var(--sj-gradient-sun);
  --sj-hero-ink: var(--sj-color-ink);
  --sj-hero-ink-muted: var(--sj-color-ink-muted);

  position: relative;
  overflow: hidden;
  border-radius: var(--sj-radius-blob);
  background-color: var(--sj-hero-surface);
  background-image: var(--sj-hero-wash);
  color: var(--sj-hero-ink);
  box-shadow: var(--sj-shadow-md);
}

.sj-hero--sky {
  --sj-hero-surface: var(--sj-color-sky-soft);
  --sj-hero-wash: var(--sj-gradient-sky);
}

.sj-hero--teal {
  --sj-hero-surface: var(--sj-color-primary);
  --sj-hero-wash: var(--sj-gradient-teal);
  --sj-hero-ink: var(--sj-color-ink-inverse);
  --sj-hero-ink-muted: color-mix(in srgb, var(--sj-color-ink-inverse) 82%, transparent);
}

.sj-hero--dusk {
  --sj-hero-surface: var(--sj-color-surface-inverse);
  --sj-hero-wash: var(--sj-gradient-dusk);
  --sj-hero-ink: var(--sj-color-ink-inverse);
  --sj-hero-ink-muted: color-mix(in srgb, var(--sj-color-ink-inverse) 82%, transparent);
}

/*
  Phone layout stacks picture over words. Text is never laid over the artwork on a narrow
  screen: the illustration is busy, and a scrim dark enough to fix the contrast would hide the
  picture the screen exists to show.
*/
.sj-hero__media {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
}

.sj-hero__video,
.sj-hero__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.sj-hero__scene {
  height: 100%;
}

.sj-hero__scene .sj-scene__art {
  height: 100%;
  border-radius: 0;
  object-fit: cover;
}

/* Caller-supplied artwork, centred rather than cropped: an avatar is a shape, not a photograph. */
.sj-hero__custom {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  padding: var(--sj-space-5);
}

.sj-hero__avatar {
  box-shadow: inset 0 0 0 5px var(--sj-color-surface), var(--sj-shadow-md);
}

.sj-hero__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    transparent 60%,
    var(--sj-hero-surface) 100%
  );
}

.sj-hero__content {
  position: relative;
  padding: var(--sj-space-5) var(--sj-space-5) var(--sj-space-6);
}

.sj-hero__eyebrow {
  color: var(--sj-hero-ink-muted);
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.sj-hero__title {
  margin-top: var(--sj-space-2);
  font-size: var(--sj-text-2xl);
  font-weight: var(--sj-weight-bold);
}

.sj-hero__lead {
  margin-top: var(--sj-space-3);
  max-width: 42ch;
  color: var(--sj-hero-ink-muted);
  font-size: var(--sj-text-md);
}

.sj-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-3);
  margin-top: var(--sj-space-5);
}

@media (min-width: 48rem) {
  .sj-hero {
    min-height: 24rem;
    display: flex;
    align-items: center;
  }

  .sj-hero__media {
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    width: 54%;
    aspect-ratio: auto;
  }

  .sj-hero__scrim {
    background: linear-gradient(
      to right,
      var(--sj-hero-surface) 0%,
      color-mix(in srgb, var(--sj-hero-surface) 40%, transparent) 45%,
      transparent 75%
    );
  }

  .sj-hero__content {
    width: 56%;
    padding: var(--sj-space-10) var(--sj-space-10) var(--sj-space-10)
      var(--sj-space-8);
  }

  .sj-hero__title {
    font-size: var(--sj-text-3xl);
  }
}

/* ----------------------------------------------------------- quiz option */

.sj-quiz-option {
  display: flex;
  align-items: center;
  gap: var(--sj-space-3);
  width: 100%;
  min-height: var(--sj-touch-child-primary);
  padding: var(--sj-space-3) var(--sj-space-4);
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  box-shadow: 0 var(--sj-lift) 0 var(--sj-color-border);
  color: inherit;
  font: inherit;
  font-size: var(--sj-text-md);
  text-align: start;
  cursor: pointer;
  transition:
    border-color var(--sj-duration-fast) var(--sj-ease-standard),
    box-shadow var(--sj-duration-fast) var(--sj-ease-standard),
    transform var(--sj-duration-fast) var(--sj-ease-standard);
}

.sj-quiz-option:not(:disabled):active {
  transform: translateY(var(--sj-lift));
  box-shadow: 0 0 0 var(--sj-color-border);
}

.sj-quiz-option__marker {
  flex: none;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface-sunken);
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-bold);
}

.sj-quiz-option--selected {
  border-color: var(--sj-color-primary);
  background: var(--sj-color-primary-soft);
  box-shadow: 0 var(--sj-lift) 0 var(--sj-color-primary);
}

/* Correctness is never signalled by colour alone (spec 17): the marker
   carries a glyph and the option exposes accessible state text. */
.sj-quiz-option--correct {
  border-color: var(--sj-color-success);
  background: var(--sj-color-success-soft);
  box-shadow: 0 var(--sj-lift) 0 var(--sj-color-success);
}

.sj-quiz-option--correct .sj-quiz-option__marker {
  background: var(--sj-color-success);
  color: var(--sj-color-ink-inverse);
}

.sj-quiz-option--incorrect {
  border-color: var(--sj-color-danger);
  background: var(--sj-color-danger-soft);
  box-shadow: 0 var(--sj-lift) 0 var(--sj-color-danger);
}

.sj-quiz-option--incorrect .sj-quiz-option__marker {
  background: var(--sj-color-danger);
  color: var(--sj-color-ink-inverse);
}

/* ---------------------------------------------------------------- player */

.sj-player {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-4);
}

/*
    One compact row above the picture: the way out, then the title.

    The title truncates rather than wrapping. A Portuguese title on a phone used to break onto a
    second line and push the picture down by the height of it, and the picture is the thing a
    child came for; the full title is on the detail screen they came from, and in the theater it
    travels onto the picture as a pill.
*/
.sj-player__header {
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
  min-height: var(--sj-touch-min);
}

.sj-player__title {
  min-width: 0;
  margin: 0;
  overflow: hidden;
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-lg);
  font-weight: var(--sj-weight-bold);
  white-space: nowrap;
  text-overflow: ellipsis;
}

/*
    The way out, before the title.

    An arrow and a destination rather than a bare tick in the far corner. It is a link in spirit and
    a button in fact — leaving the player is navigation the page owns, not an href — so it is drawn
    quietly and given the 44px height everything else here has.
*/
.sj-player__leave {
  display: inline-flex;
  align-items: center;
  gap: var(--sj-space-2);
  min-height: var(--sj-touch-min);
  padding: var(--sj-space-1) var(--sj-space-3) var(--sj-space-1) var(--sj-space-2);
  border: 0;
  border-radius: var(--sj-radius-pill);
  background: transparent;
  color: var(--sj-color-ink-muted);
  font-family: inherit;
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
  cursor: pointer;
}

.sj-player__leave:hover {
  background: var(--sj-color-surface-sunken);
  color: var(--sj-color-ink);
}

.sj-player__leave-glyph {
  font-size: var(--sj-text-lg);
  line-height: 1;
}

/*
    A switch a child can read: glyph, word, and a state that is not carried by colour alone.

    Nothing in the player draws one any more — every switch is a key on the bar over the picture.
    It is kept because the studio's preview and the design gallery still render it, and because a
    labelled toggle is the right control for any surface that has room for words.
*/
.sj-toggle-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sj-space-2);
  /* Comfortable rather than minimum (UX-5): these are the primary controls on the child's own
     settings screen and the library's filter, and spec 7.1 prefers 48-56px for a child's primary
     actions. 44px is the floor, not the target. */
  min-height: var(--sj-touch-comfortable);
  padding: var(--sj-space-2) var(--sj-space-4);
  border: 2px solid var(--sj-color-border-strong);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  color: var(--sj-color-ink);
  font-family: inherit;
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
  cursor: pointer;
}

.sj-toggle-chip:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.sj-toggle-chip__glyph {
  font-size: var(--sj-text-md);
  line-height: 1;
}

/*
    The check is the third way of saying "on", after the fill and `aria-pressed`. It holds its
    width whether or not it is showing, so a chip does not change size as it is switched — a row
    of controls that reflows under the finger is a row that gets mis-tapped.
*/
.sj-toggle-chip__state {
  display: inline-block;
  min-width: 0.75rem;
  font-weight: var(--sj-weight-bold);
}

.sj-toggle-chip--on {
  border-color: var(--sj-color-primary);
  background: var(--sj-color-primary-soft);
  color: var(--sj-color-primary-hover);
}

/*
    Mute is on when the product is quiet, which is not an achievement. Terracotta rather than teal,
    so "everything is off" never looks like "everything is going well".
*/
.sj-toggle-chip--quieting {
  border-color: var(--sj-color-secondary);
  background: var(--sj-color-secondary-soft);
  color: var(--sj-color-secondary);
}

/*
    Narrower on a phone, and the height is what does not change.

    These chips live in the options menu now, which is a panel over the picture rather than a row
    under it — so what is tight here is the panel's width, not the page's. Tightening the type and
    the side padding is what keeps a Spanish label on one line inside it.

    The label is not dropped and the touch target is not shrunk. These are the controls whose whole
    point is that they say what they do in words, and the 44px minimum is the reason a child hits
    the one they aimed for (spec 17). Only the type and the padding give.
*/
@media (max-width: 30rem) {
  .sj-toggle-chip {
    gap: var(--sj-space-1);
    padding-inline: var(--sj-space-3);
    font-size: var(--sj-text-xs);
  }

  .sj-toggle-chip__glyph {
    font-size: var(--sj-text-sm);
  }

}

/*
    The picture is the biggest thing on the screen and must not be the only thing on it.

    At 4:3 across a wide column this came out 768px tall in a 1015px viewport, which pushed Next and
    "Read it to me" to 1167 — and on a scene carrying an interaction, to 1749. A story player whose
    controls are below the fold is one a child scrolls to operate, and a parent reasonably concludes
    the narration button does not exist.

    Capped against the viewport rather than a fixed height, so it still fills a phone and still
    leaves room for the words on a laptop. `cover` was already set on what goes inside, so the cap
    crops rather than squashes.

    Raised from 52dvh once the controls moved onto the picture, and from 62dvh once the words
    moved beside it. Each old figure was budgeted for chrome that no longer exists below the
    frame: first the sticky control row, the chip toolbar and the progress strip; then, at 64rem
    and up, the words themselves, which sit in the side column now. What 72dvh still reserves is
    the header above and a first line of words below on the mid-width screens that keep the
    one-column layout.

    The frame is 3:2 because that is what the pictures are. A provider draws 1536x1024 and the
    hand-drawn plates are 16:10; against a 4:3 frame `cover` had to throw a band away, and the
    band it threw away was the one with the faces in it. Characters are composed towards the top
    of a plate, so a crop that takes from both edges takes heads off.

    The width is capped as well as the height, and that is the other half of the same fix. With a
    height cap alone a wide column made the box wider than 3:2 — 1200x629 on a desktop — and the
    picture then overflowed vertically by a quarter and was cropped top and bottom again, which is
    exactly the case a phone never shows and every laptop does. Capping both keeps the frame the
    shape of the picture at every width, so nothing is ever cropped to fit the box.

    `width: 100%` is not redundant beside those caps, and leaving it out cost a whole screen. The
    player is a flex column, and **an auto inline margin on a flex item turns off the cross-axis
    stretch** — so the box stopped taking its parent's width and sized to its own content instead.
    Every child of this frame is absolutely positioned, which makes that content zero: the picture,
    the layers and the entire control bar rendered at 0x0, present in the document and invisible on
    the screen. A definite width restores the stretch, and the auto margins then only centre what
    `max-width` leaves over.
*/
.sj-scene-viewport {
  position: relative;
  overflow: hidden;
  aspect-ratio: 3 / 2;
  width: 100%;
  max-height: 72dvh;
  max-width: calc(72dvh * 3 / 2);
  margin-inline: auto;
  border-radius: var(--sj-radius-xl);
  background: var(--sj-color-surface-sunken);
  box-shadow: var(--sj-highlight-edge), var(--sj-shadow-md);

  /*
      The frame is a size container, and the bar compaction below is keyed on it.

      The compaction used to be a viewport media query, which is the same measurement only on a
      phone. The studio renders this player in a 390px stage inside a 1400px window, so none of it
      fired there: the seven keys kept their desktop padding, overflowed the frame, and the last
      one — Mute all — sat outside `overflow: hidden`, clipped and unreachable. The exact defect
      the compaction was written to fix, resurfacing wherever the player is narrower than the
      window it is in. What decides whether seven keys fit is the frame's own width, so that is
      what is asked.
  */
  container-type: inline-size;
  container-name: sj-scene;
}

/*
    On a phone the picture goes edge to edge.

    A 3:2 frame on a portrait phone is width-limited, so the height cap above never binds and the
    only headroom is the page's own gutters. Spending them takes the picture from 343px to 375px
    wide — modest, and the most a portrait phone can honestly give without cropping, because a
    taller frame would crop the plate and move every hotspot off the thing it marks.

    The negative margins are the page's inline padding, undone. They must match `.sj-page`'s
    figure at this width, which is `--sj-space-4` — a page that changes its phone gutter has to
    change this too, or the frame either overflows or stops short. The radius goes with the
    gutters: a rounded corner against the screen edge reads as a mistake rather than a style.
*/
@media (max-width: 30rem) {
  .sj-scene-viewport {
    width: calc(100% + 2 * var(--sj-space-4));
    max-width: none;
    margin-inline: calc(-1 * var(--sj-space-4));
    border-radius: 0;
  }
}

/*
    Whatever crop is left comes off the bottom, never off the top.

    The frame now matches the plates, so on a 3:2 background there is nothing to crop at all. A
    16:10 plate still overflows sideways, and one in some future shape could overflow either way —
    and when it does, the part of a scene that can be lost cheaply is the ground, not the faces.
*/
.sj-scene-viewport__background,
.sj-scene-viewport__layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 38%;
}

/*
    A layer taller in proportion than the frame is somebody, and the top of it is their head.

    Plates come back 1536x1024 and match the frame exactly. **Character and prop layers do not**:
    the shepherd is 1024x1024, and a square picture in a 3:2 frame under `cover` has to lose a third
    of its height. Centred, that took 137px off the top of a 1085px render — the crown of his head,
    on every scene he appears in, in every story. It was worse before the frame matched the plates
    and it never went away, because the two are separate shapes coming from separate requests.

    So a layer is pinned to the top rather than biased towards it. Where the layer is the frame's
    own shape this changes nothing at all — which is every background and most plates — and where it
    is taller, the crop falls on the feet and the ground, which is the half of a cut-out that
    something in front of it is usually covering anyway.

    The real repair is upstream: these layers should arrive frame-shaped and with a hole in them,
    and they arrive square and opaque. That is provider spend and a regeneration, so this is what
    the runtime can do about art that already exists.
*/
.sj-scene-viewport__layer {
  object-position: 50% 0%;
}

.sj-scene-viewport__drawn,
.sj-scene-viewport__drawn .sj-scene__art {
  width: 100%;
  height: 100%;
  max-width: none;
  border-radius: 0;
  object-fit: cover;
}

/*
    A layer's own plane. Parallax lives here; the preset animation lives on the picture inside it,
    because a transform cannot be shared and whichever declaration lands last would win outright.
*/
.sj-scene-viewport__plane {
  position: absolute;
  inset: 0;
}

/*
    The per-scene picture stack: the plate (or clip) and every layer over it, and nothing else.

    It exists so a scene change has one element to replace — SceneViewport keys it on the scene id,
    and motion.css runs the page-turn on the fresh one. Geometry-neutral on purpose: same box as
    the viewport, so every child keeps the position it had when it was a direct child. Hotspots and
    the overlay sit outside it (see the comment in SceneViewport.razor).
*/
.sj-scene-viewport__stage {
  position: absolute;
  inset: 0;
}

/*
    The picture box: the stage and the hotspots, and the thing hotspot coordinates are measured
    against.

    On the page it is the frame exactly, so nothing about the picture changed when it arrived. It
    exists for the theater, where the frame becomes the whole screen and this box is the 3:2
    picture letterboxed inside it. Geometry-neutral here; the theater block is where it is sized.
*/
.sj-scene-viewport__picture {
  position: absolute;
  inset: 0;
}

/*
    The picture following a finger, slowly.

    Nothing below applies until `scene-parallax.js` has put `--sj-parallax-x` and `-y` on the
    viewport and added the class — so a child with reduced motion on, a browser that refused the
    module, and a test host all get exactly the same still picture they had before.

    Two rules make this calm rather than jittery. The travel is ten pixels, multiplied by a depth
    the stylesheet assigns rather than by anything a manifest can ask for; and it moves *against*
    the pointer, which is what makes near things look near. The long ease is the whole point of
    "slow": the picture lags the finger by most of a second and settles rather than tracking it,
    which is a drift a child notices and never a control they have to operate.

    The headroom this needs — a picture that exactly fills its frame walks an empty edge into view
    the moment it is translated — is no longer declared here. `motion.css` holds every scene layer
    at a 6% scale for the ambient pan, using the standalone `scale` property, and that is the same
    headroom this borrows. Declaring it again in this `transform` would multiply the two.
*/
.sj-scene-viewport--following .sj-scene-viewport__background,
.sj-scene-viewport--following .sj-scene-viewport__drawn,
.sj-scene-viewport--following .sj-scene-viewport__plane,
.sj-scene-viewport--following .sj-scene-media__hero .sj-hero__image,
.sj-scene-viewport--following .sj-scene-media__hero .sj-hero__video {
  transform: translate3d(
      calc(var(--sj-parallax-x, 0) * var(--sj-parallax-depth, 1) * -10px),
      calc(var(--sj-parallax-y, 0) * var(--sj-parallax-depth, 1) * -10px),
      0
    );
  transition: transform 900ms cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}

/*
    Hotspots do not travel with the picture, and that is deliberate rather than an oversight.

    They are the buttons a child aims at. A target that drifts ten pixels away from the thing it
    marks is a target that gets missed, and the miss lands on whichever child has the least steady
    hand — which is exactly the wrong person to make the game harder for.

    They also sit above the control overlay. The story's own interactions outrank the player's
    shortcuts: the centre replay is a convenience with a permanent alternative in the options menu,
    and a hotspot is the scene. Nothing published sits below y=0.72 and the bar starts near 0.8, so
    in practice they do not overlap at all — this decides who wins if they ever do.
*/
.sj-scene-viewport__hotspots {
  position: absolute;
  inset: 0;
  z-index: 40;
}

/*
  A hotspot is a real button at a normalized position. The target is the 44px minimum even though
  the dot is smaller, because the dot is decoration and the finger is not.
*/
.sj-hotspot {
  position: absolute;
  display: grid;
  place-items: center;
  width: var(--sj-touch-min);
  height: var(--sj-touch-min);
  transform: translate(-50%, -50%);
  border: 0;
  border-radius: var(--sj-radius-pill);
  background: transparent;
  cursor: pointer;
}

.sj-hotspot__dot {
  width: 1.25rem;
  height: 1.25rem;
  border: 3px solid var(--sj-color-surface);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-accent);
  box-shadow: var(--sj-shadow-md);
}

.sj-hotspot--revealed .sj-hotspot__dot {
  background: var(--sj-color-success);
}

/*
    Below the picture: the transcript and the activity, and nothing else since the words moved
    onto the frame. Reading-width and centred, because a card that stretched across an 84rem
    picture page would set its text in lines nobody can follow. `width: 100%` beside the auto
    margins for the same flex reason the scene frame carries it.
*/
.sj-player__panel {
  width: 100%;
  max-width: 46rem;
  margin-inline: auto;
  padding: var(--sj-space-5);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-highlight-edge), var(--sj-shadow-sm);
}

/* The chooser's cards sit centred under its centred question, not against the page edge. */
.sj-player__choose-actions {
  justify-content: center;
}

/*
    The story's words, as a subtitle band on the picture.

    They lived in a box beside or below the frame, and at every width the box was the thing the
    frame was being rationed for. On the picture they cost nothing the picture was using — the
    band sits just above the bar, in the lower part of the frame most plates keep quiet — and a
    child's eyes never leave the scene to read them.

    Sized like a subtitle rather than like a paragraph. The first version took two lines of body
    text at phone width with a paragraph's padding, and with the old bar under it the band landed
    in the middle of a 250px picture — over the faces, which is where a plate keeps them. A tighter
    band and a shorter bar together put it back in the bottom third.

    `pointer-events: none`, so a hotspot behind the words still takes the press. The band is
    text, not a control; the captions key on the bar is its switch.
*/
.sj-player__subtitle {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-1);
  align-self: center;
  max-width: min(92%, 44rem);
  margin-bottom: var(--sj-space-1);
  padding: var(--sj-space-2) var(--sj-space-4);
  border-radius: var(--sj-radius-md);
  background: rgb(20 12 6 / 58%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--sj-color-ink-inverse);
  text-align: center;
  text-shadow: 0 1px 2px rgb(0 0 0 / 45%);
  pointer-events: none;
}

.sj-player__text {
  margin: 0;
  font-size: var(--sj-text-md);
  line-height: var(--sj-leading-normal, 1.45);
}

/*
    What somebody says. Story text, not decoration — a child reading with the sound off is
    reading this instead of hearing it, so it keeps the narration's size.
*/
.sj-player__dialogue {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.sj-player__dialogue-line {
  font-size: var(--sj-text-md);
  line-height: var(--sj-leading-normal, 1.45);
}

.sj-player__speaker {
  font-weight: 700;
}

/* An em dash rather than a colon: it reads as a line in a story, not as a label and a field. */
.sj-player__speaker::after {
  content: " — ";
  font-weight: 400;
  color: rgb(255 249 240 / 72%);
}

/* The authored caption line, on the rare story that writes one; dimmer than the story it times. */
.sj-player__captions {
  margin: 0;
  color: rgb(255 249 240 / 78%);
  font-size: var(--sj-text-sm);
}

/* The band gives before the picture does: tighter type, never a smaller frame. */
/* Keyed on the frame rather than the window — see the container note on `.sj-scene-viewport`. */
@container sj-scene (max-width: 30rem) {
  .sj-player__subtitle {
    max-width: 96%;
    padding: var(--sj-space-1) var(--sj-space-3);
    border-radius: var(--sj-radius-sm);
  }

  .sj-player__text,
  .sj-player__dialogue-line {
    font-size: var(--sj-text-sm);
    line-height: 1.35;
  }
}

/*
    The quiz: one question per screen, and nothing on it that looks like a scoreboard.

    Sized like the story rather than like a form, because it is the same child reading.
*/
.sj-quiz {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-4);
  padding: var(--sj-space-5);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-highlight-edge), var(--sj-shadow-sm);
}

.sj-quiz__progress {
  margin: 0;
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
}

.sj-quiz__prompt {
  margin: 0;
  font-size: var(--sj-text-lg);
  line-height: var(--sj-leading-relaxed);
}

/*
    How far through the questions.

    Filled for questions answered, never for questions answered correctly: every question here ends
    correctly, because a wrong answer is met with a hint and another try. A row that marked right
    and wrong would be a scoreboard, and this quiz does not keep one.
*/
.sj-quiz__pips {
  display: flex;
  gap: var(--sj-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.sj-quiz__pip {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-border-strong);
}

.sj-quiz__pip--done {
  background: var(--sj-color-accent);
}

.sj-quiz__pip--current {
  background: var(--sj-color-primary);
  transform: scale(1.25);
}

.sj-quiz__options {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-3);
}

.sj-quiz__feedback {
  margin: 0;
  font-size: var(--sj-text-md);
}

/* The only colour that changes, and it is never the only signal: the option carries a glyph too. */
.sj-quiz__feedback--correct {
  color: var(--sj-color-success);
}

.sj-quiz__explanation {
  margin: 0;
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-md);
  line-height: var(--sj-leading-relaxed);
}

.sj-quiz__controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sj-space-3);
}

/*
    Past one question, quietly. Drawn as words rather than as a button on purpose — it is the
    third-choice way forward, after answering and after leaving — but still a 44px target,
    because quiet is a look and not a smaller thing to press.
*/
.sj-quiz__skip-one {
  min-height: 2.75rem;
  padding: var(--sj-space-2) var(--sj-space-3);
  border: 0;
  background: transparent;
  color: var(--sj-color-primary);
  font: inherit;
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-bold);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}

/*
    What finishing gave. Stars, points, a badge, and then a question to sit with — in that order,
    so the last thing on the screen is the reflection rather than the reward.
*/
.sj-player__reward {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sj-space-3);
}

/*
    The burst, and where it comes from.

    Positioned rather than laid out: it must not take space, or every screen that celebrates
    something would jump as it arrived and settle back as it left. `pointer-events: none` because
    it is decoration standing over controls a child may be reaching for at that moment.
*/
.sj-celebrate {
  position: absolute;
  inset-inline-start: 50%;
  inset-block-start: 0;
  z-index: 2;
  display: block;
  width: 0;
  height: 0;
  pointer-events: none;
}

.sj-celebrate__spark {
  position: absolute;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-accent);
}

/* Every other spark in the story's own gold-and-teal, so the burst reads as this product's. */
.sj-celebrate__spark:nth-child(even) {
  background: var(--sj-color-primary);
}

.sj-player__stars {
  display: flex;
  gap: var(--sj-space-2);
  margin: 0;
  font-size: var(--sj-text-2xl);
}

/* An unearned star is drawn, not hidden: three of them are what makes the count readable. */
.sj-star {
  color: var(--sj-color-border-strong);
}

.sj-star--earned {
  color: var(--sj-color-accent);
}

.sj-player__score,
.sj-player__xp {
  margin: 0;
  font-size: var(--sj-text-md);
}

.sj-player__badge {
  margin: 0;
  color: var(--sj-color-success);
  font-size: var(--sj-text-md);
}

/*
    Decoration beside the line that announces it, so it is alt-empty: the sentence under it is the
    accessible version, and reading both would say the same thing twice.
*/
.sj-player__badge-art {
  width: 96px;
  height: 96px;
}

.sj-player__reflection {
  margin: 0;
  max-width: 42ch;
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-md);
  line-height: var(--sj-leading-relaxed);
  text-align: center;
}

/* Offered rather than imposed, so it sits below the story with its own edge. */
.sj-player__transcript {
  margin-top: var(--sj-space-4);
  padding: var(--sj-space-4);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-md);
  background: var(--sj-color-surface-sunken);
}

.sj-player__transcript-text {
  margin: 0;
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-md);
  line-height: var(--sj-leading-relaxed);
}

/* ------------------------------------------------- the bar on the picture */

/*
    The player's controls, along the bottom edge of the picture, the way a video player's are.

    Everything below is an overlay, so it takes the picture's space rather than the page's. That is
    the whole gain: the sticky control row, the chip toolbar and the progress dots that used to sit
    under the scene added up to a third of a phone screen of chrome, and the story was what got
    pushed off the bottom.

    Two departures from the video player it borrows from, and both are the audience.

    It never hides. An auto-hiding bar assumes a viewer who knows the controls exist and will summon
    them; a four-year-old does not, and a control they cannot see is a control they do not have.

    Its targets stay 44px. A media bar is normally a row of 24px glyphs, which is the smallest thing
    on the screen handed to the people with the least steady hands (spec 17).

    What changed with the round keys. The bar used to be two rows — the chapter strip and then a
    row of glyph-over-word keys — under a scrim that started halfway up the frame, and on a phone
    the lot came to 107px of a 250px picture with the subtitle band above that: close to half the
    scene under its own chrome, which is what "the buttons look off and too big" was. It is one
    row of 44px discs now, with the strip as a thin line along the very bottom edge, and the scrim
    only as tall as the row needs. Measured at 375px: 60px of chrome on the picture, and the
    subtitle back in the bottom third where a subtitle goes.
*/
.sj-scene-viewport__overlay {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  /*
      The overlay itself catches nothing — only the controls inside it do. Hotspot coordinates are
      normalized against the whole picture, so an author may put one low; nothing published today
      sits below y=0.72 and the bar starts around 0.8, but a hotspot that did would still be
      reachable in the gaps between buttons rather than sealed under a transparent sheet.
  */
  pointer-events: none;
}

.sj-player__bar {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-2);
  padding: var(--sj-space-6) var(--sj-space-3) 0;
  /*
      A scrim, not a panel. The picture continues underneath, which is what keeps the frame feeling
      like one thing — and the gradient is what makes white glyphs legible over artwork nobody has
      seen yet. An opaque strip would crop the bottom of every scene instead.

      The bar catches no presses of its own either: the keys and the strip do. The scrim's upper
      band is picture, and a hotspot under it stays reachable.
  */
  background: linear-gradient(
    to bottom,
    rgb(20 12 6 / 0%) 0%,
    rgb(20 12 6 / 40%) 45%,
    rgb(20 12 6 / 78%) 100%
  );
  color: var(--sj-color-ink-inverse);
  pointer-events: none;
}

/*
    The row of keys. Lifted above the chapter strip in the stack — the strip's 44px hit bands
    reach up under this row, and without the lift a press on the lower half of a key would jump to
    a scene instead.
*/
.sj-player__bar-row {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
}

.sj-player__bar-group {
  display: flex;
  align-items: center;
  gap: var(--sj-space-2);
}

.sj-player__bar-group--end {
  margin-inline-start: auto;
}

/*
    One key on the bar: a round disc of dark glass with a white glyph, the shape a floating action
    button has everywhere else.

    Glyph only. The word that used to sit under it was clipped away on every phone anyway, and on
    a wider screen it made each key a labelled tile — a row of tiles reads as a toolbar, and a row
    of discs reads as a player. Every key keeps its accessible name, so a screen reader hears
    "Read it to me" exactly as before.

    The glass matters more than it looks. A transparent key over artwork is a glyph whose contrast
    depends on the picture behind it; a disc with its own ground is legible over a night sky and a
    sunlit wall alike, and the blur is what keeps it from reading as a hole punched in the scene.

    The size is a property rather than a value because the phone rule below has to shrink seven
    keys to fit a frame, and the one place a size is computed is easier to trust than three.
*/
.sj-player__key {
  --sj-player-key: var(--sj-touch-min);

  position: relative;
  display: grid;
  flex: none;
  place-items: center;
  width: var(--sj-player-key);
  height: var(--sj-player-key);
  padding: 0;
  border: 1px solid rgb(255 255 255 / 22%);
  border-radius: var(--sj-radius-pill);
  background: rgb(20 12 6 / 42%);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: var(--sj-color-ink-inverse);
  font-family: inherit;
  font-size: var(--sj-text-lg);
  cursor: pointer;
  pointer-events: auto;
  transition:
    background-color var(--sj-duration-fast) var(--sj-ease-standard),
    transform var(--sj-duration-fast) var(--sj-ease-standard);
}

.sj-player__key:hover:not(:disabled) {
  background: rgb(255 255 255 / 24%);
}

/* A press is felt as well as heard: the disc gives slightly under the finger. */
.sj-player__key:active:not(:disabled) {
  transform: scale(0.94);
}

.sj-player__key:disabled {
  opacity: 0.35;
  cursor: default;
}

.sj-player__key:focus-visible {
  outline: 3px solid var(--sj-color-focus);
  outline-offset: 2px;
}

.sj-player__key-glyph {
  display: block;
  line-height: 1;
}

/*
    The one key that is filled in rather than glass. It is what a child presses to be read to,
    and on a bar of seven controls something has to be the obvious one — a little larger too,
    where there is room.
*/
.sj-player__key--play {
  --sj-player-key: var(--sj-touch-comfortable);

  border-color: transparent;
  background: var(--sj-color-surface);
  color: var(--sj-color-primary-hover);
  font-size: var(--sj-text-xl);
  box-shadow: var(--sj-shadow-md);
}

.sj-player__key--play:hover:not(:disabled) {
  background: var(--sj-color-primary-soft);
}

/*
    Moving on is the other thing a child does on every scene, so it is drawn a notch brighter
    than the switches beside it.

    One treatment, on every scene including the last. There used to be a `--finish` variant that
    turned this key gold and changed its glyph to a tick, which made the single control a child
    presses most become a different-looking thing exactly once — at the moment they were least
    expecting it.
*/
.sj-player__key--next {
  background: rgb(255 255 255 / 22%);
}

/*
    A switch that is on: a brighter disc and a short line under the glyph, so the state is not
    carried by colour alone — the same mark a caption key wears on every player this audience
    knows.
*/
.sj-player__key--on {
  border-color: rgb(255 255 255 / 55%);
  background: rgb(255 255 255 / 26%);
}

.sj-player__key--on::after {
  content: "";
  position: absolute;
  inset-block-end: 0.375rem;
  inset-inline-start: 50%;
  width: 0.875rem;
  height: 0.125rem;
  border-radius: var(--sj-radius-pill);
  background: currentColor;
  transform: translateX(-50%);
}

/* Silence is not an achievement, so it is not drawn as one. */
.sj-player__key--quieting {
  border-color: transparent;
  background: var(--sj-color-secondary);
  color: var(--sj-color-ink-inverse);
}

.sj-player__position {
  margin: 0;
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-bold);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 2px rgb(0 0 0 / 45%);
}

/*
    How far through the story: one segment per scene, not a bar that fills, along the bottom edge
    of the picture where a progress line lives on every player.

    A story is five parts, not a running time. Segments are countable — "five parts, I am on the
    second" — and a continuous bar says nothing a child can check against anything.
*/
.sj-player__track {
  display: flex;
  gap: 0.25rem;
  margin: 0;
  padding: 0 0 var(--sj-space-2);
  list-style: none;
}

.sj-player__track-slot {
  flex: 1;
  display: flex;
}

/*
    Each part is a button, and the button is much bigger than the line it draws.

    A 4px strip is not a target — it is a tenth of the 44px minimum, aimed at by the people with
    the least steady hands. So the button is a transparent 44px band with the line along its
    bottom edge, and the negative margin lets the band reach up under the row of keys rather than
    making the bar 44px taller than it looks. The row is lifted above it in the stack, so the band
    only ever takes the presses that land between the keys.
*/
.sj-player__track-part {
  flex: 1;
  display: flex;
  align-items: flex-end;
  height: var(--sj-touch-min);
  margin-block-start: calc(0.25rem - var(--sj-touch-min));
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  pointer-events: auto;
}

.sj-player__track-line {
  width: 100%;
  height: 0.25rem;
  border-radius: var(--sj-radius-pill);
  background: rgb(255 255 255 / 32%);
  transition:
    background-color var(--sj-duration-fast) var(--sj-ease-standard),
    height var(--sj-duration-fast) var(--sj-ease-standard);
}

/* Read already. Gold, so "read" and "reading now" are two states rather than one. */
.sj-player__track-part--done .sj-player__track-line {
  background: var(--sj-color-accent);
}

.sj-player__track-part--current .sj-player__track-line {
  background: var(--sj-color-surface);
}

/* Aimable: the line thickens under the pointer rather than the row moving. */
.sj-player__track-part:hover .sj-player__track-line,
.sj-player__track-part:focus-visible .sj-player__track-line {
  height: 0.375rem;
  background: var(--sj-color-surface);
}

.sj-player__track-part:focus-visible {
  outline: 3px solid var(--sj-color-focus);
  outline-offset: -3px;
}

/*
    What a switch just did, said over the picture just above the bar that did it.
*/
.sj-player__flash {
  display: flex;
  justify-content: center;
  padding: var(--sj-space-3);
  pointer-events: none;
}

.sj-player__flash-pill {
  padding: var(--sj-space-2) var(--sj-space-4);
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface-inverse);
  color: var(--sj-color-ink-inverse);
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-medium);
  text-align: center;
  box-shadow: var(--sj-shadow-md);
}

/*
    On a phone the bar gets out of the picture's way.

    Keyed on the frame rather than the window — see the container note on `.sj-scene-viewport`.
    In the app the two measurements agree on a phone; in the studio's 390px stage only the frame's
    own width tells the truth about whether seven keys fit.

    The padding and the gaps give, and the play key gives up its extra size. What does not give
    is the 44px key — those are the whole reason a child hits the control they aimed for (spec
    17), and shrinking targets to reclaim a picture would be trading the wrong thing.

    The arithmetic, because it is tight. Seven 44px keys with 4px gaps is 332px inside a 359px
    bar at 375px wide. But a child on the Big word size scales every rem by 1.2, and seven 53px
    keys is 370px — so the key is computed from the frame instead: an equal share of what is left
    once the bar's padding and the six gaps are spent, capped at the 44px it wants. At the default
    size that cap always wins; at Big it comes out 46.7px, which is still over the minimum. The
    rare eighth key — a transcript the story authored separately — closes the gaps and takes an
    eighth share, 43.9px, which is the one place a target is a tenth of a pixel under the line.
*/
@container sj-scene (max-width: 30rem) {
  .sj-player__bar {
    gap: var(--sj-space-1);
    padding: var(--sj-space-4) var(--sj-space-2) 0;
  }

  .sj-player__bar-row,
  .sj-player__bar-group {
    gap: var(--sj-space-1);
  }

  .sj-player__key,
  .sj-player__key--play {
    --sj-player-key: min(var(--sj-touch-min), calc((100cqw - 2.5rem) / 7));

    font-size: var(--sj-text-lg);
  }

  .sj-player__bar-row:has(.sj-player__bar-group--end > :nth-child(4)) .sj-player__bar-group {
    gap: 0;
  }

  .sj-player__bar-row:has(.sj-player__bar-group--end > :nth-child(4)) .sj-player__key {
    --sj-player-key: min(var(--sj-touch-min), calc((100cqw - 1.25rem) / 8));
  }

  /*
      And the position number goes.

      Nine pixels wide once flex has finished squeezing it, which is narrower than "1 / 8" — so it
      wrapped to four lines and made the row 68px tall instead of 44. The strip below already
      shows where the child is, and the sentence a screen reader announces is a separate span
      that stays: what is dropped here is the duplicate, not the information.
  */
  .sj-player__position-count {
    display: none;
  }

  .sj-player__track {
    padding-bottom: var(--sj-space-1);
  }
}

/* ----------------------------------------------------------- interaction */

.sj-interaction {
  margin-top: var(--sj-space-5);
  padding: var(--sj-space-5);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-primary-soft);
}

.sj-interaction__prompt {
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-lg);
  font-weight: var(--sj-weight-bold);
}

.sj-interaction__reveals,
.sj-interaction__chosen {
  margin: var(--sj-space-3) 0 0;
  padding-inline-start: var(--sj-space-6);
}

.sj-interaction__reveals {
  list-style: none;
  padding-inline-start: 0;
}

.sj-interaction__revealed {
  padding: var(--sj-space-3);
  border-radius: var(--sj-radius-md);
  background: var(--sj-color-surface);
}

.sj-interaction__options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sj-space-3);
  margin-top: var(--sj-space-3);
}

/* find-object. A row of things to look at, wrapping rather than shrinking: a target a child has
   to hit precisely is a target some children cannot hit at all (spec 17). */
/*
 * Picture choices, as cards.
 *
 * These used to be text buttons with a 6rem image dropped inside them: the image came out 96px tall
 * in a 98px control, so it filled its own button edge to edge and pushed against the border. It
 * read as a mistake because it was one — a control sized for one line of text was being asked to
 * hold a picture.
 *
 * A grid instead, sized so the picture is the thing a child aims at. `auto-fit` with a floor keeps
 * three choices on one row on a tablet and stacks them on a phone without a media query.
 */
.sj-interaction__objects {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: var(--sj-space-3);
  margin: var(--sj-space-3) 0 0;
  padding-inline-start: 0;
  list-style: none;
}

/*
 * The button becomes the card. Styled through the container rather than by giving the shared
 * button a modifier, because this shape is specific to picture choices and the button is used
 * across both applications.
 */
.sj-interaction__objects .sj-button {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--sj-space-2);
  width: 100%;
  height: 100%;
  padding: var(--sj-space-2);
  text-align: center;
}

.sj-interaction__object {
  display: block;
  width: 100%;
  /* Square, and cropped rather than squashed: `fill` was distorting anything not already 1:1. */
  aspect-ratio: 1 / 1;
  height: auto;
  object-fit: cover;
  border-radius: var(--sj-radius-md, 0.75rem);
}

.sj-interaction__object-label {
  display: block;
  font-size: var(--sj-text-sm);
  line-height: var(--sj-leading-snug, 1.3);
  /* Two lines before it truncates, because pt-BR and es-ES labels run longer than the English. */
  overflow-wrap: anywhere;
}

.sj-interaction__result {
  margin-top: var(--sj-space-3);
  font-weight: var(--sj-weight-bold);
}

.sj-interaction__result--correct {
  color: var(--sj-color-success);
}

/*
    How much of the activity is behind the child, as pips rather than as a number.

    "2 of 3" is a reading task and this audience starts at four, so the count is drawn and also
    said — the spoken form is the clearer one for anybody using a screen reader, and the drawn one
    is the clearer one for a child who is still learning their numbers.
*/
.sj-interaction__tally {
  display: flex;
  justify-content: center;
  margin-top: var(--sj-space-4);
}

.sj-interaction__pips {
  display: flex;
  gap: var(--sj-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.sj-interaction__pip {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: var(--sj-radius-pill);
  background: var(--sj-color-surface);
  box-shadow: inset 0 0 0 2px var(--sj-color-border-strong);
}

.sj-interaction__pip--done {
  background: var(--sj-color-accent);
  box-shadow: inset 0 0 0 2px var(--sj-color-accent);
}

/* Closure, for the four activities that have no last word of their own. */
.sj-interaction__complete {
  position: relative;
  margin-top: var(--sj-space-3);
  color: var(--sj-color-success);
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-lg);
  font-weight: var(--sj-weight-bold);
  text-align: center;
}

/* The burst anchors to the line it belongs to, which has to be a positioned box for it to. */
.sj-quiz__feedback--correct {
  position: relative;
}

/* The promise, in words, on every interaction. */
.sj-interaction__optional {
  margin-top: var(--sj-space-4);
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
}

/* ------------------------------------------------------------- scene media */

/*
    A scene's moving picture, drawn through `SJHeroMedia` because ADR-0008 permits one video
    component in the product. The hero is built for the top of a page, so its copy area and scrim
    are flattened away here — what is wanted is the picture and the motion rules, not the layout.
*/
.sj-scene-media {
  margin-bottom: var(--sj-space-4);
}

.sj-scene-media__hero {
  min-height: 0;
}

/*
    The picture needs its own height here, and this is the one place it has none.
    
    At desk width the hero is a split layout: a text column on the left and `.sj-hero__media`
    absolutely positioned down the right, taking its height from the column beside it. The scene
    player has no column — it hides `.sj-hero__content` and drops `min-height` — so the hero
    collapsed, and the video inside it, being `height: 100%` of nothing, came out **553px wide and
    0px tall**. Present, playing, muted, and impossible to see. There was no poster to fall back to
    either, because the poster is the same zero-height element.

    So the split layout is undone for this context and the frame gets its aspect ratio back.
*/
.sj-scene-media__hero .sj-hero__media {
  position: relative;
  inset: auto;
  width: 100%;
  aspect-ratio: 16 / 10;
  /* The same ceiling as a drawn scene, so a filmed one does not push the controls further down. */
  max-height: 72dvh;
}

.sj-scene-media__hero .sj-hero__content {
  display: none;
}

.sj-scene-media__hero .sj-hero__scrim {
  display: none;
}

/*
    What a child gets instead of the clip. Visible rather than hidden for screen readers: the people
    who turned motion off are the people it was written for, and most of them can see.
*/
.sj-scene-media__alternative,
.sj-scene-viewport__alternative {
  margin: var(--sj-space-2) 0 0;
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-sm);
}

/*
    The next scene's pictures, downloading where nobody can see them.

    Off the left edge rather than `display: none`: a browser may decline to fetch an image it has
    been told not to lay out, and one that declined would leave the preload doing nothing while
    looking exactly as though it worked. One pixel, no paint, no pointer, and out of the flow so it
    cannot lengthen the page.
*/
.sj-player__preload {
  position: absolute;
  inset-inline-start: -9999px;
  inset-block-start: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

/*
    The offer on the reward screen: one story, with its cover to look at, going to that story's
    detail page. A card and never a countdown.
*/
.sj-player__next {
  display: flex;
  align-items: center;
  gap: var(--sj-space-3);
  max-width: 22rem;
  margin-top: var(--sj-space-2);
  padding: var(--sj-space-2) var(--sj-space-4);
  border: 1px solid var(--sj-color-border);
  border-radius: var(--sj-radius-lg);
  background: var(--sj-color-surface);
  box-shadow: var(--sj-shadow-sm);
  color: inherit;
  text-decoration: none;
  min-height: 3.5rem;
}

.sj-player__next-cover {
  flex: none;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: var(--sj-radius-md);
  object-fit: cover;
}

.sj-player__next-words {
  display: flex;
  flex-direction: column;
  gap: var(--sj-space-1);
  text-align: start;
}

.sj-player__next-lead {
  color: var(--sj-color-ink-muted);
  font-size: var(--sj-text-xs);
  font-weight: var(--sj-weight-bold);
  letter-spacing: 0.02em;
}

.sj-player__next-title {
  font-weight: var(--sj-weight-bold);
}

/*
    There is no side column any more, at any width. The words-beside-the-picture grid existed to
    keep a text box from pushing the story below the fold; with the words on the picture as a
    subtitle, the frame simply takes the width the page gives it and the occasional panel — a
    transcript, an activity — sits under it at reading width.
*/

/* --------------------------------------------------------------- theater */

/*
    The way into full screen, in the picture's top corner — the same dark-glass disc as the bar's
    keys, so it reads as one of them. Deliberately not a bar key: the bar's keys already fill a
    phone to the last pixel, and this is a way of looking at the picture rather than a switch over
    the story, so it lives on the picture the way it does in every photo viewer this audience
    knows.

    Hotspots sit above it in the stack, so the story's own interactions win any contested press.
*/
.sj-player__expand {
  position: absolute;
  inset-block-start: var(--sj-space-3);
  inset-inline-end: var(--sj-space-3);
  z-index: 1;
  display: grid;
  place-items: center;
  width: var(--sj-touch-min);
  height: var(--sj-touch-min);
  border: 1px solid rgb(255 255 255 / 22%);
  border-radius: var(--sj-radius-pill);
  background: rgb(20 12 6 / 42%);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: var(--sj-color-ink-inverse);
  font-size: var(--sj-text-lg);
  opacity: 0.85;
  pointer-events: auto;
  cursor: pointer;
  transition:
    opacity var(--sj-duration-normal) var(--sj-ease-standard),
    background-color var(--sj-duration-normal) var(--sj-ease-standard);
}

.sj-player__expand:hover,
.sj-player__expand:focus-visible {
  opacity: 1;
  background: rgb(255 255 255 / 24%);
}

.sj-player__expand:focus-visible {
  outline: 3px solid var(--sj-color-focus);
  outline-offset: 3px;
}

.sj-player__expand-glyph {
  display: block;
  line-height: 1;
}

/*
    The theater: only the picture.

    The player as a fixed layer over the whole viewport, on a warm black — and the frame is the
    whole layer. The page's header is gone, the title is a pill on the picture beside the way
    back, and the 3:2 picture is letterboxed inside the frame as large as the shorter side of the
    screen allows: the full width of a portrait phone with black above and below, the full height
    of a landscape one with black at either side. It is never cropped to fill, because a hotspot is
    placed against the picture and a crop moves it off the thing it marks.

    CSS is the mechanism and works everywhere; the Fullscreen API, where it exists, is asked for
    on top by `theater.js` — iPhone Safari has no element fullscreen at all, and it is the device
    where the stage is smallest, so nothing here may depend on the API.

    What the first theater got wrong, and why the picture vanished in landscape. It kept the page
    layout on a dark ground — header, gaps, padding, frame capped at `100dvh - 7rem` — inside a
    flex column. The frame has `overflow: hidden`, which makes its flex minimum zero, so on a
    375px-tall landscape phone with an activity card under it the column simply squeezed the
    frame to nothing: the "smaller" scene the report described was, measured, a 0px one. The
    frame has a definite height now and nothing under it can take any of it.

    Nothing auto-hides, for the same reason the bar never does. The scene text stays on the
    picture as its subtitle and can be switched off by the same key as everywhere else.

    The quiz and the reward never render in here — the player leaves the theater when the reading
    ends — so no rule below needs to restyle them for a dark ground.
*/
/*
    The page must stop being the theater's containing block, and this is why it would be.

    `.sj-page` keeps its `sj-rise` arrival animation applied at its finished state (`fill: both`),
    and an element with an applied transform animation is the containing block for every `fixed`
    descendant — even at the identity transform the finished state computes to. Without this rule
    the theater's `inset: 0` resolves against the page instead of the viewport: measured, the
    "full screen" layer came out 1344x96, the page's own collapsed box, with the story overflowing
    it. Lifting the animation while the theater is open changes nothing visible — the arrival ran
    long ago and its finished state is the natural one — and gives the layer the viewport back.
*/
.sj-page:has(.sj-player--theater) {
  animation: none;
}

.sj-player--theater {
  position: fixed;
  inset: 0;
  z-index: var(--sj-z-overlay);
  gap: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: rgb(20 12 6);
}

/* Only the picture. The title and the way out are on it. */
.sj-player--theater .sj-player__header {
  display: none;
}

/*
    The frame is the screen. A definite height — the flex column may not shrink it, which is the
    landscape defect above — and no cap, no radius, no shadow, no gutter: there is nothing around
    it to keep a distance from. It centres the picture box inside itself and lets the overlay
    cover the lot, so the bar lands at the screen's bottom edge whether that is letterbox or
    picture.
*/
.sj-player--theater .sj-scene-viewport {
  /*
      The picture's width: as wide as the screen, unless the screen is too short for that — then
      as tall as the screen. `min` of the two is the letterbox, and the safe insets are subtracted
      so a landscape notch never sits over the edge of the scene. A property because the subtitle
      below needs the same number to find the picture's bottom edge.
  */
  --sj-theater-picture: min(
    calc(100% - var(--sj-safe-left) - var(--sj-safe-right)),
    calc((100dvh - var(--sj-safe-top) - var(--sj-safe-bottom)) * 3 / 2)
  );

  display: grid;
  flex: none;
  place-items: center;
  width: 100%;
  height: 100dvh;
  max-width: none;
  max-height: none;
  aspect-ratio: auto;
  margin: 0;
  border-radius: 0;
  background: rgb(20 12 6);
  box-shadow: none;
}

.sj-player--theater .sj-scene-viewport__picture {
  position: relative;
  inset: auto;
  width: var(--sj-theater-picture);
  aspect-ratio: 3 / 2;
  overflow: hidden;
}

/*
    The subtitle stays with the picture, not with the bar.

    On a portrait phone the picture is a 3:2 band across the middle of the screen and the bar is
    at the foot of it, two hundred pixels lower. A subtitle down there beside the keys is a
    caption for the letterbox; a child reads the words where the faces are. So the band is pushed
    up from the bar by however much black lies between the bar and the picture's bottom edge —
    half the screen, less half the picture's height (a third of its width, at 3:2), less the bar
    — and clamps to its ordinary gap wherever the picture reaches the bar anyway, which is every
    landscape screen.
*/
.sj-player--theater .sj-player__subtitle {
  margin-bottom: max(
    var(--sj-space-1),
    calc(50dvh - var(--sj-theater-picture) / 3 - 4.75rem - var(--sj-safe-bottom))
  );
}

.sj-player--theater .sj-scene-media__hero .sj-hero__media {
  max-height: none;
}

/* The controls keep clear of the notch and the home indicator. */
.sj-player--theater .sj-scene-viewport__overlay {
  padding: var(--sj-safe-top) var(--sj-safe-right) 0 var(--sj-safe-left);
}

.sj-player--theater .sj-player__bar {
  padding-bottom: var(--sj-safe-bottom);
}

/*
    The way back to the page moves to the top-left corner, which is where a back control lives on
    every full-screen player, and the title sits beside it.
*/
.sj-player--theater .sj-player__expand {
  inset-block-start: calc(var(--sj-space-3) + var(--sj-safe-top));
  inset-inline-start: calc(var(--sj-space-3) + var(--sj-safe-left));
  inset-inline-end: auto;
}

/*
    The title as a pill beside the way back. Text, not a control, and it truncates rather than
    wrapping over the picture: the child knows which story they are in.
*/
.sj-player__marquee {
  position: absolute;
  inset-block-start: calc(var(--sj-space-3) + var(--sj-safe-top));
  inset-inline-start: calc(var(--sj-space-3) + var(--sj-touch-min) + var(--sj-space-2) + var(--sj-safe-left));
  z-index: 1;
  max-width: calc(100% - 2 * var(--sj-space-3) - var(--sj-touch-min) - var(--sj-space-2) - var(--sj-safe-left) - var(--sj-safe-right));
  margin: 0;
  padding: var(--sj-space-2) var(--sj-space-3);
  overflow: hidden;
  border-radius: var(--sj-radius-pill);
  background: rgb(20 12 6 / 42%);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: var(--sj-color-ink-inverse);
  font-family: var(--sj-font-display);
  font-size: var(--sj-text-sm);
  font-weight: var(--sj-weight-bold);
  line-height: 1.6;
  white-space: nowrap;
  text-overflow: ellipsis;
  pointer-events: none;
}

/*
    An activity or a transcript below the full-screen frame, reached by scrolling the layer. The
    frame keeps the whole screen so the picture is never smaller for the scene having something
    under it; the card takes the page's own light surface and its own ink, because the layer sets
    no `color` of its own — the first theater did, and cream ink landed on a white card.
*/
.sj-player--theater .sj-player__panel {
  width: calc(100% - 2 * var(--sj-space-4));
  margin: var(--sj-space-4) auto calc(var(--sj-space-6) + var(--sj-safe-bottom));
}
