/* The Studio film: one continuous walk through Fulltrace Studio, played from real captures.

   The frames are shot by slice-runner/studio-drive.js from the packaged extension in a throwaway
   VS Code, sidebar and Studio together, in both themes. The cursor, the presses, the scrolls, the
   spotlights and the captions are drawn here, over the frames, from the manifest the capture wrote,
   so the next UI change is a re-capture and never a hand-edited coordinate. Wiring is js/film.js.

   Every overlay is positioned in percent of the capture canvas, whose size the manifest states; the
   stage takes that ratio from js/film.js, so the whole stage scales as one piece. */

.film {
  margin: 0;
  /* The film's own tokens, each falling back through the showcase site's name and then the product
     site's, so one stylesheet serves both without either site learning the other's vocabulary. */
  --film-panel: var(--bg-panel, var(--surf, #10121a));
  --film-accent: var(--accent, var(--ember-500, #8b93e8));
  --film-rule: var(--rule, var(--border, rgba(255, 255, 255, 0.08)));
  --film-line: var(--card-border, var(--border-2, rgba(255, 255, 255, 0.12)));
  --film-line-strong: var(--hairline-strong, var(--border-2, rgba(255, 255, 255, 0.16)));
  --film-btn: var(--card, transparent);
  --film-btn-hover: var(--card-hover, rgba(127, 127, 127, 0.1));
  --film-text: var(--body, #dfe4ee);
  --film-muted: var(--muted, #8b93a5);
  --film-sans: var(--font-sans, var(--font-display, sans-serif));
  --film-mono: var(--font-mono, monospace);
  /* What a spotlight dims the rest of the frame towards: the old tours' dim, which is the page's own
     near-black in dark and its own paper in light, so a lit region reads as lifted either way. */
  --film-dim: rgba(14, 14, 17, 0.8);
}

:root[data-theme="light"] .film {
  --film-dim: rgba(250, 250, 252, 0.8);
}

/* A film shot in one theme only (the board has no light theme) dims towards that theme's page and
   keeps its stage colour whatever the site's theme, so a lit region on a dark frame never sits under
   a pale wash. The captions and chapter cards still follow the site. js/film.js sets the attribute. */
:root[data-theme="light"] .film[data-film-theme="dark"] {
  --film-dim: rgba(14, 14, 17, 0.8);
}

:root[data-theme="light"] .film[data-film-theme="dark"] .film__stage {
  background: #1f1f1f;
}

/* And a film shot light only (a Salesforce org has no dark theme) does the same the other way. */
:root:not([data-theme="light"]) .film[data-film-theme="light"] {
  --film-dim: rgba(250, 250, 252, 0.8);
}

:root:not([data-theme="light"]) .film[data-film-theme="light"] .film__stage {
  background: #f8f8f8;
}

.film__body {
  display: flex;
  flex-direction: column;
  background: var(--film-panel);
}

.film__stage {
  position: relative;
  width: 100%;
  aspect-ratio: 1500 / 664;
  overflow: hidden;
  background: #1f1f1f;
  isolation: isolate;
  container-type: inline-size;
}

:root[data-theme="light"] .film__stage {
  background: #f8f8f8;
}

/* The camera. Frames sit inside it, and a zoom is a transform on this layer only, so the cursor and
   the captions above it never move with it. No will-change: a layer promoted for good keeps the
   resolution it started at, and a zoomed frame would then be a stretched copy of an unzoomed one. */
.film__camera {
  position: absolute;
  inset: 0;
  transform-origin: 0 0;
  transition: transform calc(900ms / var(--film-speed, 1)) cubic-bezier(0.45, 0, 0.2, 1);
}

.film__camera--instant {
  transition: none;
}

/* A cut, the way an app changes under a click, softened by 140ms. The incoming frame dissolves in
   over the outgoing one, which stays fully opaque underneath until the swap is done, so two frames
   of UI text are never half-visible at once. */
.film__frame {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: auto;
  opacity: 0;
  z-index: 0;
  user-select: none;
  -webkit-user-drag: none;
}

.film__frame.is-shown {
  opacity: 1;
  z-index: 2;
  transition: opacity 140ms ease-out;
}

.film__frame.is-leaving {
  opacity: 1;
  z-index: 1;
}

/* The landing frame of a scroll replaces the composite pixel for pixel, so it cuts. */
.film--instant .film__frame.is-shown {
  transition: none;
}

/* A scroll. The scroller's content slides in a window the size of the scroller, below the band its
   pinned rows sit in, from a strip stitched out of the frames the scroll passed through. */
.film__scroll {
  position: absolute;
  z-index: 3;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

.film__scroll.is-on {
  opacity: 1;
}

.film__strip {
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  width: 100%;
  user-select: none;
}

/* A pane that moves with a scroll, its states side by side in one image; js/film.js sets the width
   (one state per pane width) and shifts it to the state the scroll has reached. */
.film__side {
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  height: 100%;
  max-width: none;
  user-select: none;
}

/* Crops of the landing frame laid over the pinned band and the scrollbar, fading in while the page
   scrolls, because the one pins and the other's thumb moves rather than scrolls. */
.film__crop {
  position: absolute;
  z-index: 4;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

.film__crop.is-on.is-running {
  opacity: 1;
  transition-property: opacity;
  transition-timing-function: ease-in-out;
}

.film__crop-img {
  position: absolute;
  display: block;
  max-width: none;
  user-select: none;
}

/* The spotlight: everything but the region dimmed, the way the old tours lit a step. It glides from
   one region to the next. Inside the camera, so a zoom carries it with the frame. */
.film__spot {
  position: absolute;
  z-index: 5;
  border-radius: 6px;
  box-shadow: 0 0 0 200vmax var(--film-dim);
  opacity: 0;
  pointer-events: none;
  --film-move: calc(650ms / var(--film-speed, 1));
  transition: left var(--film-move) cubic-bezier(0.45, 0, 0.2, 1), top var(--film-move) cubic-bezier(0.45, 0, 0.2, 1),
    width var(--film-move) cubic-bezier(0.45, 0, 0.2, 1), height var(--film-move) cubic-bezier(0.45, 0, 0.2, 1), opacity 450ms ease;
}

.film__spot.is-on {
  opacity: 1;
}

.film__ring {
  position: absolute;
  z-index: 6;
  border-radius: 6px;
  box-shadow: 0 0 0 2px var(--film-accent), 0 0 0 6px color-mix(in srgb, var(--film-accent) 22%, transparent);
  opacity: 0;
  pointer-events: none;
  --film-move: calc(650ms / var(--film-speed, 1));
  transition: left var(--film-move) cubic-bezier(0.45, 0, 0.2, 1), top var(--film-move) cubic-bezier(0.45, 0, 0.2, 1),
    width var(--film-move) cubic-bezier(0.45, 0, 0.2, 1), height var(--film-move) cubic-bezier(0.45, 0, 0.2, 1), opacity 450ms ease;
}

.film__ring.is-on {
  opacity: 1;
}

/* The pointer. A plain arrow with a dark rim, readable on both themes' frames. Its position is a
   percentage of the canvas, moved by a transition so the glide is the browser's and not a loop. */
.film__cursor {
  position: absolute;
  left: 0;
  top: 0;
  width: 1.7%;
  aspect-ratio: 20 / 28;
  z-index: 8;
  pointer-events: none;
  transform: translate(0, 0);
  transition-property: left, top, opacity;
  transition-timing-function: cubic-bezier(0.55, 0.05, 0.25, 1);
  transition-duration: 900ms;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.45));
  opacity: 0;
}

.film__cursor.is-on {
  opacity: 1;
}

.film__cursor svg {
  display: block;
  width: 100%;
  height: 100%;
}

.film__cursor.is-pressing svg {
  transform: scale(0.86);
  transform-origin: 10% 10%;
  transition: transform 120ms ease;
}

/* The press: a ring that opens where the cursor landed. */
.film__ripple {
  position: absolute;
  width: 3.4%;
  aspect-ratio: 1;
  margin: -1.7% 0 0 -1.7%;
  border-radius: 50%;
  border: 2px solid var(--film-accent);
  background: color-mix(in srgb, var(--film-accent) 22%, transparent);
  z-index: 7;
  pointer-events: none;
  opacity: 0;
  transform: scale(0.3);
}

.film__ripple.is-firing {
  animation: film-ripple 520ms ease-out;
}

@keyframes film-ripple {
  0% { opacity: 0.95; transform: scale(0.3); }
  100% { opacity: 0; transform: scale(1.6); }
}

/* The highlight a press target carries while the cursor travels to it, so the eye arrives first. */
.film__mark {
  position: absolute;
  z-index: 7;
  border-radius: 6px;
  box-shadow: 0 0 0 2px var(--film-accent), 0 0 0 6px color-mix(in srgb, var(--film-accent) 25%, transparent);
  pointer-events: none;
  opacity: 0;
  transition: opacity 260ms ease;
}

.film__mark.is-on {
  opacity: 1;
}

/* The caption card, beside the region it names. The old tours' card: theme-inverted literals (a
   dark frame gets a light grey card and a light frame a charcoal one), so it reads as an annotation
   floating over the dimmed screen, never as part of it. Sampled values, keep literal. */
.film__tip {
  position: absolute;
  z-index: 9;
  box-sizing: border-box;
  margin: 0;
  padding: 0.8em 1em;
  border: 1px solid rgba(0, 0, 0, 0.14);
  border-radius: 8px;
  background: #c7cad2;
  color: #20232b;
  font: 500 clamp(12px, 1.12cqw, 17px) / 1.45 var(--film-sans);
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.3);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 320ms ease, transform 320ms ease;
  pointer-events: none;
}

.film__tip.is-on {
  opacity: 1;
  transform: translateY(0);
}

/* The heading carries the same weight as the words under it and then some, and is a step larger,
   so it leads the card. */
.film__tip-head {
  margin: 0 0 0.3em;
  font: 700 1em / 1.3 var(--film-sans);
  letter-spacing: 0.01em;
  color: color-mix(in srgb, var(--film-accent) 50%, black 50%);
}

.film__tip-body {
  margin: 0;
  font-size: 0.9em;
}

:root[data-theme="light"] .film__tip {
  background: #454850;
  color: #f0f1f3;
  border-color: rgba(255, 255, 255, 0.12);
}

:root[data-theme="light"] .film__tip-head {
  color: #c3bbfb;
}

.film--no-captions .film__tip {
  display: none;
}

/* The chapter card. A title on black (paper in the light theme), the only moment the film stops to
   say where it is going. Opaque, so the frame behind never competes with the words. */
.film__card {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  background: #000;
  color: #eef1f7;
  text-align: center;
  opacity: 0;
  transition: opacity 420ms ease;
  pointer-events: none;
}

.film__card.is-on {
  opacity: 1;
}

.film__card-kicker {
  margin: 0;
  font: 500 clamp(10px, 0.95cqw, 13px) / 1 var(--film-mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--film-accent);
}

.film__card-title {
  margin: 0;
  font: 600 clamp(22px, 3cqw, 44px) / 1.1 var(--film-sans);
}

.film__card-sub {
  margin: 0;
  max-width: 50ch;
  font: 500 clamp(13px, 1.25cqw, 18px) / 1.5 var(--film-sans);
  color: #d4d8e6;
}

/* The light theme's card is the page's own paper, so a chapter opens the way the site around it
   reads. */
:root[data-theme="light"] .film__card {
  background: #f6f7f9;
  color: #1c1f26;
}

:root[data-theme="light"] .film__card-kicker {
  color: color-mix(in srgb, var(--film-accent) 70%, black 30%);
}

:root[data-theme="light"] .film__card-sub {
  color: #454a57;
}

/* The transport. */
.film__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-top: 1px solid var(--film-rule);
  background: var(--film-panel);
  font: 400 12px / 1 var(--film-mono);
  color: var(--film-muted);
}

.film__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--film-line);
  border-radius: 6px;
  background: var(--film-btn);
  color: var(--film-text);
  cursor: pointer;
}

.film__btn:hover {
  background: var(--film-btn-hover);
  border-color: var(--film-line-strong);
}

.film__btn:focus-visible,
.film__track:focus-visible {
  outline: 2px solid var(--film-accent);
  outline-offset: 2px;
}

.film__btn svg {
  width: 13px;
  height: 13px;
}

.film__play .film__i-play { display: none; }
.film--paused .film__play .film__i-play { display: block; }
.film--paused .film__play .film__i-pause { display: none; }

/* The speed menu: a button labelled with the speed, as wide as "1.25x" needs, and the choices
   above it. They open upward from the button on a hover (or a press, for a touch screen or the
   keyboard), revealed from the bottom edge. The padding under the list bridges the gap to the
   button, so the pointer crosses it without the menu closing. */
.film__speedbox {
  position: relative;
  flex: none;
  display: inline-flex;
}

.film__speed {
  width: auto;
  min-width: 46px;
  padding: 0 6px;
  font: 500 12px / 1 var(--film-mono);
  font-variant-numeric: tabular-nums;
}

.film__speeds {
  position: absolute;
  left: 50%;
  bottom: 100%;
  z-index: 20;
  padding-bottom: 6px;
  transform: translateX(-50%);
  visibility: hidden;
  opacity: 0;
  clip-path: inset(100% 0 0 0);
  transition: clip-path 180ms ease-out, opacity 180ms ease-out, visibility 0s linear 180ms;
}

.film__speedbox:hover .film__speeds,
.film__speedbox.is-open .film__speeds {
  visibility: visible;
  opacity: 1;
  clip-path: inset(0);
  transition: clip-path 180ms ease-out, opacity 180ms ease-out, visibility 0s;
}

.film__speeds-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px;
  border: 1px solid var(--film-line-strong);
  border-radius: 8px;
  background: var(--film-panel);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.3);
}

.film__speed-opt {
  display: block;
  min-width: 52px;
  padding: 7px 10px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--film-text);
  font: 500 12px / 1 var(--film-mono);
  font-variant-numeric: tabular-nums;
  text-align: center;
  cursor: pointer;
}

.film__speed-opt:hover {
  background: var(--film-btn-hover);
}

.film__speed-opt[aria-checked="true"] {
  color: var(--film-accent);
  font-weight: 700;
}

.film__speed-opt:focus-visible {
  outline: 2px solid var(--film-accent);
  outline-offset: -2px;
}

.film__cc[aria-pressed="false"] {
  opacity: 0.55;
}

/* The narration button, made by js/film.js where the film has a voice: a speaker, struck through
   when it is off. While the browser holds the sound back until a first press, it shows off and
   pulses, asking for that press. */
.film__voice .film__i-voice-off,
.film__voice[aria-pressed="false"] .film__i-voice-on,
.film__voice.is-blocked .film__i-voice-on {
  display: none;
}

.film__voice[aria-pressed="false"] .film__i-voice-off,
.film__voice.is-blocked .film__i-voice-off {
  display: block;
}

.film__voice[aria-pressed="false"] {
  opacity: 0.55;
}

.film__voice.is-blocked {
  border-color: var(--film-accent);
  animation: film-voice-ask 1.8s ease-in-out infinite;
}

@keyframes film-voice-ask {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--film-accent) 0%, transparent); }
  50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--film-accent) 40%, transparent); }
}

.film__track {
  position: relative;
  flex: 1;
  height: 22px;
  cursor: pointer;
}

.film__rail {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 4px;
  margin-top: -2px;
  border-radius: 2px;
  background: var(--film-rule);
  overflow: hidden;
}

.film__fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  background: var(--film-accent);
}

/* A chapter boundary on the rail. */
.film__tick {
  position: absolute;
  top: 50%;
  width: 2px;
  height: 12px;
  margin: -6px 0 0 -1px;
  border-radius: 1px;
  background: var(--film-line-strong);
}

.film__time {
  flex: none;
  min-width: 9ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.film__where {
  flex: none;
  min-width: 13ch;
  color: var(--film-text);
}

/* The chapter tabs in the window chrome reuse .demo-tab; a chapter the film is in reads active. */

/* Full screen. The film's own width token, read by the chrome bar through --stage-w (css/demo.css)
   and by the film body below, so the two bracket one block exactly as the tour stages did. The
   height budget is the chrome (104px, as demo.css counts it) plus the transport bar under the
   stage; the ratio is the capture canvas's, which js/film.js states as --film-ratio. Capped at the
   canvas's own width (--film-canvas, also from js/film.js), so where the screen has room the frame
   is drawn one canvas pixel to one CSS pixel and js/film.js can draw the 1x frames pixel for pixel.
   Smaller screens scale it down. */
.window--film.is-modal {
  --film-w: min(var(--film-canvas, 1500px), 100vw, calc((100vh - 158px) * var(--film-ratio, 2.259)));
  --stage-base: var(--film-w);
  --stage-main: var(--film-w);
  --stage-w: var(--film-w);
}

.window--film.is-modal > .film {
  margin-block: 0 auto;
}

.window--film.is-modal .film__body {
  width: calc(var(--film-w) + 2px);
  max-width: 100%;
  margin-inline: auto;
  border: 1px solid var(--film-line);
  border-top: 0;
  border-radius: 0 0 8px 8px;
  overflow: hidden;
}

/* Reduced motion: no glide, no fade, no camera, no scroll. The film still steps, by hand. */
@media (prefers-reduced-motion: reduce) {
  .film__camera,
  .film__frame,
  .film__cursor,
  .film__tip,
  .film__card,
  .film__mark,
  .film__spot,
  .film__ring,
  .film__crop,
  .film__speeds {
    transition: none !important;
  }

  .film__ripple.is-firing,
  .film__voice.is-blocked {
    animation: none;
  }
}
