/* Scroll-story styles for index.html.
   Two motion systems:
   1. Reveals — IntersectionObserver (story.js) toggles .in-view; CSS transitions.
      Content is fully visible without JS; body.js-anim arms the hidden states.
   2. Scene loops — plain time-based CSS keyframe animations (no JS, identical
      in every browser), all inside prefers-reduced-motion: no-preference.
   prefers-reduced-motion: loops never run, story.js never arms .js-anim —
   everything is a static, fully readable diagram. */

@layer components {

  main.story { max-width: none; padding: 0; }

  .scene {
    display: grid;
    place-items: center;
    padding: var(--sp-6) var(--sp-3);
    position: relative;
  }
  /* only the hero claims a full viewport — other scenes size to content */
  .scene.hero { min-height: 100svh; }
  .scene + .scene { border-top: 1px solid color-mix(in oklab, var(--line) 55%, transparent); }

  .scene-inner { max-width: var(--content-w); width: 100%; }
  .scene-inner.center { text-align: center; display: grid; justify-items: center; gap: var(--sp-3); }
  .scene-inner.cols {
    display: grid;
    gap: var(--sp-5);
    align-items: center;
  }
  @media (min-width: 56rem) {
    .scene-inner.cols { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }
    .scene-inner.cols.flip > .scene-text { order: 2; }
    .scene-inner.cols.flip > .scene-fig { order: 1; }
  }
  .scene-inner.stack { display: grid; gap: var(--sp-4); justify-items: center; }
  .scene-text.center { text-align: center; display: grid; justify-items: center; }

  .hero-title {
    font-size: var(--fs-hero);
    font-weight: 650;
    letter-spacing: -0.01em;
    background: linear-gradient(100deg, var(--text-100) 30%, var(--neutron) 75%, var(--muon));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
  .hero-pulse { inline-size: min(20rem, 60vw); margin-block: var(--sp-2); }

  .scene-stat { margin-top: var(--sp-3); font-family: var(--font-mono); }
  .scene-stat .num {
    display: block;
    font-size: clamp(2.2rem, 1.2rem + 3.4vw, 4rem);
    font-weight: 600;
    color: var(--accent);
    line-height: 1.05;
  }
  .scene-stat .label {
    font-size: var(--fs-tiny);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-500);
  }
  /* unit symbols keep their case — uppercasing turns µs into ΜS (reads as ms) */
  .scene-stat .label .unit { text-transform: none; }
  #s3 .scene-stat .num { color: var(--peak); }
  .stat-row { display: flex; gap: var(--sp-5); flex-wrap: wrap; justify-content: center; }

  .scene-fig { width: 100%; }
  .scene-fig.wide { max-width: 56rem; }
  .scene-fig svg { width: 100%; height: auto; }
  .tof-eq { text-align: center; font-size: var(--fs-small); color: var(--text-300); margin-top: var(--sp-2); }
  .tof-eq code { color: var(--peak); font-size: 1.05rem; }

  .scroll-cue {
    font-family: var(--font-mono);
    font-size: var(--fs-small);
    letter-spacing: 0.3em;
    color: var(--text-500);
    margin-top: var(--sp-4);
  }

  /* short viewports (e.g. 768px-tall laptops): tighten the hero so the stat
     and scroll cue stay above the fold */
  @media (max-height: 52rem) {
    /* 3.75rem ≈ the in-flow header, so header + hero fill one viewport */
    .scene.hero { padding-block: var(--sp-4); min-height: calc(100svh - 3.75rem); margin-top: 0; }
    .scene.hero .scene-inner.center { gap: var(--sp-2); }
    .scene.hero .hero-title { font-size: clamp(2.4rem, 1.2rem + 4vw, 4.2rem); }
    .hero-pulse { inline-size: min(13rem, 50vw); margin-block: 0; }
    .scene.hero .scene-stat { margin-top: var(--sp-2); }
    .scene.hero .scene-stat .num { font-size: clamp(1.9rem, 1rem + 2.6vw, 3rem); }
    .scroll-cue { margin-top: var(--sp-2); }
  }

  .cta-row { width: 100%; margin-top: var(--sp-4); text-align: start; }
  /* five topic cards: full-width fifth at two-column widths, 3+2 at laptop,
     one row of five on wide screens — never a lone orphan card */
  @media (max-width: 49.99rem) {
    .cta-row .cta-card:nth-child(5) { grid-column: 1 / -1; }
  }
  @media (min-width: 56rem) {
    .cta-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  }
  @media (min-width: 75rem) {
    .cta-row { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  }

  /* ---------- 1. Reveals (IO-driven, all browsers) ---------- */

  body.js-anim .reveal {
    opacity: 0;
    translate: 0 26px;
    transition: opacity 0.7s ease, translate 0.7s ease;
  }
  body.js-anim .reveal.d1 { transition-delay: 0.12s; }
  body.js-anim .reveal.d2 { transition-delay: 0.24s; }
  body.js-anim .reveal.d3 { transition-delay: 0.36s; }
  body.js-anim .reveal.d4 { transition-delay: 0.48s; }
  body.js-anim .in-view .reveal,
  body.js-anim .reveal.in-view {
    opacity: 1;
    translate: 0 0;
  }

  /* ---------- time-based motion: ambient + scene loops ---------- */

  /* Static page (reduced motion / very old browsers) shows only the final
     energy reading; the earlier ones appear during the loop. Arrival stamps
     (s5) and event-log lines (s6) stay visible statically — they double as
     labels. (Scene 6's flying neutrons + histogram build are generated WAAPI
     loops in story.js; they simply never exist on the static page.) */
  .ring-energy .e1,
  .ring-energy .e2 { opacity: 0; }
  /* recap interlude actors are loop-only; statically the figure is a
     labelled map of the machine */
  .rc-actor { opacity: 0; }
  /* s7 static page shows the final ("physical units") caption only */
  .pu-1 { opacity: 0; }

  @media (prefers-reduced-motion: no-preference) {
    .hero-pulse .bunch {
      animation: bunch-throb 1.1s ease-in-out infinite;
      transform-origin: 100px 30px;
    }
    @keyframes bunch-throb {
      0%, 100% { transform: scale(1); opacity: 1; }
      50% { transform: scale(1.18); opacity: 0.75; }
    }
    /* gentle bob on the hero scroll cue */
    .scroll-cue { animation: cue-bob 1.5s ease-in-out infinite; }
    @keyframes cue-bob {
      0%, 100% { transform: translateY(0); }
      50% { transform: translateY(6px); }
    }
    /* moderator: fast dot in, slow dot out, looping */
    .mod-fast { animation: mod-in 3.2s linear infinite; }
    .mod-slow { animation: mod-out 3.2s linear infinite; }
    @keyframes mod-in {
      0% { transform: translateX(0); opacity: 1; }
      35% { transform: translateX(140px); opacity: 1; }
      45%, 100% { transform: translateX(140px); opacity: 0; }
    }
    @keyframes mod-out {
      0%, 45% { transform: translateX(0); opacity: 0; }
      55% { transform: translateX(0); opacity: 1; }
      100% { transform: translateX(140px); opacity: 1; }
    }

    /* s2 synchrotron loop: inject from the linac (0.75turn = the 9 o'clock
       injection point), laps at increasing speed, extraction kick at 3 o'clock
       (3.25turn), exit along the extraction line. The bunch sits at 12 o'clock
       in the markup; rotation is clockwise. */
    .fx-ring { animation: ring-cycle 7s linear infinite; }
    @keyframes ring-cycle {
      0%   { transform: rotate(0.75turn) translateY(-62px); opacity: 0; }
      2%   { opacity: 1; }
      7%   { transform: rotate(0.75turn) translateY(0); }
      30%  { transform: rotate(1.10turn) translateY(0); }
      52%  { transform: rotate(1.70turn) translateY(0); }
      70%  { transform: rotate(2.45turn) translateY(0); }
      88%  { transform: rotate(3.25turn) translateY(0); }
      95%  { transform: rotate(3.25turn) translateY(-66px); opacity: 1; }
      100% { transform: rotate(3.25turn) translateY(-66px); opacity: 0; }
    }
    /* beam-energy readout stepping up in sync with the laps */
    .ring-energy .e1 { animation: energy-1 7s linear infinite; }
    .ring-energy .e2 { animation: energy-2 7s linear infinite; }
    .ring-energy .e3 { animation: energy-3 7s linear infinite; }
    @keyframes energy-1 { 0%, 27% { opacity: 1; } 32%, 100% { opacity: 0; } }
    @keyframes energy-2 { 0%, 29% { opacity: 0; } 34%, 53% { opacity: 1; } 58%, 100% { opacity: 0; } }
    @keyframes energy-3 { 0%, 56% { opacity: 0; } 61%, 93% { opacity: 1; } 100% { opacity: 0; } }

    /* s3 probes loop: proton flies the beamline (passes graphite ~20%),
       muons rise from the graphite, burst grows from the tungsten. */
    .fx-proton { animation: probe-proton 7s linear infinite; }
    @keyframes probe-proton {
      0%        { transform: translateX(0); opacity: 0; }
      3%        { opacity: 1; }
      58%       { transform: translateX(330px); opacity: 1; }
      62%, 100% { transform: translateX(330px); opacity: 0; }
    }
    .fx-muons { animation: probe-muons 7s linear infinite; }
    @keyframes probe-muons {
      0%, 20% { transform: translateY(0); opacity: 0; }
      26%     { opacity: 1; }
      55%     { transform: translateY(-90px); opacity: 1; }
      90%     { transform: translateY(-90px); opacity: 1; }
      100%    { transform: translateY(-90px); opacity: 0; }
    }
    .fx-burst { animation: probe-burst 7s linear infinite; }
    @keyframes probe-burst {
      0%, 58% { transform: scale(0); opacity: 0; }
      64%     { opacity: 1; }
      78%     { transform: scale(1); opacity: 1; }
      92%     { transform: scale(1); opacity: 1; }
      100%    { transform: scale(1); opacity: 0; }
    }

    /* s5 TOF race loop: all three launch together at 5%; travel times are in
       ratio 2:3:4 (λ = 2/3/4 Å), matching the t = 5.1/7.6/10.1 ms stamps. */
    .fx-race1 { animation: race-1 8s linear infinite; }
    .fx-race2 { animation: race-2 8s linear infinite; }
    .fx-race3 { animation: race-3 8s linear infinite; }
    @keyframes race-1 {
      0%   { transform: translateX(0); opacity: 0; }
      4%   { opacity: 1; }
      5%   { transform: translateX(0); }
      45%  { transform: translateX(640px); }
      92%  { transform: translateX(640px); opacity: 1; }
      100% { transform: translateX(640px); opacity: 0; }
    }
    @keyframes race-2 {
      0%   { transform: translateX(0); opacity: 0; }
      4%   { opacity: 1; }
      5%   { transform: translateX(0); }
      65%  { transform: translateX(640px); }
      92%  { transform: translateX(640px); opacity: 1; }
      100% { transform: translateX(640px); opacity: 0; }
    }
    @keyframes race-3 {
      0%   { transform: translateX(0); opacity: 0; }
      4%   { opacity: 1; }
      5%   { transform: translateX(0); }
      85%  { transform: translateX(640px); }
      92%  { transform: translateX(640px); opacity: 1; }
      100% { transform: translateX(640px); opacity: 0; }
    }
    /* each arrival time is logged the moment its neutron lands */
    .race-stamps .stamp-1 { animation: stamp-1 8s linear infinite; }
    .race-stamps .stamp-2 { animation: stamp-2 8s linear infinite; }
    .race-stamps .stamp-3 { animation: stamp-3 8s linear infinite; }
    @keyframes stamp-1 { 0%, 45% { opacity: 0; } 48%, 92% { opacity: 1; } 100% { opacity: 0; } }
    @keyframes stamp-2 { 0%, 65% { opacity: 0; } 68%, 92% { opacity: 1; } 100% { opacity: 0; } }
    @keyframes stamp-3 { 0%, 85% { opacity: 0; } 88%, 92% { opacity: 1; } 100% { opacity: 0; } }

    /* (s6 detectors loop — neutron stream, tile flashes, log lines and the
       per-hit histogram build — is generated in story.js with WAAPI, since
       its ~30 actors and the tiles/bars themselves are JS-built.) */

    /* recap interlude: the whole chain in one 14 s loop — inject, 2.5
       accelerating laps, extraction, muons at the graphite, burst at the
       tungsten, into the moderator, then three wavelengths race the flight
       path (speed ratio 2:3:4, as in s5) and scatter onto the detectors. */
    .rc-p1 { animation: rc-p1 14s linear infinite; }
    @keyframes rc-p1 {
      0%, 1%    { transform: translateX(0); opacity: 0; }
      2.5%      { opacity: 1; }
      6%        { transform: translateX(64px); opacity: 1; }
      7%, 100%  { transform: translateX(64px); opacity: 0; }
    }
    .rc-p2 { animation: rc-p2 14s linear infinite; }
    @keyframes rc-p2 {
      0%, 5.5%  { transform: rotate(0.75turn); opacity: 0; }
      6.5%      { opacity: 1; }
      11%       { transform: rotate(1.15turn); }
      15%       { transform: rotate(1.6turn); }
      18.5%     { transform: rotate(2.15turn); }
      21.5%     { transform: rotate(2.75turn); }
      24%       { transform: rotate(3.25turn); opacity: 1; }
      25%, 100% { transform: rotate(3.25turn); opacity: 0; }
    }
    .rc-p3 { animation: rc-p3 14s linear infinite; }
    @keyframes rc-p3 {
      0%, 24%     { transform: translateX(0); opacity: 0; }
      25%         { opacity: 1; }
      30%         { transform: translateX(64px); }
      36.5%       { transform: translateX(137px); opacity: 1; }
      37.5%, 100% { transform: translateX(137px); opacity: 0; }
    }
    .rc-mu { animation: rc-mu 14s linear infinite; }
    @keyframes rc-mu {
      0%, 30%   { transform: translateY(0); opacity: 0; }
      32%       { opacity: 1; }
      41%       { transform: translateY(-62px); opacity: 1; }
      46%, 100% { transform: translateY(-78px); opacity: 0; }
    }
    .rc-burst { animation: rc-burst 14s linear infinite; }
    @keyframes rc-burst {
      0%, 36.5% { transform: scale(0); opacity: 0; }
      38%       { opacity: 1; }
      40.5%     { transform: scale(1); opacity: 1; }
      45%, 100% { transform: scale(1); opacity: 0; }
    }
    .rc-nf { animation: rc-nf 14s linear infinite; }
    @keyframes rc-nf {
      0%, 37.5%   { transform: translateX(0); opacity: 0; }
      38.5%       { opacity: 1; }
      44%         { transform: translateX(44px); opacity: 1; }
      46.5%, 100% { transform: translateX(44px); opacity: 0; }
    }
    .rc-r1 { animation: rc-r1 14s linear infinite; }
    .rc-r2 { animation: rc-r2 14s linear infinite; }
    .rc-r3 { animation: rc-r3 14s linear infinite; }
    @keyframes rc-r1 {
      0%, 49%   { transform: translate(0, 0); opacity: 0; }
      50.5%     { opacity: 1; }
      70%       { transform: translate(295px, 0); }
      71.5%     { transform: translate(345px, -22px); opacity: 1; }
      73%, 100% { transform: translate(345px, -22px); opacity: 0; }
    }
    @keyframes rc-r2 {
      0%, 49%   { transform: translate(0, 0); opacity: 0; }
      50.5%     { opacity: 1; }
      79%       { transform: translate(295px, 0); }
      80.5%     { transform: translate(357px, 0); opacity: 1; }
      82%, 100% { transform: translate(357px, 0); opacity: 0; }
    }
    @keyframes rc-r3 {
      0%, 49%   { transform: translate(0, 0); opacity: 0; }
      50.5%     { opacity: 1; }
      88%       { transform: translate(295px, 0); }
      89.5%     { transform: translate(345px, 22px); opacity: 1; }
      91%, 100% { transform: translate(345px, 22px); opacity: 0; }
    }
    .rc-d1 { animation: rc-d1 14s linear infinite; }
    .rc-d2 { animation: rc-d2 14s linear infinite; }
    .rc-d3 { animation: rc-d3 14s linear infinite; }
    @keyframes rc-d1 {
      0%, 71%   { filter: brightness(1); }
      72%       { filter: brightness(2.6); }
      76%, 100% { filter: brightness(1); }
    }
    @keyframes rc-d2 {
      0%, 80.5%   { filter: brightness(1); }
      81.5%       { filter: brightness(2.6); }
      85.5%, 100% { filter: brightness(1); }
    }
    @keyframes rc-d3 {
      0%, 89.5%   { filter: brightness(1); }
      90.5%       { filter: brightness(2.6); }
      94.5%, 100% { filter: brightness(1); }
    }

    /* s7 reduction loop: the chain lights up step by step while the same
       curve morphs in place — raw (instrument units, orange) → ConvertUnits
       slides the peaks into position → Rebin smooths the noise → Normalize
       flattens the baseline (and the data turns physical-units cyan) → Fit
       draws the orange model over the main peak. All states share one
       18-point polyline so `d` interpolates; browsers without CSS `d`
       interpolation keep the final curve and still get the chain/captions. */
    .pipe-data { animation: pipe-data 12s linear infinite; }
    @keyframes pipe-data {
      0%, 4% { d: path("M120,100 L136,88 L153,104 L169,92 L186,62 L202,78 L219,108 L235,96 L252,118 L268,104 L285,84 L301,120 L318,128 L334,142 L351,132 L367,152 L384,144 L400,160"); stroke: #ff8a5c; opacity: 0; }
      8%     { opacity: 1; }
      20%    { d: path("M120,100 L136,88 L153,104 L169,92 L186,62 L202,78 L219,108 L235,96 L252,118 L268,104 L285,84 L301,120 L318,128 L334,142 L351,132 L367,152 L384,144 L400,160"); stroke: #ff8a5c; }
      32%    { d: path("M120,102 L136,90 L153,100 L169,96 L186,104 L202,94 L219,58 L235,80 L252,110 L268,100 L285,116 L301,108 L318,86 L334,124 L351,136 L367,146 L384,140 L400,158"); }
      38%    { d: path("M120,102 L136,90 L153,100 L169,96 L186,104 L202,94 L219,58 L235,80 L252,110 L268,100 L285,116 L301,108 L318,86 L334,124 L351,136 L367,146 L384,140 L400,158"); }
      48%    { d: path("M120,100 L136,97 L153,99 L169,98 L186,99 L202,88 L219,62 L235,84 L252,104 L268,106 L285,110 L301,104 L318,90 L334,122 L351,132 L367,142 L384,148 L400,155"); stroke: #ff8a5c; }
      54%    { d: path("M120,100 L136,97 L153,99 L169,98 L186,99 L202,88 L219,62 L235,84 L252,104 L268,106 L285,110 L301,104 L318,90 L334,122 L351,132 L367,142 L384,148 L400,155"); }
      64%    { d: path("M120,160 L136,159 L153,160 L169,158 L186,159 L202,130 L219,78 L235,118 L252,157 L268,158 L285,159 L301,148 L318,112 L334,150 L351,158 L367,159 L384,158 L400,160"); stroke: #4fd8eb; }
      93%    { d: path("M120,160 L136,159 L153,160 L169,158 L186,159 L202,130 L219,78 L235,118 L252,157 L268,158 L285,159 L301,148 L318,112 L334,150 L351,158 L367,159 L384,158 L400,160"); stroke: #4fd8eb; opacity: 1; }
      100%   { d: path("M120,160 L136,159 L153,160 L169,158 L186,159 L202,130 L219,78 L235,118 L252,157 L268,158 L285,159 L301,148 L318,112 L334,150 L351,158 L367,159 L384,158 L400,160"); stroke: #4fd8eb; opacity: 0; }
    }
    /* the raw ghost fades in once the data starts leaving it behind */
    .pipe-ghost { animation: pipe-ghost 12s linear infinite; }
    @keyframes pipe-ghost {
      0%, 20%   { opacity: 0; }
      28%, 90%  { opacity: 0.25; }
      97%, 100% { opacity: 0; }
    }
    /* the fit draws on during the Fit step (pathLength=1 + dash trick) */
    .pipe-fit { animation: pipe-fit 12s linear infinite; }
    @keyframes pipe-fit {
      0%, 70%  { stroke-dashoffset: 1; opacity: 1; }
      80%, 93% { stroke-dashoffset: 0; opacity: 1; }
      100%     { stroke-dashoffset: 0; opacity: 0; }
    }
    /* chain steps highlight in sync */
    .pipe-chain .pc-1 { animation: pc-1 12s linear infinite; }
    .pipe-chain .pc-2 { animation: pc-2 12s linear infinite; }
    .pipe-chain .pc-3 { animation: pc-3 12s linear infinite; }
    .pipe-chain .pc-4 { animation: pc-4 12s linear infinite; }
    .pipe-chain .pc-5 { animation: pc-5 12s linear infinite; }
    @keyframes pc-1 { 0%, 2% { fill: #586a9e; } 5%, 18% { fill: #4fd8eb; } 23%, 100% { fill: #586a9e; } }
    @keyframes pc-2 { 0%, 20% { fill: #586a9e; } 24%, 32% { fill: #4fd8eb; } 37%, 100% { fill: #586a9e; } }
    @keyframes pc-3 { 0%, 36% { fill: #586a9e; } 40%, 48% { fill: #4fd8eb; } 53%, 100% { fill: #586a9e; } }
    @keyframes pc-4 { 0%, 52% { fill: #586a9e; } 56%, 64% { fill: #4fd8eb; } 69%, 100% { fill: #586a9e; } }
    @keyframes pc-5 { 0%, 68% { fill: #586a9e; } 72%, 90% { fill: #ffb84d; } 95%, 100% { fill: #586a9e; } }
    /* units caption crossfades at Normalize */
    .pu-1 { animation: pu-1 12s linear infinite; }
    .pu-2 { animation: pu-2 12s linear infinite; }
    @keyframes pu-1 { 0%, 4% { opacity: 0; } 8%, 50% { opacity: 1; } 56%, 100% { opacity: 0; } }
    @keyframes pu-2 { 0%, 52% { opacity: 0; } 58%, 93% { opacity: 1; } 100% { opacity: 0; } }
    /* detector tiles: staggered fade-in when scene in view (histogram bars
       are driven per-hit by the story.js WAAPI loop instead) */
    body.js-anim #s6 .det-tile { opacity: 0.25; transition: opacity 0.3s ease; }
    body.js-anim #s6 .in-view .det-tile,
    body.js-anim #s6.in-view .det-tile { opacity: 1; transition-delay: calc(var(--i) * 0.12s); }
  }

}
