/* The story component: one sticky WebGL stage with DOM panels scrolling past it.
 *
 * Shared by the standalone page (story/index.html, .story) and the landing, where it plays inline
 * below the hero (.story.story--inline). Everything is scoped under .story and every variable is
 * --st-*, so the component never inherits a look's palette or type and never leaks into the page.
 *
 * Structure (built by js/story/mount.js):
 *   .story[data-gl=pending|on|off]
 *     .st-viewport          sticky, one viewport tall: the picture and its chrome
 *       canvas.st-stage, canvas.st-fade, .st-scrim, .st-status, nav.st-rail-wrap
 *     .st-track             the scroll length; panels sit on it at their story time
 */

.story {
  --st-display: 'Newsreader', Georgia, serif;
  --st-body: 'Inter', system-ui, -apple-system, sans-serif;
  --st-mono: 'IBM Plex Mono', ui-monospace, monospace;
  --st-type: var(--u-type, 1); --st-text: var(--u-text, 1); --st-leading: var(--u-leading, 1);
  --st-measure: var(--u-measure, 1); --st-space: var(--u-space, 1); --st-radius: var(--u-radius, 1);
  --st-motion: var(--u-motion, 1);
  --era-len: 150vh;
  --st-ease: cubic-bezier(.2, .7, .2, 1);
  --st-dur: calc(240ms * var(--st-motion));
  --st-panel-y: 50vh;

  position: relative;
  isolation: isolate;
  display: block;
  margin: 0;
  color: var(--st-text-1);
  font: 400 calc(16px * var(--st-text)) / calc(1.6 * var(--st-leading)) var(--st-body);
  text-align: left;
}
html[data-theme='light'] .story {
  --st-bg: #ebe4d9;
  --st-text-1: #28241f;
  --st-text-2: #49433b;
  --st-text-3: #5e574d;
  --st-accent: #7a4f2c;
  --st-surface: rgba(248, 244, 237, .9);
  --st-line: rgba(40, 36, 31, .14);
  --st-scrim-a: rgba(235, 228, 217, .82);
  --st-scrim-b: rgba(235, 228, 217, 0);
  --st-shadow: 0 1px 2px rgba(60, 44, 28, .06), 0 12px 32px -12px rgba(60, 44, 28, .22);
  --st-tick: rgba(40, 36, 31, .3);
}
html[data-theme='dark'] .story {
  --st-bg: #14110e;
  --st-text-1: #ede7dc;
  --st-text-2: #c7bfb2;
  --st-text-3: #a39a8c;
  --st-accent: #d6b086;
  --st-surface: rgba(26, 22, 18, .84);
  --st-line: rgba(237, 231, 220, .12);
  --st-scrim-a: rgba(20, 17, 14, .78);
  --st-scrim-b: rgba(20, 17, 14, 0);
  --st-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 16px 40px -16px rgba(0, 0, 0, .7);
  --st-tick: rgba(237, 231, 220, .28);
}
.story *, .story *::before, .story *::after { box-sizing: border-box; }
.story a { color: inherit; }
.story :focus-visible { outline: 2px solid var(--st-accent); outline-offset: 3px; border-radius: 4px; }

/* ── The picture ──────────────────────────────────────────────────────────────────────────── */
.st-viewport {
  position: sticky; top: 0; z-index: 0;
  height: 100vh; overflow: hidden;
  background: var(--st-bg);
  pointer-events: none;
  transition: background-color var(--st-dur) var(--st-ease);
}
.st-viewport canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.st-fade { opacity: 0; }
/* A soft wash under the text column, so copy reads on any frame without a hard box edge. */
.st-scrim { position: absolute; inset: 0; background: linear-gradient(90deg, var(--st-scrim-a) 0%, var(--st-scrim-a) 18%, var(--st-scrim-b) 46%); opacity: .9; }
/* The words, while the picture draws: pinned with it and placed by the shown time (mount.js). */
.st-copy {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  /* Words on their way out dissolve before they reach the fixed name and theme pills at the
     top, and come up out of the page at the bottom rather than out from under the dock. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, transparent 56px, #000 128px, #000 calc(100% - 40px), transparent);
  mask-image: linear-gradient(to bottom, transparent 0, transparent 56px, #000 128px, #000 calc(100% - 40px), transparent);
}
.story .st-copy .st-panel { top: var(--st-panel-y); will-change: transform, opacity; }
.st-status {
  position: absolute; right: 24px; bottom: 20px; margin: 0;
  font: 400 12px/1 var(--st-mono); letter-spacing: .04em; color: var(--st-text-3);
  transition: opacity 400ms var(--st-ease);
}
.st-status:empty { opacity: 0; }
.story[data-gl='pending'] .st-stage { opacity: 0; }
.story[data-gl='on'] .st-stage { opacity: 1; transition: opacity 900ms var(--st-ease); }

/* The era rail: where you are, and a way to jump. Lives in the sticky viewport. */
.st-rail-wrap { position: absolute; right: clamp(12px, 2vw, 28px); top: 50%; transform: translateY(-50%); z-index: 2; pointer-events: auto; }
.st-rail { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.st-rail button {
  display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-direction: row-reverse;
  min-height: 28px; padding: 4px 0; border: 0; background: none; color: var(--st-text-3); cursor: pointer;
  font: 500 11px/1 var(--st-body); letter-spacing: .06em; text-transform: uppercase;
}
.st-rail .tick { width: 18px; height: 2px; border-radius: 2px; background: var(--st-tick); transition: width var(--st-dur) var(--st-ease), background-color var(--st-dur) var(--st-ease); }
.st-rail .label {
  opacity: 0; transform: translateX(6px); white-space: nowrap;
  padding: 5px 8px; border-radius: 6px; background: var(--st-surface); box-shadow: var(--st-shadow);
  transition: opacity var(--st-dur) var(--st-ease), transform var(--st-dur) var(--st-ease);
}
.st-rail button:hover .label, .st-rail button:focus-visible .label { opacity: 1; transform: none; }
.st-rail button[aria-current] { color: var(--st-text-1); }
.st-rail button[aria-current] .tick { width: 30px; background: var(--st-accent); }

/* ── The scroll track and its panels ──────────────────────────────────────────────────────── */
/* The track starts at the top of the component (pulled up under the sticky viewport), so the
   component is exactly as tall as its scroll length: 5 eras plus one viewport. */
.st-track { position: relative; z-index: 1; margin-top: -100vh; height: calc(var(--era-len) * 5 + 100vh); pointer-events: none; }
/* The copy's catch-up offset (--st-lag) can run to several eras after a fast fling. Clipped, a
   panel pushed past the track's end can never lengthen the page: unclipped, the fling carried on
   into that phantom length, past the footer, onto the bare hero layer behind the page. */
.st-track { overflow: clip; }
.story .st-panel {
  position: absolute; left: clamp(20px, 6vw, 96px); top: calc(var(--era-len) * var(--at) + var(--st-panel-y));
  transform: translateY(-50%);
  /* Copy follows the SHOWN story time: mount.js sets --st-lag to the gap between where the page
     has scrolled and where the picture is, so words and picture never disagree mid-catch-up. */
  translate: 0 var(--st-lag, 0px);
  width: min(calc(30rem * var(--st-measure)), 40vw);
  margin: 0; padding: 0;
  will-change: opacity;
  pointer-events: auto;
}
.story .st-panel :where(h1, h2, p, ul, figure) { margin: 0; padding: 0; border: 0; background: none; text-transform: none; max-width: none; }
.story .st-panel :is(h1, h2), .story .st-close-line {
  font-family: var(--st-display); font-weight: 300; font-style: normal; letter-spacing: -.02em; color: var(--st-text-1); text-wrap: balance;
}
.story .st-hero-name { font-size: calc(clamp(52px, 7vw, 104px) * var(--st-type)); line-height: .95; }
.story .st-hero-tag { font: 300 calc(clamp(20px, 2vw, 26px) * var(--st-type)) / 1.3 var(--st-display); color: var(--st-text-2); margin-top: calc(18px * var(--st-space)); max-width: 22ch; }
.story .st-panel h2 { font-size: calc(clamp(34px, 3.8vw, 56px) * var(--st-type)); line-height: 1.02; }
.story .st-body { color: var(--st-text-2); margin-top: calc(16px * var(--st-space)); max-width: 34ch; font: 400 calc(17px * var(--st-text)) / calc(1.6 * var(--st-leading)) var(--st-body); }
.story .st-panel:where(:has(h2.is-long)) { width: min(calc(34rem * var(--st-measure)), 44vw); }
.story .st-panel h2.is-long { font-size: calc(clamp(30px, 3vw, 44px) * var(--st-type)); line-height: 1.06; }
.story .st-note { color: var(--st-text-3); margin-top: calc(14px * var(--st-space)); font: italic 400 13px/1.4 var(--st-body); }
.story .st-kicker {
  display: flex; align-items: center; gap: 10px; margin: 0 0 calc(14px * var(--st-space));
  font: 500 12px/1 var(--st-body); letter-spacing: .1em; text-transform: uppercase; color: var(--st-text-3);
}
.story .st-kicker .num { font: 400 12px/1 var(--st-mono); color: var(--st-accent); letter-spacing: 0; }
.story .st-draft {
  font: 500 10px/1 var(--st-mono); letter-spacing: .08em; padding: 4px 6px; border-radius: calc(4px * var(--st-radius));
  border: 1px solid var(--st-accent); color: var(--st-accent);
}
.story .st-cue { display: flex; align-items: center; gap: 8px; margin-top: calc(36px * var(--st-space)); color: var(--st-text-3); font: 400 14px/1.4 var(--st-body); }
.story .st-cue svg { animation: st-cue 2.4s var(--st-ease) infinite; }
@keyframes st-cue { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }

.story .st-close-line { font-size: calc(clamp(30px, 3.2vw, 48px) * var(--st-type)); line-height: 1.08; }
.story .st-links { list-style: none; margin-top: calc(28px * var(--st-space)); display: flex; flex-wrap: wrap; gap: 10px; }
.story .st-links li { margin: 0; padding: 0; }
.story .st-links li::before { content: none; }
.story .st-links a {
  display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 8px 14px;
  border-radius: calc(999px * var(--st-radius)); border: 1px solid var(--st-line); background: var(--st-surface);
  text-decoration: none; font: 400 14px/1.2 var(--st-body); color: var(--st-text-1); box-shadow: var(--st-shadow);
  transition: border-color var(--st-dur) var(--st-ease), transform var(--st-dur) var(--st-ease);
}
.story .st-links a:hover { border-color: var(--st-accent); transform: translateY(-1px); }
.story .st-links svg { color: var(--st-accent); }
.story .st-credit { margin-top: calc(26px * var(--st-space)); font: 400 12px/1.4 var(--st-mono); color: var(--st-text-3); }
html[data-work='hidden'] .story .work-only { display: none; }

.story .st-poster { display: none; margin: 0 0 20px; border-radius: calc(12px * var(--st-radius)); overflow: hidden; aspect-ratio: 16 / 10; background: var(--st-surface); }
.story .st-poster img { width: 100%; height: 100%; object-fit: cover; display: block; }
html[data-theme='light'] .story .poster-dark, html[data-theme='dark'] .story .poster-light { display: none; }

/* ── Phones: the set up top, the words at the bottom ─────────────────────────────────────── */
@media (max-aspect-ratio: 4/5), (max-width: 720px) {
  .story { --st-panel-y: 74svh; }
  .story .st-panel {
    left: 16px; right: 16px; width: auto; padding: 18px 20px; border-radius: calc(16px * var(--st-radius));
    background: var(--st-surface); border: 1px solid var(--st-line); box-shadow: var(--st-shadow);
  }
  .story .st-panel.is-hero { background: none; border: 0; box-shadow: none; --st-panel-y: 70svh; }
  .st-scrim { background: linear-gradient(0deg, var(--st-scrim-a) 0%, var(--st-scrim-a) 16%, var(--st-scrim-b) 44%); }
  .story .st-hero-name { font-size: calc(54px * var(--st-type)); }
  .story .st-hero-tag { font-size: calc(21px * var(--st-type)); }
  .story .st-panel h2 { font-size: calc(30px * var(--st-type)); }
  .story .st-panel h2.is-long { font-size: calc(27px * var(--st-type)); }
  .story .st-body { font-size: calc(16px * var(--st-text)); margin-top: 10px; }
  .story .st-kicker { margin-bottom: 10px; }
  .story .st-cue { margin-top: 20px; }
  .story .st-close-line { font-size: calc(26px * var(--st-type)); }
  .st-rail-wrap { right: 6px; top: 40%; }
  /* A card passing through the rail on the way to its place covers it, rather than wearing it. */
  .st-copy { z-index: 3; }
  .st-rail .label { display: none; }
  .st-rail .tick { width: 12px; }
  .st-rail button[aria-current] .tick { width: 20px; }
  .st-status { right: 16px; bottom: auto; top: 64px; }
  /* Inline, the hero's camera rail is still on the right edge while the stage rises. */
  .story--inline .st-status { right: auto; left: 16px; }
}

/* ── No WebGL: posters and words, in normal flow ─────────────────────────────────────────── */
.story[data-gl='off'] .st-viewport { display: none; }
.story[data-gl='off'] .st-track { margin-top: 0; height: auto; padding: 120px clamp(20px, 6vw, 96px) 80px; display: grid; gap: 96px; justify-items: center; background: var(--st-bg); }
.story[data-gl='off'] .st-panel {
  position: static; transform: none; width: 100%; max-width: 52rem; opacity: 1 !important; visibility: visible !important;
  background: none; border: 0; box-shadow: none; padding: 0;
}
.story[data-gl='off'] .st-poster { display: block; }
.story[data-gl='off'] .st-panel.is-hero { padding-top: 10vh; }

/* ── Inline on the landing ───────────────────────────────────────────────────────────────── */
/* The stage rises out of the hero: its edges feather by --st-feather-top/-bottom (set by
   js/story/landing.js from scroll position through a spring) so it never arrives as a hard
   block, and it settles to full strength as it covers the viewport. */
/* The site's scroll-padding-top (80px, for the fixed header) would park #story 80px short of
   the fold, leaving the story half-engaged; the story starts at the very top of the viewport. */
.story--inline { --st-feather-top: 0px; --st-feather-bottom: 0px; --st-reveal: 1; scroll-margin-top: -80px; }
.story--inline .st-viewport {
  opacity: var(--st-reveal);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--st-feather-top), #000 calc(100% - var(--st-feather-bottom)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--st-feather-top), #000 calc(100% - var(--st-feather-bottom)), transparent 100%);
}
/* No feather in flight: no mask at all, so the stage composites as a plain layer. */
.story--inline.is-settled .st-viewport { -webkit-mask-image: none; mask-image: none; }
.story--inline .st-panel.is-hero .st-kicker { margin-bottom: calc(18px * var(--st-space)); }
/* The story ends into the page below it: Part 2, the work index (when published) or the footer.
   As the close arrives the picture fades to the page's paper, foot first and then whole (--st-end-*
   from the shown time, mount.js), so the close stands on clean paper. It lives in the picture under
   the words. Two layers, the page's own (--work-bg) over the veil's paper, because that is exactly
   what the section below the story sits on. */
.st-end { display: none; }
.story--inline .st-end {
  --st-end-top: 0; --st-end-foot: 0;
  --st-end-a: calc(var(--st-end-top) * 100%); --st-end-b: calc(var(--st-end-foot) * 100%);
  display: block; position: absolute; inset: 0; opacity: 0;
  background:
    linear-gradient(to bottom, color-mix(in srgb, var(--work-bg, var(--st-bg)) var(--st-end-a), transparent) 0%, color-mix(in srgb, var(--work-bg, var(--st-bg)) var(--st-end-a), transparent) 40%, color-mix(in srgb, var(--work-bg, var(--st-bg)) var(--st-end-b), transparent) 82%),
    linear-gradient(to bottom, color-mix(in srgb, var(--st-paper) var(--st-end-a), transparent) 0%, color-mix(in srgb, var(--st-paper) var(--st-end-a), transparent) 40%, color-mix(in srgb, var(--st-paper) var(--st-end-b), transparent) 82%);
  pointer-events: none;
}
/* One grid with the page: the words hang on the name's line (base.css --edge-text), and the close
   stands at the foot of the picture, a step above where the footer's rule begins at the end of
   the page, so the close, the footer's links and its archive link read as one block. */
.story--inline { --st-close-gap: calc(40px * var(--st-space)); }
.story--inline .st-panel { left: var(--edge-text, clamp(20px, 6vw, 96px)); }
.story--inline:not([data-gl='off']) .st-panel.is-close {
  --st-panel-y: calc(100vh - var(--st-close-gap));
  translate: 0 calc(-50% + var(--st-lag, 0px));
}
@media (max-aspect-ratio: 4/5), (max-width: 720px) {
  /* Phones: the cards' edges are the chrome's edges, and the close stands a step above the footer. */
  .story--inline .st-panel { left: var(--edge, 16px); right: var(--edge, 16px); padding: 16px var(--chip-pad, 12px); }
  .story--inline { --st-close-gap: 16px; }
  .story--inline:not([data-gl='off']) .st-panel.is-close { --st-panel-y: calc(100svh - var(--st-close-gap)); }
}
/* The fixed veil between the hero's scene and the page: the hero dims into the story's paper. */
.story-veil {
  position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background: var(--st-veil, #14110e); opacity: 0;
}
html[data-theme='light'] { --st-paper: #ebe4d9; }
html[data-theme='dark'] { --st-paper: #14110e; }
.story-veil { --st-veil: var(--st-paper); }
/* The other handoffs (js/story/handoff.js; ?handoff=<id>, ⌘⇧D "Handoff"). While the section is
   still rising they pin the story's picture to the viewport (.is-pinned) and reveal it through a
   clip, a mask or a turn, so the picture is whole from the first pixel rather than scrolled in.
   Fixed here is where sticky will hold it, so letting go at the top is seamless. */
.story--inline.is-pinned .st-viewport { position: fixed; top: 0; left: 0; right: 0; }
.story--inline.is-pinned .st-track { margin-top: 0; }
/* Past the end, while the picture is still playing its way there (mount.js holdEnd): it stays
   pinned over the page below, then lifts (its transform) to where sticky puts it and lets go.
   It takes the clicks while it covers the footer. */
.story.is-held { z-index: 1; }
.story.is-held .st-viewport { position: fixed; top: 0; left: 0; right: 0; pointer-events: auto; will-change: transform; }
.story.is-held .st-track { margin-top: 0; }
/* The hero's scene moves as a picture: its children, never the layer, whose size the looks read. */
.scene-layer[data-hh] > * { transform: var(--hh-tf); transform-origin: 0 0; will-change: transform; backface-visibility: hidden; }
.handoff-fx { position: fixed; left: 0; top: 0; pointer-events: none; opacity: 0; will-change: transform, opacity; }
.handoff-under { z-index: 1; }
.handoff-over { z-index: 2; }
/* The window's frame: the shadow it casts on the hero, and a lit hairline on its edge. */
.handoff-under[data-kind='frame'] { box-shadow: 0 34px 90px -24px rgba(0, 0, 0, .6), 0 10px 28px -12px rgba(0, 0, 0, .4); }
.handoff-over[data-kind='frame'] { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12), inset 0 1px 0 rgba(255, 255, 255, .22); }
html[data-theme='light'] .handoff-under[data-kind='frame'] { box-shadow: 0 34px 90px -24px rgba(60, 40, 20, .38), 0 10px 28px -12px rgba(60, 40, 20, .26); }
html[data-theme='light'] .handoff-over[data-kind='frame'] { box-shadow: inset 0 0 0 1px rgba(40, 30, 20, .1), inset 0 1px 0 rgba(255, 255, 255, .5); }
/* The pop-up page's shadow, cast on the hero just above its top edge. */
.handoff-under[data-kind='band'] { background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, .55)); }
html[data-theme='light'] .handoff-under[data-kind='band'] { background: linear-gradient(to bottom, rgba(60, 40, 20, 0), rgba(60, 40, 20, .32)); }
/* …and its top edge, catching the light. */
.handoff-over[data-kind='rim'] { background: linear-gradient(to right, rgba(255, 238, 214, 0), rgba(255, 238, 214, .55) 18%, rgba(255, 238, 214, .7) 50%, rgba(255, 238, 214, .55) 82%, rgba(255, 238, 214, 0)); box-shadow: 0 0 14px rgba(255, 220, 180, .18); }
html[data-theme='light'] .handoff-over[data-kind='rim'] { background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, .85) 18%, #fff 50%, rgba(255, 255, 255, .85) 82%, rgba(255, 255, 255, 0)); box-shadow: 0 1px 0 rgba(60, 40, 20, .12); }
/* The pop-up page turned away from the light: darkest at its far (top) edge, which catches a rim. */
.handoff-shade {
  position: absolute; inset: 0; z-index: 3; pointer-events: none; opacity: 0;
  background: linear-gradient(to bottom, rgba(8, 6, 4, .92), rgba(8, 6, 4, .5) 55%, rgba(8, 6, 4, .22));
  box-shadow: inset 0 2px 0 rgba(255, 244, 228, .35);
}
/* The corner's floor, tipped away at its far (bottom) edge. */
.handoff-shade[data-dir='down'] { background: linear-gradient(to top, rgba(8, 6, 4, .78), rgba(8, 6, 4, .3) 60%, rgba(8, 6, 4, .08)); box-shadow: none; }
/* The room's dark around the lamp's pool of light (the gradient is written per frame). */
.handoff-dark { position: absolute; inset: 0; z-index: 4; pointer-events: none; opacity: 0; }
/* The hero's scrim, inside the picture while the story draws the hero on Chris's page (sketch). */
.handoff-scrim { position: absolute; inset: 0; pointer-events: none; opacity: 0; background: var(--scrim); }

/* A story cue in the hero while the work index is unpublished (its own cue is hidden then). */
/* Set by landing.js from the handoff spring: the hero's copy fades as the story rises. */
.hero { opacity: var(--story-hero-fade, 1); }
/* …and the look switcher with it (it only steers the hero), out of reach once the hero is gone. */
.director { opacity: var(--story-hero-fade, 1); }
html[data-story-hero='gone'] .director { visibility: hidden; pointer-events: none; }
.story-cue {
  display: inline-flex; align-items: center; gap: 8px; justify-self: center; align-self: end;
  /* Clears the look switcher, which sits along the bottom edge over the hero. */
  margin-bottom: calc(52px + env(safe-area-inset-bottom, 0px));
  padding: 8px 12px; font-family: var(--font-mono); font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-2); text-decoration: none; pointer-events: auto;
  transition: color var(--dur-fast, 160ms) var(--ease-enter, ease);
  animation: story-cue-in calc(900ms * var(--u-motion, 1)) cubic-bezier(.2, .7, .2, 1) 700ms both;
}
@keyframes story-cue-in { from { opacity: 0; transform: translateY(8px); } }
.story-cue svg:not(.story-cue__thread) { transition: transform calc(320ms * var(--u-motion, 1)) var(--ease-enter, ease); }
.story-cue:hover { color: var(--text); }
.story-cue:hover svg:not(.story-cue__thread) { transform: translateY(3px); }
html[data-work='on'] .story-cue { display: none; }

/* The cue doubles as the story's warm-up (js/story/preload.js): a hand-drawn thread draws
   under it as the story builds off screen, in the look's accent, then runs on into the arrow
   (the arrow takes the tug) and the cue is the plain cue again. Never a bar: no track, no
   percentage. If the warm-up is done before the cue arrives, the thread is just its entrance. */
.story-cue { position: relative; }
.story-cue__thread {
  position: absolute; left: 12px; right: 12px; bottom: 1px; width: calc(100% - 24px); height: 7px;
  overflow: visible; pointer-events: none;
}
.story-cue__thread path {
  fill: none; stroke: var(--accent, currentColor); stroke-width: 1.4px; stroke-linecap: round;
  vector-effect: non-scaling-stroke; stroke-dasharray: 0 2; stroke-dashoffset: 0;
}
.story-cue[data-warm='done'] .story-cue__thread { visibility: hidden; }
.story-cue[data-warm='done'] svg:not(.story-cue__thread) { animation: story-cue-tug calc(620ms * var(--u-motion, 1)) cubic-bezier(.3, .7, .2, 1) both; }
@keyframes story-cue-tug { 38% { transform: translateY(4px); } 70% { transform: translateY(-1px); } }
/* Clicked (story/glide.js): the arrow gathers, then leads the way down as the camera moves. */
.story-cue[data-going] { color: var(--text); }
.story-cue[data-going] svg:not(.story-cue__thread) { animation: story-cue-go calc(900ms * var(--u-motion, 1)) cubic-bezier(.5, 0, .2, 1) both; }
@keyframes story-cue-go { 0% { transform: none; } 22% { transform: translateY(-2px); } 100% { transform: translateY(10px); opacity: 0; } }
/* The story itself, if the visitor is there before it is built: it holds its opening beat
   (mount.js) and the same thread runs under its status line. */
.st-status::after {
  content: ''; display: block; height: 1.5px; margin-top: 7px; border-radius: 2px;
  background: currentColor; opacity: .7;
  transform-origin: left; transform: scaleX(var(--st-warm, 0));
  transition: transform 300ms var(--st-ease);
}
.story:not([data-held]) .st-status::after { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .story .st-cue svg { animation: none; }
  .story--inline .st-viewport { opacity: 1; -webkit-mask-image: none; mask-image: none; }
  .story-veil, .handoff-fx, .handoff-shade, .handoff-dark, .handoff-scrim { display: none; }
  .story-cue { animation: none; }
  .story-cue[data-warm='done'] svg:not(.story-cue__thread) { animation: none; }
  .st-status::after { transition: none; }
}
