/* ---------- Story beats (story-card.ts, beats.ts, wake.ts) ----------
   Owner direction (2026-09-28): the end of a place should feel interactive,
   with visuals, "less block of text trying to tell me everything at once".
   The place's scene fills the card; a small dialogue box plays the beat one
   step at a time. The card keeps one size while the lines play, on every
   screen from 320 × 568 up; only the closing step may grow the box.
   Motion is calm: slow eases, small amplitudes, nothing that loops loudly.
   Reduced motion: no typewriter (story-card.ts) and no animation here; every
   state below has a still end state that the animations arrive at. */

#story-card {
  --box-h: clamp(178px, 26svh, 210px);
  --card-w: min(460px, calc(100vw - 16px));
  --gold: #f2d88d;
  --ink: #0b1a16;
  width: var(--card-w);
  /* The picture a little taller than wide, and the box under it. */
  height: min(calc(100svh - 16px), calc(var(--card-w) * 1.18 + var(--box-h)));
  max-width: none;
  max-height: none;
  padding: 0;
  overflow: hidden;
  text-align: left;
}
#story-card[open] {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
}

/* ---------- The picture ---------- */
#story-card .story-art {
  position: relative;
  height: auto;
  min-height: 0;
  margin: 0;
  overflow: hidden;
  background: var(--ink);
  border-bottom: 1px solid #60735a55;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.story-art .beat-scene {
  position: absolute;
  inset: 0;
}
.story-art .beat-scene svg {
  overflow: hidden;
}
/* Quiet lines dim the scene a little, as a room goes quiet. */
.story-art::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #07131f;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.4s ease;
}
.story-art:has(.beat-scene[data-cue="quiet"])::after {
  opacity: 0.26;
}

.story-heading {
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
  right: 0;
  padding: 14px 18px 30px;
  background: linear-gradient(#07120ed0, #07120e00);
  pointer-events: none;
  text-shadow: 0 1px 8px #000a;
}
#story-card .story-heading .eyebrow {
  margin: 0;
  font-size: 11px;
  letter-spacing: 1.5px;
  color: #ced8b4;
}
#story-card #story-title {
  margin: 4px 0 0;
  font:
    normal 24px/1.15 Georgia,
    serif;
  letter-spacing: -0.3px;
  color: #f0eacc;
}

/* The hotspot: a slow glow around the thing to tap, with its words. */
.story-hotspot {
  position: absolute;
  z-index: 2;
  left: var(--x, 50%);
  top: var(--y, 50%);
  width: var(--size, 72px);
  height: var(--size, 72px);
  translate: -50% -50%;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.story-hotspot[hidden] {
  display: none;
}
.story-hotspot .ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1.5px solid #f6dc8ab0;
  background: radial-gradient(circle, #f6dc8a26 0 45%, #f6dc8a00 70%);
  box-shadow:
    0 0 22px 6px #f6dc8a40,
    inset 0 0 16px #f6dc8a3a;
  animation: hotspot-breathe 2.6s ease-in-out infinite;
}
@keyframes hotspot-breathe {
  50% {
    transform: scale(1.08);
    opacity: 0.62;
  }
}
.story-hotspot:hover .ring {
  border-color: #f6dc8a;
}

/* ---------- The dialogue box ---------- */
.story-box {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: var(--box-h);
  padding: 8px 18px 14px;
  outline: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.story-box:focus-visible {
  box-shadow: inset 0 0 0 2px #f2d88d9c;
  border-radius: 0 0 23px 23px;
}
.story-box[data-step="close"] {
  cursor: auto;
  padding-top: 14px;
}
.story-box-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 32px;
}
.story-box[data-step="close"] .story-box-top {
  display: none;
}
.story-dots {
  display: flex;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.story-dots li {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #60735a88;
  transition: background 0.3s ease;
}
.story-dots li.done {
  background: #b9a86c;
}
.story-dots li.now {
  background: var(--gold);
  box-shadow: 0 0 6px #f2d88d99;
}
#story-card .story-skip {
  min-height: 32px;
  padding: 4px 14px;
  border: 1px solid #60735a99;
  border-radius: 16px;
  background: transparent;
  color: #c5d3bf;
  font: 13px system-ui;
  cursor: pointer;
}
.story-line {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}
.story-line[hidden],
.story-close[hidden] {
  display: none;
}
.story-who {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 2px 8px;
  min-height: 22px;
  margin: 4px 0 4px;
}
.story-speaker {
  padding: 2px 9px 1px;
  border-radius: 10px;
  font: 600 11.5px/1.5 system-ui;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--who, #f2d88d);
  background: color-mix(in srgb, var(--who, #f2d88d) 16%, transparent);
}
.story-speaker[hidden],
.story-note[hidden] {
  display: none;
}
.story-speaker[data-speaker="hazel"] {
  --who: #f3c877;
}
.story-speaker[data-speaker="tam"] {
  --who: #f5b3a8;
}
.story-speaker[data-speaker="parsnip"] {
  --who: #eb9a5f;
}
.story-speaker[data-speaker="rowan"],
.story-speaker[data-speaker="wren"] {
  --who: #ffe6a6;
}
.story-note {
  font:
    italic 13.5px/1.3 Georgia,
    serif;
  color: #b8c9b4;
}
#story-card #story-body {
  flex: 1;
  margin: 0;
  font:
    17px/1.45 Georgia,
    serif;
  color: #f0ead6;
  text-wrap: pretty;
}
#story-body.narration {
  color: #e2e6d4;
}
#story-body.prompt {
  font-style: italic;
  color: var(--gold);
}
#story-body .unrevealed {
  color: transparent;
}
.story-hint {
  margin: 6px 0 0;
  text-align: right;
  font: 12.5px system-ui;
  color: #a9bda6;
  opacity: 0;
  transition: opacity 0.4s ease;
}
.story-hint::after {
  content: " ›";
}
#story-body.whole + .story-hint {
  opacity: 1;
}
.story-box[data-step="action"] .story-hint {
  opacity: 0;
}
.story-hint .keys {
  display: none;
}
@media (hover: hover) and (pointer: fine) {
  .story-hint .touch {
    display: none;
  }
  .story-hint .keys {
    display: inline;
  }
}

/* The closing step: how far the valley has come, what's next, the way on. */
.story-close {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
#story-card .story-progress {
  margin: 0;
  text-align: center;
}
#story-card .story-progress[hidden] {
  display: none;
}
#story-card .story-progress .valley-strip {
  justify-content: center;
}
#story-card .story-progress p {
  text-align: center;
}
#story-card .story-next {
  margin: 0;
  padding: 9px 12px;
  border-radius: 12px;
  background: #0f231d99;
  border: 1px solid #60735a44;
  font-size: 13px;
  line-height: 1.4;
  color: #cfdac6;
}
#story-card .story-next[hidden] {
  display: none;
}
#story-card .story-next strong {
  display: inline;
  margin: 0;
  font:
    14.5px Georgia,
    serif;
  color: #f0e3a8;
}
#story-card .story-next em {
  display: inline;
  margin: 0;
  font-style: normal;
  font-size: 12px;
  color: #b5c9b6;
}
#story-card .story-next em::before {
  content: "· ";
}
#story-card .story-actions {
  position: static;
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 0;
  background: none;
}
#story-card[data-mood] .story-actions {
  background: none;
}
#story-card button:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

/* ---------- Sizes ---------- */
/* Short phones: a little less box, a little smaller type. */
@media (max-height: 640px) and (max-width: 559px) {
  #story-card {
    --box-h: 176px;
  }
  #story-card #story-body {
    font-size: 16px;
    line-height: 1.4;
  }
  #story-card #story-title {
    font-size: 21px;
  }
  .story-heading {
    padding: 10px 14px 24px;
  }
  .story-box {
    padding: 6px 14px 10px;
  }
  .story-close {
    gap: 8px;
  }
  #story-card .story-actions button {
    min-height: 42px;
  }
}
/* Landscape phones and short windows: the picture beside the box. */
@media (min-width: 560px) and (max-height: 560px) {
  #story-card {
    --card-w: min(860px, calc(100vw - 16px));
    height: calc(100svh - 16px);
  }
  #story-card[open] {
    grid-template-rows: minmax(0, 1fr);
    grid-template-columns: 56% minmax(0, 1fr);
  }
  #story-card .story-art {
    border-bottom: 0;
    border-right: 1px solid #60735a55;
  }
  .story-heading {
    right: 44%;
  }
  .story-box {
    min-height: 0;
    overflow: auto;
    padding: 10px 18px 12px;
  }
  .story-line {
    justify-content: center;
  }
  .story-hint {
    margin-top: auto;
  }
}
/* Roomy screens: a framed picture-book page. */
@media (min-width: 700px) and (min-height: 700px) {
  #story-card {
    --box-h: 214px;
    --card-w: min(600px, calc(100vw - 48px));
    height: min(760px, calc(100svh - 48px));
  }
  #story-card #story-title {
    font-size: 27px;
  }
  .story-box {
    padding: 10px 24px 18px;
  }
  .story-heading {
    padding: 18px 24px 34px;
  }
}

/* ---------- The scene: a landmark asleep, waking, awake ---------- */
.beat-scene .w-target {
  fill: none;
  stroke: none;
  pointer-events: none;
}
.w-stop.a {
  stop-color: #fff6d0;
  stop-opacity: 0.95;
}
.w-stop.b {
  stop-color: var(--glow);
  stop-opacity: 0.45;
}
.w-stop.c {
  stop-color: var(--glow);
  stop-opacity: 0;
}
.beat-scene .st-landmark .p-glow {
  --to: 1;
}
.beat-scene .st-landmark .p-glow.soft {
  --to: 0.32;
}
.beat-scene .st-landmark .p-glow:is(.ray, .comet, .sheet) {
  --to: 0.6;
}
.beat-scene .st-landmark .p-bloom {
  transform-box: fill-box;
  transform-origin: 50% 70%;
}
/* The warm light behind the landmark. */
.beat-scene .w-bloom {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 50%;
  mix-blend-mode: screen;
}
.beat-scene[data-beat="awake"] .w-bloom {
  opacity: 0.34;
}
.beat-scene[data-quiet][data-beat="awake"] .w-bloom {
  opacity: 0.2;
}
.beat-scene[data-beat="waking"] .w-bloom {
  animation: wake-bloom 2.4s cubic-bezier(0.2, 0.6, 0.3, 1) both;
}
.beat-scene[data-quiet][data-beat="waking"] .w-bloom {
  animation-name: wake-bloom-quiet;
  animation-duration: 2.6s;
}
@keyframes wake-bloom {
  0% {
    opacity: 0;
    transform: scale(0.25);
  }
  35% {
    opacity: 0.85;
  }
  100% {
    opacity: 0.34;
    transform: none;
  }
}
@keyframes wake-bloom-quiet {
  0% {
    opacity: 0;
    transform: scale(0.4);
  }
  45% {
    opacity: 0.45;
  }
  100% {
    opacity: 0.2;
    transform: none;
  }
}
/* Asleep: the landmark's lights are out and its flowers closed. */
.beat-scene[data-beat="asleep"] .st-landmark .p-glow {
  opacity: 0.1;
}
.beat-scene[data-beat="asleep"] .st-landmark .p-bloom {
  opacity: 0.3;
  transform: scale(0.6);
}
/* Waking: lights flicker on (in turn where they have an order), flowers open. */
.beat-scene[data-beat="waking"] .st-landmark .p-glow {
  animation: wake-flicker 1.4s ease-out both;
  animation-delay: calc(0.3s + var(--i, 0) * 0.18s);
}
@keyframes wake-flicker {
  0% {
    opacity: 0.1;
  }
  22% {
    opacity: 0.85;
  }
  34% {
    opacity: 0.3;
  }
  52% {
    opacity: 1;
  }
  64% {
    opacity: 0.65;
  }
  100% {
    opacity: var(--to);
  }
}
.beat-scene[data-beat="waking"] .st-landmark .p-bloom {
  animation: wake-pop 0.9s cubic-bezier(0.3, 1.35, 0.5, 1) both;
  animation-delay: 0.35s;
}
.beat-scene[data-beat="waking"] .st-landmark .p-bloom:nth-of-type(3n + 1) {
  animation-delay: 0.6s;
}
.beat-scene[data-beat="waking"] .st-landmark .p-bloom:nth-of-type(3n + 2) {
  animation-delay: 0.85s;
}
@keyframes wake-pop {
  from {
    opacity: 0.3;
    transform: scale(0.6);
  }
}

/* Strands, fronds and reeds: light runs down them (the willow, the wisteria). */
.beat-scene[data-beat="asleep"]
  .st-landmark
  :is(.p-strand, .p-wisteria, .p-frond) {
  opacity: 0.4;
}
.beat-scene[data-beat="waking"]
  .st-landmark
  :is(.p-strand, .p-wisteria, .p-frond) {
  animation: strand-wake 1.6s ease-out both;
  animation-delay: 0.3s;
}
.beat-scene[data-beat="waking"]
  .st-landmark
  :is(.p-strand, .p-wisteria, .p-frond):nth-of-type(2n) {
  animation-delay: 0.65s;
}
.beat-scene[data-beat="waking"]
  .st-landmark
  :is(.p-strand, .p-wisteria, .p-frond):nth-of-type(3n) {
  animation-delay: 1s;
}
@keyframes strand-wake {
  from {
    opacity: 0.4;
    transform: translateY(-2px);
  }
}

/* The gate: two closed leaves swing open with a sigh; the lamp flickers on. */
.beat-scene .st-landmark .w-door {
  display: none;
}
.beat-scene .w-leaf {
  fill: var(--wood);
  transform-box: fill-box;
}
.beat-scene .w-leaf.l {
  transform-origin: 0% 100%;
}
.beat-scene .w-leaf.r {
  transform-origin: 100% 100%;
}
.beat-scene .w-plank {
  fill: none;
  stroke: var(--sil);
  stroke-width: 0.9;
  opacity: 0.45;
}
.beat-scene[data-beat="awake"] .w-leaf.l {
  transform: scaleX(0.3) skewY(-9deg);
  opacity: 0.82;
}
.beat-scene[data-beat="awake"] .w-leaf.r {
  transform: scaleX(0.3) skewY(9deg);
  opacity: 0.82;
}
.beat-scene[data-beat="waking"] .w-leaf.l {
  animation: gate-swing-l 2s cubic-bezier(0.45, 0, 0.2, 1) 0.1s both;
}
.beat-scene[data-beat="waking"] .w-leaf.r {
  animation: gate-swing-r 2s cubic-bezier(0.45, 0, 0.2, 1) 0.25s both;
}
@keyframes gate-swing-l {
  0% {
    transform: none;
  }
  12% {
    transform: scaleX(0.97) skewY(-1deg);
  }
  100% {
    transform: scaleX(0.3) skewY(-9deg);
    opacity: 0.82;
  }
}
@keyframes gate-swing-r {
  0% {
    transform: none;
  }
  12% {
    transform: scaleX(0.97) skewY(1deg);
  }
  100% {
    transform: scaleX(0.3) skewY(9deg);
    opacity: 0.82;
  }
}
.beat-scene[data-wake="gate"][data-beat="waking"] .st-landmark .p-glow {
  animation-delay: 0.9s;
  animation-duration: 1.6s;
}

/* The sundial: the shadow sweeps round to noon, a glint on the gnomon. */
.beat-scene .w-shadow {
  fill: var(--sil);
  opacity: 0.6;
  transform-box: fill-box;
  transform-origin: 0% 50%;
  transform: rotate(-90deg);
}
.beat-scene[data-beat="asleep"] .w-shadow {
  transform: rotate(-190deg);
}
.beat-scene[data-beat="waking"] .w-shadow {
  animation: shadow-sweep 2.2s cubic-bezier(0.4, 0, 0.2, 1) 0.2s both;
}
@keyframes shadow-sweep {
  from {
    transform: rotate(-190deg);
  }
  to {
    transform: rotate(-90deg);
  }
}
.beat-scene .w-glint {
  fill: #fff8dc;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
.beat-scene[data-beat="waking"] .w-glint {
  animation: glint 1.1s ease-out 1.7s both;
}
@keyframes glint {
  0% {
    opacity: 0;
    transform: scale(0.3);
  }
  35% {
    opacity: 1;
    transform: scale(1.5) rotate(45deg);
  }
  100% {
    opacity: 0;
    transform: scale(0.6) rotate(90deg);
  }
}

/* The well: the bucket rises, glinting. */
.beat-scene .w-bucket,
.beat-scene .w-rope {
  transform-box: fill-box;
  transform-origin: 50% 0%;
}
.beat-scene[data-beat="asleep"] .w-bucket {
  opacity: 0;
  transform: translateY(8px);
}
.beat-scene[data-beat="asleep"] .w-rope {
  transform: scaleY(1.5);
}
.beat-scene[data-beat="waking"] .w-bucket {
  animation: bucket-rise 1.8s cubic-bezier(0.3, 0, 0.2, 1) 0.2s both;
}
.beat-scene[data-beat="waking"] .w-rope {
  animation: rope-rise 1.8s cubic-bezier(0.3, 0, 0.2, 1) 0.2s both;
}
@keyframes bucket-rise {
  0% {
    opacity: 0;
    transform: translateY(8px);
  }
  30% {
    opacity: 1;
  }
  80% {
    transform: translateY(-0.6px);
  }
}
@keyframes rope-rise {
  from {
    transform: scaleY(1.5);
  }
}

/* The hives: a low hum, and bees like sparks rising to hover. */
.beat-scene .w-bee {
  fill: #ffe9a8;
  opacity: 0;
}
.beat-scene[data-beat="awake"] .w-bee {
  opacity: 0.85;
  animation: bee-hover calc(2.6s + var(--i) * 0.3s) ease-in-out infinite
    alternate;
  animation-delay: calc(var(--i) * -0.7s);
}
.beat-scene[data-beat="waking"] .w-bee {
  animation: bee-rise 1.6s ease-out both;
  animation-delay: calc(0.4s + var(--i) * 0.12s);
}
@keyframes bee-rise {
  0% {
    opacity: 0;
    transform: translate(0, 10px);
  }
  50% {
    opacity: 1;
    transform: translate(2px, -2px);
  }
  100% {
    opacity: 0.85;
    transform: none;
  }
}
@keyframes bee-hover {
  0% {
    transform: translate(-1.5px, 0.5px);
  }
  50% {
    transform: translate(1px, -1.5px);
  }
  100% {
    transform: translate(2px, 1px);
  }
}
.beat-scene[data-beat="waking"] .st-landmark .p-hive:not(.quiet) {
  animation: hive-hum 0.11s linear 16 alternate 0.3s;
}
@keyframes hive-hum {
  to {
    transform: translateX(0.4px);
  }
}

/* The mill and the waterwheel: still until the tap, then they start turning. */
.beat-scene[data-beat="asleep"] .st-landmark .p-wheel {
  animation: none;
}
.beat-scene[data-beat="waking"] .st-landmark .p-wheel {
  animation: wheel-start 2.6s cubic-bezier(0.5, 0, 0.4, 1) both;
}
@keyframes wheel-start {
  from {
    transform: none;
  }
  to {
    transform: rotate(135deg);
  }
}

/* The lanterns light one by one, up the hill. */
.beat-scene[data-wake="lanterns"][data-beat="waking"] .st-landmark .p-glow {
  animation-delay: calc(0.25s + var(--i, 0) * 0.45s);
}

/* Small touches for the other landmarks. */
.beat-scene .st-landmark .p-bell {
  transform-box: fill-box;
  transform-origin: 50% 0%;
}
.beat-scene[data-beat="waking"] .st-landmark .p-bell {
  animation: bell-swing 2.4s ease-out 0.1s both;
}
@keyframes bell-swing {
  15% {
    transform: rotate(14deg);
  }
  35% {
    transform: rotate(-10deg);
  }
  55% {
    transform: rotate(6deg);
  }
  75% {
    transform: rotate(-3deg);
  }
}
.beat-scene .st-landmark .p-glow:is(.jet, .sheet) {
  transform-box: fill-box;
}
.beat-scene .st-landmark .p-glow.jet {
  transform-origin: 50% 100%;
}
.beat-scene .st-landmark .p-glow.sheet {
  transform-origin: 50% 0%;
}
.beat-scene[data-beat="asleep"] .st-landmark .p-glow:is(.jet, .sheet) {
  transform: scaleY(0.05);
}
.beat-scene[data-beat="waking"] .st-landmark .p-glow:is(.jet, .sheet) {
  animation: wake-rise 1.6s cubic-bezier(0.3, 0, 0.2, 1) 0.3s both;
}
@keyframes wake-rise {
  from {
    opacity: 0.1;
    transform: scaleY(0.05);
  }
  to {
    opacity: var(--to);
  }
}
.beat-scene[data-beat="asleep"] .st-landmark .p-smoke {
  opacity: 0;
  animation: none;
}
.beat-scene .st-landmark .w-scope {
  transform-box: fill-box;
  transform-origin: 0% 100%;
}
.beat-scene[data-beat="asleep"] .st-landmark .w-scope {
  transform: rotate(26deg);
}
.beat-scene[data-beat="waking"] .st-landmark .w-scope {
  animation: scope-lift 2s ease-in-out 0.2s both;
}
@keyframes scope-lift {
  from {
    transform: rotate(26deg);
  }
}
.beat-scene[data-beat="asleep"] .st-landmark .p-glow.comet {
  opacity: 0;
  transform: translate(-26px, -14px);
}
.beat-scene[data-beat="waking"] .st-landmark .p-glow.comet {
  animation: comet-in 2.2s cubic-bezier(0.2, 0.6, 0.3, 1) 0.2s both;
}
@keyframes comet-in {
  from {
    opacity: 0;
    transform: translate(-26px, -14px);
  }
  to {
    opacity: var(--to);
  }
}

/* The opening: Hazel's lantern passes to you. */
.beat-scene[data-wake="lantern"][data-beat="awake"] .actor.hazel .held {
  opacity: 0;
}
.beat-scene[data-wake="lantern"][data-beat="waking"] .actor.hazel .held {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: lantern-give 1s ease-in both;
}
@keyframes lantern-give {
  40% {
    opacity: 1;
    transform: scale(1.8);
  }
  100% {
    opacity: 0;
    transform: scale(2.6) translate(-1px, 2px);
  }
}
.beat-lantern {
  position: absolute;
  left: 50%;
  bottom: -6px;
  width: 120px;
  height: 120px;
  translate: -50% 0;
  opacity: 0;
  pointer-events: none;
}
.beat-lantern::before {
  content: "";
  position: absolute;
  inset: -40px;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 55%,
    #ffe7a0aa,
    #ffd98a44 30%,
    #ffd98a00 62%
  );
}
.beat-lantern svg {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 44px;
  translate: -50% 0;
  transform-origin: 50% 0;
  animation: lantern-sway 4.6s ease-in-out infinite alternate;
}
@keyframes lantern-sway {
  from {
    transform: rotate(-3deg);
  }
  to {
    transform: rotate(3deg);
  }
}
.beat-scene[data-beat="awake"] ~ .beat-lantern {
  opacity: 1;
}
.beat-scene[data-beat="waking"] ~ .beat-lantern {
  animation: lantern-take 1.6s cubic-bezier(0.2, 0.6, 0.3, 1) 0.6s both;
}
@keyframes lantern-take {
  from {
    opacity: 0;
    transform: translateY(40px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ---------- The cast answers their lines ----------
   story-card.ts marks who a line belongs to (.speaking on their figure). */
.beat-scene .actor {
  transition: filter 0.6s ease;
}
.beat-scene .actor.speaking {
  filter: drop-shadow(0 0 1.6px #ffe8a8cc);
}
/* Glad beats: a small hop. */
.beat-scene .actor.speaking > .figure {
  animation: beat-hop 0.75s ease-out 0.1s both;
}
@keyframes beat-hop {
  35% {
    transform: translateY(-7%);
  }
  60% {
    transform: translateY(0) scaleY(0.98);
  }
}
/* Wonder: a slow lift. Quiet beats and lines: a slow bow of the head. */
.beat-scene[data-mood="wonder"] .actor.speaking > .figure {
  animation: beat-lift 2s ease-in-out both;
}
@keyframes beat-lift {
  50% {
    transform: translateY(-3%) scaleY(1.02);
  }
}
.beat-scene[data-quiet] .actor.speaking > .figure,
.beat-scene[data-cue="quiet"] .actor.speaking > .figure {
  animation: beat-bow 2.6s ease-in-out both;
}
@keyframes beat-bow {
  40%,
  70% {
    transform: rotate(3deg) translateY(1%);
  }
}
/* Warm: the speaker leans toward the light, glowing. */
.beat-scene[data-cue="warm"] .actor.speaking > .figure {
  animation: beat-warm 2.4s ease-in-out both;
}
@keyframes beat-warm {
  35%,
  70% {
    transform: translateX(-2.5px) rotate(-4deg);
  }
}
.beat-scene[data-cue="warm"] .actor.speaking {
  filter: drop-shadow(0 0 3px #ffd98acc);
}
/* Hold: two figures lean together (Tam holds Hazel's hand). */
.beat-scene[data-cue="hold"] .actor.hazel > .figure {
  animation: lean-in-r 3s ease-in-out both;
}
.beat-scene[data-cue="hold"] .actor.tam > .figure {
  animation: lean-in-l 3s ease-in-out both;
}
@keyframes lean-in-r {
  35%,
  75% {
    transform: translateX(2px) rotate(2deg);
  }
}
@keyframes lean-in-l {
  35%,
  75% {
    transform: translateX(-2.5px) rotate(-3deg);
  }
}
/* Trot: the fox trots in, as if he arranged it. */
.beat-scene[data-cue="trot"] .actor.fox > .figure {
  animation: fox-trot 2.2s ease-out both;
}
@keyframes fox-trot {
  0% {
    opacity: 0;
    transform: translate(-16px, 0);
  }
  18% {
    opacity: 1;
  }
  25% {
    transform: translate(-11px, -0.8px);
  }
  40% {
    transform: translate(-7px, 0);
  }
  55% {
    transform: translate(-4px, -0.8px);
  }
  70% {
    transform: translate(-2px, 0);
  }
  85% {
    transform: translate(-0.8px, -0.5px);
  }
  100% {
    transform: none;
  }
}
/* Peek: something small and orange comes up out of the brambles. */
.beat-scene[data-fox="hidden"] .actor.fox > .figure {
  animation: none;
  opacity: 0;
  transform: translateY(45%);
}
.beat-scene[data-fox="peek"][data-fox] .actor.fox > .figure {
  animation: fox-peek 2.2s ease-out both;
}
@keyframes fox-peek {
  0% {
    opacity: 0;
    transform: translateY(45%);
  }
  30% {
    opacity: 1;
    transform: translateY(22%);
  }
  55% {
    transform: translateY(24%) rotate(-4deg);
  }
  100% {
    transform: none;
  }
}
/* Arrive: someone comes up the path on their line (Wren, with Rowan’s lantern, at the top). */
.beat-scene:is([data-arrive="wren"]):not([data-arrived])
  .actor.wren
  > .figure {
  animation: none;
  opacity: 0;
}
.beat-scene[data-arrive="wren"][data-arrived] .actor.wren > .figure {
  animation: walk-in 2.6s cubic-bezier(0.3, 0, 0.3, 1) both;
}
@keyframes walk-in {
  0% {
    opacity: 0;
    transform: translateX(-9px);
  }
  30% {
    opacity: 1;
  }
  20%,
  40%,
  60%,
  80% {
    translate: 0 -0.4px;
  }
  30%,
  50%,
  70%,
  90% {
    translate: 0 0;
  }
  100% {
    transform: none;
  }
}
/* Rain: the storm arrives on its line. */
.beat-scene[data-rain] .stage-rain {
  transition: opacity 2.4s ease-in;
}
.beat-scene[data-rain="wait"] .stage-rain {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  #story-card *,
  #story-card *::before,
  #story-card *::after,
  .story-art::after {
    animation: none !important;
    transition: none !important;
  }
}
