/* Capstone — How AI helps us deliver
   30-second hero animation. Composes Scene components inside a <Stage>.
   Brand tokens: navy = #003070, sun = #F5B622, coral = #E96A4D
*/

// ---------- Brand tokens ----------
const NAVY = '#003070';
const NAVY_DARK = '#001A40';
const NAVY_LIGHT = '#0050A0';
const SUN = '#F5B622';
const CORAL = '#E96A4D';
const WHITE = '#FFFFFF';
const MIST = 'rgba(255,255,255,0.08)';
const DISPLAY_FONT = 'Montserrat, system-ui, sans-serif';
const MONO_FONT = 'JetBrains Mono, ui-monospace, monospace';

// ---------- Reusable bits ----------

// Subtle grid backdrop with vignette
function GridBackdrop({ opacity = 0.06 }) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      backgroundImage: `
        linear-gradient(rgba(255,255,255,${opacity}) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,${opacity}) 1px, transparent 1px)
      `,
      backgroundSize: '64px 64px',
      maskImage: 'radial-gradient(ellipse at center, #000 35%, transparent 75%)',
      WebkitMaskImage: 'radial-gradient(ellipse at center, #000 35%, transparent 75%)',
      pointerEvents: 'none',
    }} />
  );
}

// A pulsing dot used for "LIVE" indicators
function PulseDot({ size = 14, color = SUN, x, y }) {
  const time = useTime();
  const phase = (time % 1.8) / 1.8; // 1.8s cycle
  const scale = 1 + Easing.easeOutQuad(phase) * 1.4;
  const opacity = 1 - phase;
  return (
    <div style={{ position: 'absolute', left: x, top: y, width: size, height: size }}>
      <div style={{
        position: 'absolute', inset: 0,
        borderRadius: '50%',
        background: color,
        transform: `scale(${scale})`,
        opacity,
      }} />
      <div style={{
        position: 'absolute', inset: 0,
        borderRadius: '50%',
        background: color,
        boxShadow: `0 0 18px ${color}`,
      }} />
    </div>
  );
}

// CountUp number — eased from 0 to target over the sprite's duration window
function CountUp({ to, suffix = '', prefix = '', start = 0, dur = 1.2, ...style }) {
  const { localTime } = useSprite();
  const elapsed = Math.max(0, localTime - start);
  const t = clamp(elapsed / dur, 0, 1);
  const eased = Easing.easeOutCubic(t);
  const value = Math.round(eased * to);
  return (
    <div style={{
      fontFamily: DISPLAY_FONT,
      fontWeight: 800,
      letterSpacing: '-0.03em',
      lineHeight: 1,
      fontVariantNumeric: 'tabular-nums',
      ...style,
    }}>
      {prefix}{value}{suffix}
    </div>
  );
}

// Camera wrapper — animates origin transform on its children
function Camera({ scale = 1, x = 0, y = 0, children }) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      transform: `translate(${x}px, ${y}px) scale(${scale})`,
      transformOrigin: 'center center',
      willChange: 'transform',
    }}>
      {children}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────
// SCENE 1 — Hook (0–3s)
// "We built it before we taught it." — bold, navy, sun-yellow accent
// ─────────────────────────────────────────────────────────────────
function SceneHook() {
  return (
    <Sprite start={0} end={3.2}>
      {({ localTime }) => {
        // Line entry timings
        const line1At = 0.15;
        const line2At = 0.6;
        const line3At = 1.05;
        const opacity = (t) => Easing.easeOutCubic(clamp(t, 0, 1));
        const slide  = (t) => (1 - opacity(t)) * 30;

        const t1 = (localTime - line1At) / 0.5;
        const t2 = (localTime - line2At) / 0.5;
        const t3 = (localTime - line3At) / 0.6;

        // Camera slowly pulls in over the whole 3s
        const camScale = 1 + (localTime / 3.2) * 0.04;

        // Whole scene fades out in last 0.4s
        const exitT = clamp((localTime - 2.8) / 0.4, 0, 1);
        const groupOpacity = 1 - Easing.easeInCubic(exitT);

        return (
          <Camera scale={camScale}>
            <div style={{ position: 'absolute', inset: 0, opacity: groupOpacity }}>
              <div style={{
                position: 'absolute',
                left: '50%', top: '50%',
                transform: 'translate(-50%, -50%)',
                textAlign: 'center',
                lineHeight: 0.94,
              }}>
                <div style={{
                  fontFamily: DISPLAY_FONT, fontWeight: 800,
                  fontSize: 156, letterSpacing: '-0.035em',
                  color: WHITE,
                  opacity: opacity(t1),
                  transform: `translateY(${slide(t1)}px)`,
                }}>WE BUILT IT</div>
                <div style={{
                  fontFamily: DISPLAY_FONT, fontWeight: 800,
                  fontSize: 156, letterSpacing: '-0.035em',
                  color: WHITE,
                  opacity: opacity(t2),
                  transform: `translateY(${slide(t2)}px)`,
                  marginTop: 8,
                }}>BEFORE WE</div>
                <div style={{
                  fontFamily: DISPLAY_FONT, fontWeight: 800,
                  fontSize: 156, letterSpacing: '-0.035em',
                  color: SUN,
                  opacity: opacity(t3),
                  transform: `translateY(${slide(t3)}px)`,
                  marginTop: 8,
                  filter: `drop-shadow(0 0 24px rgba(245,182,34,${0.4 * opacity(t3)}))`,
                }}>TAUGHT IT.</div>
              </div>
            </div>
          </Camera>
        );
      }}
    </Sprite>
  );
}

// ─────────────────────────────────────────────────────────────────
// SCENE 2 — The Problem (3–7s)
// Notifications piling up; clock ticks forward
// ─────────────────────────────────────────────────────────────────
function NotificationCard({ index, label, sender, time, color = WHITE, sceneT }) {
  // Each card slides in slightly delayed from the previous
  const appearAt = 0.2 + index * 0.22;
  const t = clamp((sceneT - appearAt) / 0.5, 0, 1);
  const eased = Easing.easeOutBack(t);
  const opacity = clamp(t * 1.5, 0, 1);

  // Cards stack from bottom-up, each offset by 18px and slight x jitter
  const baseY = 60 - index * 78;
  const x = (index % 2 === 0 ? -8 : 6) + (1 - eased) * 60;

  return (
    <div style={{
      position: 'absolute',
      right: 80,
      bottom: baseY,
      width: 460,
      padding: '14px 18px',
      background: 'rgba(255,255,255,0.96)',
      border: '1px solid rgba(0,0,0,0.08)',
      borderRadius: 10,
      boxShadow: '0 12px 36px rgba(0,0,0,0.35)',
      transform: `translate(${x}px, 0)`,
      opacity,
      display: 'flex', gap: 12, alignItems: 'flex-start',
      fontFamily: DISPLAY_FONT,
    }}>
      <div style={{
        width: 36, height: 36, borderRadius: 8,
        background: color, flexShrink: 0,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: WHITE, fontWeight: 800, fontSize: 16,
      }}>{sender[0]}</div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          display: 'flex', justifyContent: 'space-between',
          fontSize: 13, color: '#222',
        }}>
          <span style={{ fontWeight: 700 }}>{sender}</span>
          <span style={{ color: '#888', fontFamily: MONO_FONT, fontSize: 11 }}>{time}</span>
        </div>
        <div style={{
          fontSize: 14, color: '#444', marginTop: 4, lineHeight: 1.35,
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        }}>{label}</div>
      </div>
    </div>
  );
}

function SceneProblem() {
  return (
    <Sprite start={3.2} end={7.4}>
      {({ localTime, duration }) => {
        // Clock simulation: morning ticks 7:14 → 8:42
        const clockT = clamp(localTime / 2.8, 0, 1);
        const hh = 7 + Math.floor(clockT * 1.5); // 7 → 8
        const mm = Math.floor((clockT * 88) % 60); // jumps quickly
        const timeStr = `${String(Math.min(hh, 8)).padStart(2, '0')}:${String(mm).padStart(2, '0')}`;

        const exitT = clamp((localTime - (duration - 0.6)) / 0.6, 0, 1);
        const groupOpacity = 1 - Easing.easeInCubic(exitT);

        // Subtle camera pull toward right (toward the notification pile)
        const camX = -40 * Easing.easeOutQuad(clamp(localTime / 3, 0, 1));

        const inquiries = [
          { sender: 'Sarah Boyd',  label: 'RE: Workshop next month',     time: '7:14',  color: '#E96A4D' },
          { sender: 'WhatsApp',    label: 'Hi! Interested in your AI…',   time: '7:31',  color: '#25D366' },
          { sender: 'Jamie Tan',   label: 'Proposal request — 30 ppl',    time: '7:52',  color: '#0050A0' },
          { sender: 'Mercer Marsh',label: 'Quote for leadership prog…',   time: '8:08',  color: '#003070' },
          { sender: 'WhatsApp',    label: 'Quick question about pricing', time: '8:24',  color: '#25D366' },
          { sender: 'AIA L&D',     label: 'Customer service follow-up',   time: '8:42',  color: '#C8102E' },
        ];

        return (
          <Camera x={camX}>
            <div style={{ position: 'absolute', inset: 0, opacity: groupOpacity }}>
              {/* Side caption */}
              <div style={{
                position: 'absolute',
                left: 96, top: 320, maxWidth: 620,
                opacity: clamp(localTime / 0.6, 0, 1) - exitT,
              }}>
                <div style={{
                  fontFamily: MONO_FONT, fontSize: 22,
                  letterSpacing: '0.15em', textTransform: 'uppercase',
                  color: SUN, marginBottom: 22,
                }}>{timeStr} &middot; MONDAY</div>
                <div style={{
                  fontFamily: DISPLAY_FONT, fontWeight: 800,
                  fontSize: 78, lineHeight: 0.98,
                  letterSpacing: '-0.025em',
                  color: WHITE,
                }}>The inbox has been<br />busy <span style={{ color: CORAL }}>since 5am.</span></div>
                <div style={{
                  fontFamily: DISPLAY_FONT, fontWeight: 500,
                  fontSize: 22, lineHeight: 1.45,
                  color: 'rgba(255,255,255,0.7)',
                  marginTop: 24, maxWidth: 540,
                }}>For most training firms in Singapore, this is when the day starts.</div>
              </div>

              {/* Notification pile (bottom-right) */}
              {inquiries.map((q, i) => (
                <NotificationCard key={i} index={i} {...q} sceneT={localTime} />
              ))}
            </div>
          </Camera>
        );
      }}
    </Sprite>
  );
}

// ─────────────────────────────────────────────────────────────────
// SCENE 3 — Today at Capstone (7.4–14.5s)
// The productivity panel materializes; numbers tick up row by row
// ─────────────────────────────────────────────────────────────────
function PanelRow({ index, num, label, tag, sceneT, panelStart }) {
  // Row appears in sequence
  const rowAppearAt = panelStart + 0.6 + index * 0.45;
  const rowProgress = clamp((sceneT - rowAppearAt) / 0.4, 0, 1);
  const opacity = Easing.easeOutCubic(rowProgress);
  const ty = (1 - opacity) * 14;

  // Count-up starts when row enters
  const countDur = 0.7;
  const countT = clamp((sceneT - rowAppearAt - 0.05) / countDur, 0, 1);
  const value = Math.round(Easing.easeOutCubic(countT) * num);

  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: '120px 1fr auto',
      alignItems: 'baseline',
      gap: 28,
      padding: '28px 0',
      borderBottom: index < 3 ? '1px dashed rgba(0,30,70,0.16)' : 'none',
      opacity, transform: `translateY(${ty}px)`,
    }}>
      <div style={{
        fontFamily: DISPLAY_FONT, fontWeight: 800,
        fontSize: 90, color: NAVY,
        letterSpacing: '-0.04em', lineHeight: 1,
        textAlign: 'right',
        fontVariantNumeric: 'tabular-nums',
      }}>{value}</div>
      <div>
        <div style={{
          fontFamily: DISPLAY_FONT, fontWeight: 700,
          fontSize: 28, color: NAVY,
          letterSpacing: '-0.005em',
        }}>{label[0]}</div>
        <div style={{
          fontFamily: DISPLAY_FONT, fontWeight: 500,
          fontSize: 22, color: '#555',
          marginTop: 4,
        }}>{label[1]}</div>
      </div>
      <div style={{
        fontFamily: MONO_FONT, fontSize: 18,
        color: '#4FA978', letterSpacing: '0.04em',
        fontWeight: 600,
      }}>{tag}</div>
    </div>
  );
}

function ScenePanel() {
  return (
    <Sprite start={7.0} end={14.5}>
      {({ localTime, duration }) => {
        // The panel slides up from below
        const panelT = clamp(localTime / 0.7, 0, 1);
        const panelEased = Easing.easeOutCubic(panelT);
        const panelY = (1 - panelEased) * 80;
        const panelOpacity = panelEased;

        const exitT = clamp((localTime - (duration - 0.7)) / 0.7, 0, 1);
        const groupOpacity = 1 - Easing.easeInCubic(exitT);

        // Camera slowly drifts in toward the footer (where the "9 hrs" lives)
        const camScale = 1 + (localTime / duration) * 0.08;
        const camY = -(localTime / duration) * 30;

        const rows = [
          { num: 47, label: ['Client inquiries answered', 'WhatsApp · web · email — first response in seconds'], tag: '24/7' },
          { num: 3,  label: ['Proposals drafted',          'Ready for human review and finalisation'],            tag: '~10 min each' },
          { num: 12, label: ['Pieces of content scheduled','TikTok, LinkedIn & Facebook queue, on brand'],         tag: 'Auto-pub' },
          { num: 8,  label: ['Invoices & expenses reconciled','Captured from email, logged, ready for sign-off'], tag: 'Hands-off' },
        ];

        // Footer reveal — last 1s of scene
        const footerAt = 3.0;
        const footerT = clamp((localTime - footerAt) / 0.6, 0, 1);
        const footerOpacity = Easing.easeOutCubic(footerT);

        return (
          <Camera scale={camScale} y={camY}>
            <div style={{ position: 'absolute', inset: 0, opacity: groupOpacity }}>
              <div style={{
                position: 'absolute',
                left: '50%', top: '50%',
                transform: `translate(-50%, calc(-50% + ${panelY}px))`,
                width: 1280,
                background: WHITE,
                border: '1px solid rgba(0,0,0,0.06)',
                borderRadius: 24,
                boxShadow: '0 32px 80px rgba(0,15,50,0.55), 0 4px 16px rgba(0,0,0,0.3)',
                overflow: 'hidden',
                opacity: panelOpacity,
              }}>
                {/* Header */}
                <div style={{
                  background: 'rgba(0,48,112,0.04)',
                  padding: '22px 36px',
                  borderBottom: '1px solid rgba(0,0,0,0.06)',
                  display: 'flex', alignItems: 'center', gap: 16,
                }}>
                  <div>
                    <div style={{
                      fontFamily: DISPLAY_FONT, fontWeight: 800,
                      fontSize: 26, color: NAVY,
                      letterSpacing: '-0.005em', lineHeight: 1.1,
                    }}>Today at Capstone</div>
                    <div style={{
                      fontFamily: MONO_FONT, fontSize: 14,
                      color: '#888', marginTop: 5,
                    }}>A typical day &middot; AI-handled</div>
                  </div>
                  <div style={{
                    marginLeft: 'auto',
                    display: 'inline-flex', alignItems: 'center', gap: 10,
                    fontFamily: DISPLAY_FONT, fontWeight: 700,
                    fontSize: 14, letterSpacing: '0.18em',
                    textTransform: 'uppercase',
                    color: '#4FA978',
                  }}>
                    <div style={{
                      width: 12, height: 12, borderRadius: 6, background: '#4FA978',
                      boxShadow: '0 0 8px rgba(79,169,120,0.5)',
                    }} />
                    LIVE
                  </div>
                </div>

                {/* Rows */}
                <div style={{ padding: '10px 36px 14px' }}>
                  {rows.map((r, i) => (
                    <PanelRow key={i} index={i} {...r} sceneT={localTime} panelStart={0} />
                  ))}
                </div>

                {/* Footer */}
                <div style={{
                  background: NAVY,
                  color: WHITE,
                  padding: '24px 36px',
                  display: 'flex', alignItems: 'center', gap: 22,
                  opacity: footerOpacity,
                  fontFamily: DISPLAY_FONT,
                }}>
                  <div style={{
                    fontWeight: 800, fontSize: 38,
                    letterSpacing: '-0.02em', color: SUN, lineHeight: 1,
                  }}>≈ 9 hrs</div>
                  <div style={{
                    fontSize: 22, lineHeight: 1.35, fontWeight: 500,
                  }}>
                    of human work, automated before lunch.<br />
                    <span style={{ color: SUN, fontWeight: 700 }}>We'll show your team how.</span>
                  </div>
                </div>
              </div>
            </div>
          </Camera>
        );
      }}
    </Sprite>
  );
}

// ─────────────────────────────────────────────────────────────────
// SCENE 4 — Punchline zoom (14.5–18.5s)
// "≈ 9 hours of human work, automated before lunch."
// ─────────────────────────────────────────────────────────────────
function ScenePunchline() {
  return (
    <Sprite start={14.5} end={18.6}>
      {({ localTime, duration }) => {
        // Massive text scales in from 0.7 → 1
        const headlineT = clamp(localTime / 0.6, 0, 1);
        const headlineScale = 0.85 + Easing.easeOutCubic(headlineT) * 0.15;
        const headlineOpacity = Easing.easeOutCubic(headlineT);

        // Subline at 0.6s
        const sublineT = clamp((localTime - 0.6) / 0.6, 0, 1);
        const sublineOpacity = Easing.easeOutCubic(sublineT);
        const sublineTy = (1 - sublineOpacity) * 24;

        const exitT = clamp((localTime - (duration - 0.5)) / 0.5, 0, 1);
        const groupOpacity = 1 - Easing.easeInCubic(exitT);

        // Camera barely drifts — small ken-burns
        const camScale = 1 + (localTime / duration) * 0.03;

        return (
          <Camera scale={camScale}>
            <div style={{ position: 'absolute', inset: 0, opacity: groupOpacity }}>
              <div style={{
                position: 'absolute',
                left: '50%', top: '50%',
                transform: 'translate(-50%, -50%)',
                textAlign: 'center',
                width: 1600,
              }}>
                <div style={{
                  fontFamily: DISPLAY_FONT, fontWeight: 800,
                  fontSize: 280, letterSpacing: '-0.045em', lineHeight: 0.9,
                  color: SUN,
                  opacity: headlineOpacity,
                  transform: `scale(${headlineScale})`,
                  transformOrigin: 'center',
                  filter: `drop-shadow(0 0 36px rgba(245,182,34,${0.45 * headlineOpacity}))`,
                }}>≈ 9 HOURS</div>
                <div style={{
                  marginTop: 28,
                  fontFamily: DISPLAY_FONT, fontWeight: 500,
                  fontSize: 56, color: WHITE,
                  lineHeight: 1.15,
                  opacity: sublineOpacity,
                  transform: `translateY(${sublineTy}px)`,
                  letterSpacing: '-0.01em',
                }}>of human work, automated <span style={{ color: SUN, fontWeight: 700 }}>before lunch.</span></div>
              </div>
            </div>
          </Camera>
        );
      }}
    </Sprite>
  );
}

// ─────────────────────────────────────────────────────────────────
// SCENE 5 — The pivot (18.6–25s)
// "We don't just teach AI. We run it." + Programme cards
// ─────────────────────────────────────────────────────────────────
function ProgrammeCard({ index, name, level, days, sceneT, cardsStart, accent }) {
  const appearAt = cardsStart + index * 0.25;
  const t = clamp((sceneT - appearAt) / 0.55, 0, 1);
  const eased = Easing.easeOutCubic(t);
  const opacity = Easing.easeOutCubic(t);
  const ty = (1 - eased) * 40;

  return (
    <div style={{
      width: 580,
      background: WHITE,
      borderLeft: `6px solid ${accent}`,
      borderRadius: 14,
      padding: '36px 40px',
      boxShadow: '0 24px 60px rgba(0,15,50,0.4)',
      opacity, transform: `translateY(${ty}px)`,
    }}>
      <div style={{
        display: 'flex', gap: 10, alignItems: 'center',
        marginBottom: 18,
      }}>
        <span style={{
          fontFamily: DISPLAY_FONT, fontWeight: 800,
          fontSize: 14, letterSpacing: '0.18em', textTransform: 'uppercase',
          color: NAVY,
          padding: '6px 12px', borderRadius: 999,
          background: 'rgba(245,182,34,0.18)',
        }}>{level}</span>
        <span style={{
          fontFamily: MONO_FONT, fontSize: 14,
          color: '#888', letterSpacing: '0.05em',
        }}>{days}</span>
      </div>
      <div style={{
        fontFamily: DISPLAY_FONT, fontWeight: 800,
        fontSize: 56, color: NAVY,
        letterSpacing: '-0.025em', lineHeight: 1.0,
      }}>{name}</div>
      <div style={{
        marginTop: 18,
        fontFamily: DISPLAY_FONT, fontWeight: 500,
        fontSize: 22, color: '#444', lineHeight: 1.4,
      }}>
        You'll leave with AI <strong style={{ color: accent, fontWeight: 700 }}>deployed</strong>.
      </div>
    </div>
  );
}

function ScenePivot() {
  return (
    <Sprite start={18.6} end={25.0}>
      {({ localTime, duration }) => {
        // Headline
        const head1T = clamp(localTime / 0.55, 0, 1);
        const head1O = Easing.easeOutCubic(head1T);
        const head1Y = (1 - head1O) * 24;

        const head2T = clamp((localTime - 0.55) / 0.55, 0, 1);
        const head2O = Easing.easeOutCubic(head2T);
        const head2Y = (1 - head2O) * 24;

        const cardsStart = 1.4;

        const exitT = clamp((localTime - (duration - 0.6)) / 0.6, 0, 1);
        const groupOpacity = 1 - Easing.easeInCubic(exitT);

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: groupOpacity }}>
            {/* Headline */}
            <div style={{
              position: 'absolute',
              top: 130, left: 0, right: 0, textAlign: 'center',
            }}>
              <div style={{
                fontFamily: DISPLAY_FONT, fontWeight: 800,
                fontSize: 88, color: WHITE,
                letterSpacing: '-0.025em', lineHeight: 1.02,
                opacity: head1O,
                transform: `translateY(${head1Y}px)`,
              }}>We don't just teach AI.</div>
              <div style={{
                marginTop: 6,
                fontFamily: DISPLAY_FONT, fontWeight: 800,
                fontSize: 132, color: SUN,
                letterSpacing: '-0.035em', lineHeight: 1.0,
                opacity: head2O,
                transform: `translateY(${head2Y}px)`,
                filter: `drop-shadow(0 0 26px rgba(245,182,34,${0.4 * head2O}))`,
              }}>We run it.</div>
            </div>

            {/* Cards */}
            <div style={{
              position: 'absolute',
              left: '50%', bottom: 110,
              transform: 'translateX(-50%)',
              display: 'flex', gap: 36,
            }}>
              <ProgrammeCard index={0} name="AI Launchpad"            level="Foundational" days="2 days · AI-001" accent={SUN} sceneT={localTime} cardsStart={cardsStart} />
              <ProgrammeCard index={1} name="Agentic AI Accelerator"  level="Hands-on"     days="1 day · AI-002"  accent={CORAL} sceneT={localTime} cardsStart={cardsStart} />
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ─────────────────────────────────────────────────────────────────
// SCENE 6 — Closer (25–30s)
// "You'll leave with AI deployed. Not just learned." + Capstone wordmark
// ─────────────────────────────────────────────────────────────────
function SceneCloser() {
  return (
    <Sprite start={25.0} end={30}>
      {({ localTime, duration }) => {
        // Line 1
        const line1T = clamp(localTime / 0.55, 0, 1);
        const line1O = Easing.easeOutCubic(line1T);
        const line1Y = (1 - line1O) * 24;

        // Line 2 (sun-yellow emphasis)
        const line2T = clamp((localTime - 0.5) / 0.55, 0, 1);
        const line2O = Easing.easeOutCubic(line2T);
        const line2Y = (1 - line2O) * 24;

        // Wordmark + tagline
        const wmT = clamp((localTime - 1.6) / 0.6, 0, 1);
        const wmO = Easing.easeOutCubic(wmT);
        const wmScale = 0.92 + wmO * 0.08;

        // Tagline appears slightly after wordmark
        const tagT = clamp((localTime - 2.4) / 0.5, 0, 1);
        const tagO = Easing.easeOutCubic(tagT);

        // Whole scene gently zooms in
        const camScale = 1 + (localTime / duration) * 0.03;

        return (
          <Camera scale={camScale}>
            <div style={{ position: 'absolute', inset: 0 }}>
              {/* Lines */}
              <div style={{
                position: 'absolute',
                top: 200, left: 0, right: 0, textAlign: 'center',
              }}>
                <div style={{
                  fontFamily: DISPLAY_FONT, fontWeight: 800,
                  fontSize: 92, color: WHITE,
                  letterSpacing: '-0.025em', lineHeight: 1.02,
                  opacity: line1O,
                  transform: `translateY(${line1Y}px)`,
                }}>You'll leave with AI <span style={{ color: SUN }}>deployed.</span></div>
                <div style={{
                  marginTop: 18,
                  fontFamily: DISPLAY_FONT, fontWeight: 600,
                  fontSize: 48, color: 'rgba(255,255,255,0.7)',
                  letterSpacing: '-0.015em',
                  opacity: line2O,
                  transform: `translateY(${line2Y}px)`,
                }}>Not just learned.</div>
              </div>

              {/* Wordmark */}
              <div style={{
                position: 'absolute',
                left: '50%', bottom: 240,
                transform: `translate(-50%, 0) scale(${wmScale})`,
                opacity: wmO,
                textAlign: 'center',
              }}>
                <div style={{
                  fontFamily: DISPLAY_FONT, fontWeight: 800,
                  fontSize: 88, color: WHITE,
                  letterSpacing: '0.04em', lineHeight: 1,
                }}>
                  CAPSTONE&nbsp;<span style={{ color: SUN, fontWeight: 600, fontSize: 66 }}>consulting</span>
                </div>
              </div>

              {/* Tagline */}
              <div style={{
                position: 'absolute',
                left: '50%', bottom: 160,
                transform: `translateX(-50%) translateY(${(1 - tagO) * 12}px)`,
                opacity: tagO,
                fontFamily: DISPLAY_FONT, fontWeight: 700,
                fontSize: 22, color: SUN,
                letterSpacing: '0.22em', textTransform: 'uppercase',
              }}>A cut above the rest.</div>

              {/* URL */}
              <div style={{
                position: 'absolute',
                left: '50%', bottom: 100,
                transform: 'translateX(-50%)',
                opacity: tagO * 0.7,
                fontFamily: MONO_FONT, fontSize: 18,
                color: 'rgba(255,255,255,0.55)',
                letterSpacing: '0.08em',
              }}>capstoneconsulting.com.sg</div>
            </div>
          </Camera>
        );
      }}
    </Sprite>
  );
}

// ─────────────────────────────────────────────────────────────────
// Background — a navy gradient that subtly drifts across all scenes
// ─────────────────────────────────────────────────────────────────
function GlobalBackground() {
  const time = useTime();
  // Drifting radial highlight
  const driftX = 30 + Math.sin(time * 0.4) * 8;
  const driftY = 25 + Math.cos(time * 0.5) * 6;
  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: `
        radial-gradient(ellipse at ${driftX}% ${driftY}%, rgba(245,182,34,0.16) 0%, transparent 50%),
        radial-gradient(ellipse at 70% 80%, rgba(0,80,160,0.45) 0%, transparent 60%),
        linear-gradient(135deg, ${NAVY_DARK} 0%, ${NAVY} 60%, ${NAVY_LIGHT} 100%)
      `,
    }}>
      <GridBackdrop opacity={0.05} />
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────
// Master scene timestamp tracker — updates data-screen-label on root
// ─────────────────────────────────────────────────────────────────
function TimestampTracker() {
  const time = useTime();
  React.useEffect(() => {
    const root = document.querySelector('[data-video-root]');
    if (root) {
      const t = time.toFixed(1);
      root.setAttribute('data-screen-label', `t=${t}s`);
    }
  }, [Math.floor(time * 2)]); // update every 0.5s
  return null;
}

// ─────────────────────────────────────────────────────────────────
// Master composition
// ─────────────────────────────────────────────────────────────────
function HeroVideo() {
  return (
    <Stage
      width={1920}
      height={1080}
      duration={30}
      background={NAVY_DARK}
      loop={true}
      autoplay={true}
      persistKey="capstone-hero-video"
    >
      <GlobalBackground />
      <TimestampTracker />

      <SceneHook />
      <SceneProblem />
      <ScenePanel />
      <ScenePunchline />
      <ScenePivot />
      <SceneCloser />
    </Stage>
  );
}

// Expose to global scope
window.HeroVideo = HeroVideo;
