/* The stage: four full-screen scenes that change under a fixed frame. js/stage.js moves one
   scene per scroll, swipe or arrow key. Phones stack each scene, text over the visual; from
   960px the text sits beside the visual. In every scene the visual gets the most room.

   Without scripting, or on screens under 500px tall, the scenes are plain sections one after
   another (html without .stage-on), and the page scrolls. */

:root {
  --frame-top: 48px;
  --frame-bottom: 48px;
  --scene-gap: clamp(12px, 2.4vh, 24px);
  --text-col: 100%;
  --col-gap: 0px;
}
@media (min-width: 960px) {
  :root {
    --frame-top: 68px;
    --frame-bottom: 64px;
    --text-col: clamp(348px, 34vw, 480px);
    --col-gap: clamp(40px, 5vw, 88px);
  }
}

html.stage-on,
html.stage-on body {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}

/* ---------- The frame ---------- */

.frame-top {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  height: var(--frame-top);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-inline: var(--gutter);
  pointer-events: none;
}
.frame-top > * {
  pointer-events: auto;
}
.frame-title {
  margin: 0;
  min-width: 0;
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.frame-title a {
  display: grid;
  align-items: center;
  min-height: 44px;
  color: var(--ink);
  text-decoration: none;
}
.frame-title a {
  white-space: nowrap;
  transition:
    opacity var(--fade) var(--ease),
    visibility 0s linear 0s;
}
/* The opening scene carries the title itself, large, so the frame's copy waits for scene 2. */
html.stage-on[data-scene="0"] .frame-title a {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--fade) var(--ease),
    visibility 0s linear var(--fade);
}
.page-rating {
  display: flex;
  flex: none;
  align-items: center;
  gap: 10px;
  margin: 0;
}
.page-rating .stars {
  width: 76px;
  height: 14px;
}
.page-rating-num {
  min-width: 2.2em;
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.page-rating-label {
  display: none;
}
@media (min-width: 600px) {
  .page-rating-label {
    display: inline;
  }
}

.frame-bottom {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 40;
  height: var(--frame-bottom);
  display: flex;
  align-items: center;
  gap: 10px;
  padding-inline: var(--gutter);
  pointer-events: none;
}
.frame-bottom > * {
  pointer-events: auto;
}
.frame-count {
  margin: 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.frame-now {
  color: var(--ink);
}
.frame-dots {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
}
.frame-dots button {
  display: grid;
  place-items: center;
  width: 26px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  touch-action: manipulation;
}
.frame-dots button::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink-rule-strong);
  transition:
    background-color var(--press) var(--ease),
    transform var(--move) var(--ease);
}
.frame-dots button[aria-current="step"]::before {
  background: var(--ink);
  transform: scale(1.35);
}
@media (hover: hover) and (pointer: fine) {
  .frame-dots button:not([aria-current="step"]):hover::before {
    background: var(--ink-label);
  }
}
/* The cue: a small pulse on the first scene only. */
.frame-cue {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  margin-left: auto;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  touch-action: manipulation;
  transition:
    opacity var(--fade) var(--ease),
    visibility 0s linear 0s;
  animation: cue-pulse 1.8s var(--ease) infinite;
}
.frame-cue svg {
  width: 12px;
  height: 8px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  animation: cue-nudge 1.8s var(--ease) infinite;
}
@keyframes cue-pulse {
  0%,
  56%,
  100% {
    opacity: 0.55;
  }
  28% {
    opacity: 1;
  }
}
@keyframes cue-nudge {
  0%,
  56%,
  100% {
    transform: translateY(0);
  }
  28% {
    transform: translateY(4px);
  }
}
html:not([data-scene="0"]) .frame-cue,
html:not(.stage-on) .frame-cue {
  opacity: 0;
  visibility: hidden;
  animation: none;
  transition:
    opacity var(--fade) var(--ease),
    visibility 0s linear var(--fade);
}
@media (min-width: 960px) {
  .frame-cue {
    position: absolute;
    left: 50%;
    margin-left: 0;
    translate: -50% 0;
  }
}

/* ---------- The stage ---------- */

html.stage-on .stage {
  position: fixed;
  inset: 0;
  /* Clipped, not just hidden: a hidden box can still be scrolled by a link to a scene or by a
     control brought into view, which would shift the whole scene. */
  overflow: hidden;
  overflow: clip;
  /* Swipes belong to the stage; pinch zoom still works. */
  touch-action: pinch-zoom;
}
html.stage-on .scene {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--move) var(--ease),
    transform var(--move) var(--ease),
    visibility 0s linear var(--move);
}
html.stage-on .scene.is-current {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}
/* The scene on its way out drifts the way the reader is going: up when going forward. */
html.stage-on .scene.is-leaving {
  transform: translateY(calc(var(--dir, 1) * -28px));
}

/* One scene. Phones and narrow tablets: text over the visual, the visual taking what is left. */
.scene {
  display: flex;
  flex-direction: column;
  gap: var(--scene-gap);
  padding: calc(var(--frame-top) + 2px) var(--gutter) calc(var(--frame-bottom) + 12px);
}
.scene-text {
  flex: none;
  display: grid;
  align-content: start;
  gap: 10px;
  min-width: 0;
}
.scene-visual {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
  container-type: size;
}
@media (min-width: 960px) {
  .scene {
    display: grid;
    grid-template-columns: var(--text-col) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
    column-gap: var(--col-gap);
    align-items: center;
    padding: var(--frame-top) max(var(--gutter), (100vw - 1640px) / 2) var(--frame-bottom);
  }
  .scene-text {
    gap: 18px;
  }
  .scene-visual {
    height: 100%;
  }
}

/* The serif role, large: headlines. */
.scene-title {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(1.75rem, 1.1rem + 2.6vw, 3.375rem);
  font-weight: 500;
  line-height: 1.04;
  letter-spacing: -0.022em;
  font-variation-settings: "opsz" 60;
  text-wrap: balance;
  color: var(--ink);
}
.scene-title:focus {
  outline: none;
}
/* The opening's title, the page's h1: larger than the other scenes' headlines. */
.scene-title-big {
  font-size: clamp(2.5rem, 1.3rem + 4.6vw, 5.25rem);
  line-height: 0.98;
  letter-spacing: -0.028em;
  font-variation-settings: "opsz" 72;
}
/* Body text at body strength: one line, or a short paragraph, per scene. */
.scene-body {
  max-width: 32rem;
  font-size: clamp(1.0625rem, 0.95rem + 0.4vw, 1.25rem);
  line-height: 1.42;
  color: var(--ink-body);
}
.byline {
  margin-top: 4px;
}
.scene-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 12px;
}
.drift-key.scene-links {
  display: block;
}
.why-button {
  display: inline-block;
  padding-block: 12px;
  margin-block: -12px;
}

/* ---------- The opening and the close: the page's own rating, large ---------- */

/* The serif role, large: a big number, with its stars under it. The opening shows the 4.8 in
   the title; the close shows the 5.0 this page has drifted to. */
.big-rating {
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100%;
  font-size: clamp(3rem, min(58cqw, 46cqh), 19rem);
}
.big-rating-num {
  margin: 0;
  font-family: var(--serif);
  font-size: 1em;
  font-weight: 400;
  line-height: 0.8;
  letter-spacing: -0.045em;
  font-variant-numeric: lining-nums;
  font-variation-settings: "opsz" 72;
  color: var(--ink);
}
.big-rating-stars {
  width: min(1.9em, 100%);
  height: auto;
  margin-top: 0.14em;
}
.big-rating-cap {
  max-width: 22rem;
  margin: 0.2rem 0 0;
  font-size: 0.75rem;
}
/* The opening's 4.8 takes most of its scene's height. */
.scene-open .big-rating {
  font-size: clamp(3rem, min(64cqw, 62cqh), 19rem);
}
/* The close's 5.0 leaves room for its caption, and on the shortest screens gives way to it,
   first the stars, then the number: the frame's rating already shows the 5.0. */
.scene-close .big-rating {
  font-size: clamp(2.5rem, min(58cqw, calc((100cqh - 60px) * 0.74)), 19rem);
}
@container (max-height: 150px) {
  .scene-close .big-rating-stars {
    display: none;
  }
}
@container (max-height: 104px) {
  .scene-close .big-rating-num {
    display: none;
  }
}
.big-rating-stars + .big-rating-cap {
  margin-top: max(14px, 0.07em);
}

/* ---------- "Why?" ---------- */

/* On the stage the card is frosted glass over the scene's text, which dims behind it; the
   visual beside or under it stays as it was. Without the stage it opens in the page. */
.why-card {
  margin: 16px 0 0;
  padding: 0 0 18px;
  border: 1px solid var(--ink-rule-strong);
}
.why-card[hidden] {
  display: none;
}
.why-card:focus {
  outline: none;
}
.why-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 48px;
  padding: 0 16px;
  border-bottom: 1px solid var(--ink-rule);
}
.why-card-title {
  margin: 0;
  color: var(--ink);
}
.why-card-head .text-button {
  padding-block: 12px;
}
.why-card > p:not(.why-card-src) {
  margin: 14px 16px 0;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ink-body);
}
.why-card-src {
  margin: 12px 16px 0;
}
.why-card-src a {
  display: inline-block;
  padding-block: 10px;
  margin-block: -10px;
}
html.stage-on .why-card {
  position: fixed;
  z-index: 34;
  top: var(--frame-top);
  left: 8px;
  right: 8px;
  max-height: calc(100% - var(--frame-top) - var(--frame-bottom));
  margin: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: rgb(243 238 227 / 0.74);
  -webkit-backdrop-filter: blur(10px) saturate(1.05);
  backdrop-filter: blur(10px) saturate(1.05);
  box-shadow: 0 18px 48px rgb(var(--ink-rgb) / 0.1);
  opacity: 0;
  transform: translateY(-12px);
  transition:
    opacity var(--move) var(--ease),
    transform var(--move) var(--ease);
}
html.stage-on .why-card.is-open {
  opacity: 1;
  transform: none;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  html.stage-on .why-card {
    background: rgb(243 238 227 / 0.96);
  }
}
@media (min-width: 960px) {
  html.stage-on .why-card {
    top: 50%;
    left: calc(max(var(--gutter), (100vw - 1640px) / 2) - 24px);
    right: auto;
    width: calc(var(--text-col) + var(--col-gap) / 2 + 24px);
    max-height: calc(100% - var(--frame-top) - var(--frame-bottom) - 16px);
    translate: 0 -50%;
    transform: translateX(-14px);
  }
  html.stage-on .why-card.is-open {
    transform: none;
  }
}
.scene .scene-text {
  transition: opacity var(--fade) var(--ease);
}
.scene.is-why .scene-text {
  opacity: 0.28;
  pointer-events: none;
}
html:not(.js) .why-button,
html:not(.js) .why-button + span,
html:not(.js) [data-why-close] {
  display: none;
}
.byline a,
.beat-links a,
.beat-links .text-button,
.drift-key a {
  display: inline-block;
  padding-block: 12px;
  margin-block: -12px;
}

/* ---------- Arrivals ---------- */

/* Headlines arrive a word at a time; the rest of a scene rises in after them. stage.js splits
   each headline into words, gives each its place in --i, and gives every other piece its own
   delay. A unit (a scene, or one of the lab's two beats) is .is-staged while it waits and
   .is-in once it has arrived. */
.w {
  display: inline-block;
}
html.stage-on:not(.stage-ready) [data-words],
html.stage-on:not(.stage-ready) [data-rise] {
  opacity: 0;
  /* If the script never runs, show everything after a moment instead of hiding it. */
  animation: show-late 0s linear 2.5s forwards;
}
@keyframes show-late {
  to {
    opacity: 1;
  }
}
[data-rise],
.w {
  transition: none;
}
.is-staged [data-rise],
.is-staged .w {
  opacity: 0;
  transform: translateY(calc(var(--dir, 1) * 18px));
}
.is-staged .w {
  transform: translateY(calc(var(--dir, 1) * 0.32em));
}
.is-in [data-rise],
.is-in .w {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--fade) var(--ease) var(--delay, 0ms),
    transform var(--move) var(--ease) var(--delay, 0ms);
}
.is-in .w {
  --delay: calc(40ms + var(--i, 0) * 36ms);
}
/* A scene reached while the tab was hidden is in place when the reader comes back. */
html.is-sudden .scene,
html.is-sudden .beat,
html.is-sudden [data-rise],
html.is-sudden .w {
  transition: none !important;
}
/* A beat's own state wins over its scene's, so the lab's text can change beat by beat while its
   chart stays put. */
.beat.is-staged [data-rise],
.beat.is-staged .w {
  opacity: 0;
  transition: none;
}
.beat.is-in [data-rise],
.beat.is-in .w {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--fade) var(--ease) var(--delay, 0ms),
    transform var(--move) var(--ease) var(--delay, 0ms);
}

/* ---------- The lab's two beats ---------- */

.scene-lab .scene-text {
  display: grid;
}
/* Beside the chart, the lab's text starts at the top of its column, so scene 5's headline stays
   put when its rules chart changes height, and a column taller than the screen keeps its
   headline in view. */
@media (min-width: 960px) {
  html.stage-on .scene-lab .scene-text {
    align-self: start;
  }
}
.beat {
  grid-area: 1 / 1;
  display: grid;
  align-content: start;
  gap: 10px;
  min-width: 0;
}
@media (min-width: 960px) {
  .beat {
    gap: 18px;
  }
}
html.stage-on .beat {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--move) var(--ease),
    visibility 0s linear var(--move);
}
html.stage-on .beat.is-current {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

/* The big switch, inside scene 5's headline: the track and a star that turns gold. */
.scene-switch {
  font-size: clamp(1.625rem, 1rem + 2.5vw, 3.125rem);
}
.big-switch {
  appearance: none;
  display: flex;
  align-items: flex-start;
  gap: 0.42em;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  line-height: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
}
.big-switch-track {
  position: relative;
  flex: none;
  width: 1.84em;
  height: 1em;
  margin-top: 0.04em;
  border: 2px solid var(--ink);
  border-radius: 999px;
  transition:
    background-color var(--move) var(--ease),
    transform var(--press) var(--ease);
}
.big-switch-thumb {
  position: absolute;
  top: 50%;
  left: 0.08em;
  width: 0.76em;
  height: 0.76em;
  margin-top: -0.38em;
  transition: transform var(--move) var(--ease);
}
.big-switch-thumb svg {
  width: 100%;
  height: 100%;
  fill: var(--paper);
  stroke: var(--ink);
  stroke-width: 1.6;
  stroke-linejoin: round;
  transition:
    fill var(--move) var(--ease),
    stroke var(--move) var(--ease);
}
.big-switch[aria-checked="true"] .big-switch-track {
  background: var(--ink);
}
.big-switch[aria-checked="true"] .big-switch-thumb {
  transform: translateX(0.84em);
}
.big-switch[aria-checked="true"] .big-switch-thumb svg {
  fill: var(--gold);
  stroke: var(--gold);
}
.big-switch:active .big-switch-track {
  transform: scale(0.96);
}
@media (hover: hover) and (pointer: fine) {
  .big-switch[aria-checked="false"]:hover .big-switch-track {
    background: var(--ink-wash);
  }
}
.big-switch:focus-visible {
  outline-offset: 6px;
}
.beat-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 12px;
}

/* ---------- Without the stage ---------- */

html:not(.stage-on) .frame-top {
  position: sticky;
  background: var(--paper);
  border-bottom: 1px solid var(--ink-rule);
}
html:not(.stage-on) .frame-bottom {
  display: none;
}
html:not(.stage-on) .scene {
  min-height: 100svh;
  padding: 40px var(--gutter) 56px;
  border-bottom: 1px solid var(--ink-rule);
}
html:not(.stage-on) .scene-visual {
  flex: none;
  min-height: 70svh;
  container-type: inline-size;
}
html:not(.stage-on) .scene-lab .scene-text {
  gap: 40px;
}
html:not(.stage-on) .beat {
  grid-area: auto;
}
@media (min-width: 960px) {
  html:not(.stage-on) .scene {
    grid-template-rows: auto;
  }
  html:not(.stage-on) .scene-visual {
    height: 70svh;
  }
}

/* ---------- Reduced motion ---------- */

/* Scenes swap with plain fades: nothing slides or rises, and headlines arrive whole. */
@media (prefers-reduced-motion: reduce) {
  html.stage-on .scene.is-leaving {
    transform: none;
  }
  .is-staged [data-rise],
  .is-staged .w,
  .beat.is-staged [data-rise],
  .beat.is-staged .w {
    transform: none;
  }
  .is-in .w,
  .beat.is-in .w {
    --delay: 0ms;
  }
  .frame-cue,
  .frame-cue svg {
    animation: none;
  }
  html.stage-on .why-card,
  html.stage-on .why-card.is-open {
    transform: none;
  }
  .big-switch-thumb,
  .big-switch:active .big-switch-track {
    transition: none;
    transform: none;
  }
  .big-switch[aria-checked="true"] .big-switch-thumb {
    transform: translateX(0.84em);
  }
}

@media (min-width: 960px) and (max-height: 900px) {
  .scene-text {
    gap: 12px;
  }
  .scene-title {
    font-size: clamp(1.6rem, 5.4vh, 3.375rem);
  }
  .scene-title-big {
    font-size: clamp(2.2rem, 8.4vh, 5.25rem);
  }
  .scene-body {
    font-size: clamp(0.95rem, 2.1vh, 1.25rem);
    line-height: 1.38;
  }
}
