// scroll-scene.jsx
// Static iPhone, screenshots scroll/slide/zoom through the screen.
// ~79s (~1:19) total — durations proportional to image height.

const CANVAS_W = 1080;
const CANVAS_H = 1920;

const PHONE_W  = 430;
const PHONE_H  = 930;
const BEZEL    = 14;
const SCREEN_W = PHONE_W - BEZEL * 2;   // 402
const SCREEN_H = PHONE_H - BEZEL * 2;   // 902

const XFADE = 0.5;   // crossfade overlap between segments (seconds)
const INTRO  = 1.5;
const OUTRO  = 1.5;

// ─── Segments ────────────────────────────────────────────────────────────────
// Logical user journey: Home → Program → Navigate → Network → Exhibitors/Sponsors → Engage
// Durations are proportional to pixel height (min 4s).
// Sessions_AND_My_Agenda is a 6×6 composite grid → treated as single-screen effective height.

const SEGMENTS = [
  // ── Home ──────────────────────────────────────────────────────────────────
  { src: 'screenshot.png',                 label: 'App Overview',        subtitle: 'Give every attendee one place for agenda, maps, people & updates',     type: 'scroll', dur: 15,   nW: 1125, nH: 9777, accent: '#ff6b4a' },

  // ── Programme ─────────────────────────────────────────────────────────────
  { src: 'Sessions_AND_My_Agenda.png',     label: 'Sessions & Agenda',    subtitle: 'Let attendees build their own schedule so they never miss a session',  type: 'cover',  dur:  4,   nW: 4500, nH: 9744, accent: '#ffc24a' },
  { src: 'InteractiveFloorMaps.png',       label: 'Floor Maps',           subtitle: 'Eliminate floor confusion — attendees self-navigate on day one',       type: 'slide',  dur:  4,   nW:  750, nH: 1624, accent: '#4af4a0', dir:  1 },

  // ── Networking ────────────────────────────────────────────────────────────
  { src: 'Attendees.png',                  label: 'Attendees',            subtitle: 'Turn a crowd into a connected community before day one even starts',   type: 'scroll', dur:  6,   nW:  750, nH: 3248, accent: '#ff9a6e' },
  { src: 'Meetings.png',                   label: 'Meetings',             subtitle: 'Facilitate hundreds of quality 1:1 meetings with zero manual effort',  type: 'scroll', dur:  7,   nW:  750, nH: 3804, accent: '#4a9dff' },
  { src: 'Event_Community.png',            label: 'Event Community',      subtitle: 'Keep conversations and energy alive well beyond the closing keynote',  type: 'scroll', dur:  4.5, nW:  750, nH: 2446, accent: '#4ae4ff' },
  { src: 'Private_social_network.png',     label: 'Social Network',       subtitle: 'A private branded space your attendees keep returning to year-round',  type: 'scroll', dur:  8,   nW:  750, nH: 4336, accent: '#ff4a8e' },

  // ── Exhibitors & Sponsors ─────────────────────────────────────────────────
  { src: 'Highlight_Exhibitors.png',       label: 'Exhibitors',           subtitle: 'Drive qualified booth traffic for every exhibiting partner',           type: 'slide',  dur:  4,   nW:  750, nH: 1624, accent: '#ffb84a', dir: -1 },
  { src: 'Sponsor_Profile.png',            label: 'Sponsor Profiles',     subtitle: 'Prove sponsor ROI with rich digital profiles and real-time analytics', type: 'scroll', dur: 13.5, nW:  750, nH: 7406, accent: '#b85cff' },
  { src: 'LeadRetrival.png',               label: 'Lead Retrieval',       subtitle: 'Exhibitors scan a badge and walk away with a qualified lead',          type: 'slide',  dur:  4,   nW:  750, nH: 1624, accent: '#ff6b4a', dir:  1 },

  // ── Engagement ────────────────────────────────────────────────────────────
  { src: 'Instant_Push_Notifications.png', label: 'Push Notifications',   subtitle: 'Reach every attendee in real time — no email open rates to worry about', type: 'scroll', dur:  4,   nW:  375, nH: 1803, accent: '#a0e040' },
  { src: 'Gamification.png',               label: 'Gamification',         subtitle: 'Turn passive audiences into active participants with points & rewards', type: 'slide',  dur:  4,   nW:  750, nH: 1624, accent: '#b85cff', dir: -1 },
  { src: 'AI_Copilot.png',                 label: 'AI Copilot',           subtitle: 'An always-on event assistant that handles attendee questions instantly', type: 'slide',  dur:  4,   nW:  750, nH: 1624, accent: '#4a9dff', dir:  1 },
];

// Build absolute start times; segments overlap by XFADE so crossfades are seamless
const TIMELINE = (() => {
  let t = INTRO;
  return SEGMENTS.map(def => {
    const seg = { ...def, start: t };
    t += def.dur - XFADE;
    return seg;
  });
})();

const LAST = TIMELINE[TIMELINE.length - 1];
const TOTAL = LAST.start + LAST.dur + OUTRO;

// ─── Helpers ─────────────────────────────────────────────────────────────────

function segOpacity(seg, t) {
  const lt = t - seg.start;
  if (lt <= 0 || lt >= seg.dur) return 0;
  if (lt < XFADE) return lt / XFADE;
  if (lt > seg.dur - XFADE) return 1 - (lt - (seg.dur - XFADE)) / XFADE;
  return 1;
}

// Interpolate accent color across active segments for the backdrop orb
function activeAccent(t) {
  let r = 0, g = 0, b = 0, total = 0;
  for (const seg of TIMELINE) {
    const op = segOpacity(seg, t);
    if (op <= 0) continue;
    const hex = seg.accent.replace('#', '');
    r += parseInt(hex.slice(0, 2), 16) * op;
    g += parseInt(hex.slice(2, 4), 16) * op;
    b += parseInt(hex.slice(4, 6), 16) * op;
    total += op;
  }
  if (total === 0) return SEGMENTS[0].accent;
  const h = v => Math.round(Math.max(0, Math.min(255, v / total))).toString(16).padStart(2, '0');
  return `#${h(r)}${h(g)}${h(b)}`;
}

// ─── Backdrop ─────────────────────────────────────────────────────────────────

function Backdrop({ t }) {
  const accent = activeAccent(t);
  const dx = Math.sin(t * 0.18) * 50;
  const dy = Math.cos(t * 0.13) * 35;

  return (
    <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', background: '#080610' }}>
      {/* Base radial */}
      <div style={{
        position: 'absolute', inset: 0,
        background: 'radial-gradient(ellipse 80% 70% at 50% 42%, #12102a 0%, #080610 70%)',
      }} />

      {/* Accent orb — shifts color with segment */}
      <div style={{
        position: 'absolute',
        left: CANVAS_W / 2 + dx - 420,
        top:  CANVAS_H / 2 + dy - 420,
        width: 840, height: 840, borderRadius: '50%',
        background: `radial-gradient(circle at 40% 40%, ${accent}60 0%, ${accent}18 45%, transparent 70%)`,
        filter: 'blur(80px)',
        mixBlendMode: 'screen',
      }} />

      {/* Secondary cool orb */}
      <div style={{
        position: 'absolute',
        left: 120 - dx * 0.5,
        top:  CANVAS_H * 0.7 - dy * 0.5,
        width: 560, height: 560, borderRadius: '50%',
        background: 'radial-gradient(circle, #4a9dff30 0%, transparent 70%)',
        filter: 'blur(90px)',
        mixBlendMode: 'screen',
      }} />

      {/* Subtle grid */}
      <div style={{
        position: 'absolute', inset: -60,
        backgroundImage: `
          linear-gradient(rgba(255,255,255,0.028) 1px, transparent 1px),
          linear-gradient(90deg, rgba(255,255,255,0.028) 1px, transparent 1px)
        `,
        backgroundSize: '90px 90px',
        transform: `translateY(${Math.sin(t * 0.17) * 12}px) translateX(${Math.cos(t * 0.14) * 8}px)`,
        maskImage: 'radial-gradient(ellipse at 50% 46%, black 25%, transparent 72%)',
        WebkitMaskImage: 'radial-gradient(ellipse at 50% 46%, black 25%, transparent 72%)',
      }} />

      {/* Film grain */}
      <div style={{
        position: 'absolute', inset: 0,
        backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence baseFrequency='0.85' numOctaves='2' seed='5'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.55 0'/></filter><rect width='200' height='200' filter='url(%23n)' opacity='0.5'/></svg>")`,
        opacity: 0.055, mixBlendMode: 'overlay',
      }} />
    </div>
  );
}

// ─── Hand-swipe scroll ────────────────────────────────────────────────────────
// Divides the total scroll into N discrete upward swipes matching real thumb
// behaviour: ease-out drag → brief hold → finger lifts → re-enters for next pass.
// The final swipe gets a rubberband overshoot + snap-back.

const DRAG_FRAC = 0.58;   // fraction of each swipe slot that is active dragging
const LIFT_FRAC = 0.18;   // fraction for lift-off fade

const PX_PER_SWIPE = SCREEN_H * 0.43;  // each swipe covers ~43% of visible height

function computeSwipeState(bp, maxScroll) {
  if (maxScroll <= 0) return { scrollY: 0, si: 0, localP: bp, n: 1 };

  const n  = Math.max(1, Math.round(maxScroll / PX_PER_SWIPE));
  const sf = 1 / n;                          // fraction of bodyDur per swipe
  const si = Math.min(n - 1, Math.floor(bp / sf));
  const lp = Math.min(1, (bp - si * sf) / sf);

  const fromY = maxScroll * si / n;
  const toY   = maxScroll * (si + 1) / n;   // may exceed maxScroll on last — clamped below

  const drag  = lp <= DRAG_FRAC ? Easing.easeOutCubic(lp / DRAG_FRAC) : 1;
  let scrollY = fromY + (toY - fromY) * drag;

  // Rubberband: last swipe only — overshoot at drag end, snap back during hold
  if (si === n - 1) {
    const BOUNCE = Math.min(26, maxScroll * 0.012);
    if (BOUNCE > 2 && lp > DRAG_FRAC * 0.85) {
      const rbp = (lp - DRAG_FRAC * 0.85) / (1 - DRAG_FRAC * 0.85);
      scrollY = maxScroll + BOUNCE * Math.sin(Math.min(rbp, 1) * Math.PI) * 0.6;
    }
  }

  return { scrollY: Math.max(0, scrollY), si, localP: lp, n };
}

// ─── Finger overlay ───────────────────────────────────────────────────────────
// Renders a semi-transparent fingertip that moves upward in sync with each swipe.

// Small horizontal offsets per swipe index for natural hand drift
const FINGER_X_OFFSETS = [0, -14, 10, -6, 16, -10, 5, -18, 8];

function SwipeFinger({ si, localP }) {
  const x = SCREEN_W * 0.60 + FINGER_X_OFFSETS[si % FINGER_X_OFFSETS.length];

  const START_Y = SCREEN_H * 0.71;
  const END_Y   = SCREEN_H * 0.23;

  let y, opacity, scale = 1;

  if (localP <= DRAG_FRAC) {
    // Dragging upward — matches content speed (ease-out)
    const p = Easing.easeOutCubic(localP / DRAG_FRAC);
    y = START_Y + (END_Y - START_Y) * p;
    opacity = 0.70;
  } else if (localP <= DRAG_FRAC + LIFT_FRAC) {
    // Lifting off — drifts up a little and fades
    const p = Easing.easeInCubic((localP - DRAG_FRAC) / LIFT_FRAC);
    y = END_Y - 28 * p;
    opacity = 0.70 * (1 - p);
    scale = 1 - 0.08 * p;
  } else {
    return null; // finger invisible while repositioning
  }

  return (
    <div style={{
      position: 'absolute',
      left: x - 20, top: y - 20,
      width: 40, height: 60,
      opacity,
      transform: `scale(${scale})`,
      transformOrigin: 'center bottom',
      pointerEvents: 'none',
      zIndex: 60,
    }}>
      <svg width="40" height="60" viewBox="0 0 40 60" fill="none">
        {/* Finger body */}
        <path d="M20 3 C9 3 5 15 5 29 C5 46 12 57 20 57 C28 57 35 46 35 29 C35 15 31 3 20 3 Z"
              fill="rgba(20,12,6,0.70)"/>
        {/* Fingernail sheen */}
        <ellipse cx="20" cy="15" rx="7" ry="5" fill="rgba(255,255,255,0.10)"/>
        {/* Contact glow ring at fingertip */}
        <ellipse cx="20" cy="52" rx="17" ry="5"
                 fill="none" stroke="rgba(255,255,255,0.20)" strokeWidth="1.5"/>
        {/* Inner contact highlight */}
        <ellipse cx="20" cy="52" rx="9" ry="2.5"
                 fill="rgba(255,255,255,0.08)"/>
      </svg>
    </div>
  );
}

// ─── Screen layers ────────────────────────────────────────────────────────────

function ScrollLayer({ seg, lt, opacity }) {
  const scale     = SCREEN_W / seg.nW;
  const imgH      = seg.nH * scale;
  const maxScroll = Math.max(0, imgH - SCREEN_H);

  const bodyDur = Math.max(0.01, seg.dur - 2 * XFADE);
  const bp      = Math.min(1, Math.max(0, (lt - XFADE) / bodyDur));

  const { scrollY, si, localP } = computeSwipeState(bp, maxScroll);

  return (
    <div style={{ position: 'absolute', inset: 0, opacity }}>
      <img src={seg.src} alt="" style={{
        position: 'absolute', left: 0, top: -scrollY,
        width: SCREEN_W, height: imgH, display: 'block',
      }} />
      <SwipeFinger si={si} localP={localP} />
    </div>
  );
}

function SlideLayer({ seg, lt, opacity }) {
  const coverScale = Math.max(SCREEN_W / seg.nW, SCREEN_H / seg.nH);
  const imgW = seg.nW * coverScale;
  const imgH = seg.nH * coverScale;
  const baseLeft = (SCREEN_W - imgW) / 2;
  const baseTop  = (SCREEN_H - imgH) / 2;

  let slideX = 0;
  const { dir } = seg;
  if (lt < XFADE) {
    slideX = dir * SCREEN_W * (1 - Easing.easeOutCubic(lt / XFADE));
  } else if (lt > seg.dur - XFADE) {
    const ep = (lt - (seg.dur - XFADE)) / XFADE;
    slideX = -dir * SCREEN_W * Easing.easeInCubic(ep);
  }

  return (
    <div style={{ position: 'absolute', inset: 0, opacity }}>
      <img src={seg.src} alt="" style={{
        position: 'absolute',
        left: baseLeft + slideX,
        top:  baseTop,
        width: imgW, height: imgH, display: 'block',
      }} />
    </div>
  );
}

function CoverLayer({ seg, lt, opacity }) {
  const coverScale = Math.max(SCREEN_W / seg.nW, SCREEN_H / seg.nH);
  const imgW = seg.nW * coverScale;
  const imgH = seg.nH * coverScale;
  const left = (SCREEN_W - imgW) / 2;
  const top  = (SCREEN_H - imgH) / 2;

  return (
    <div style={{ position: 'absolute', inset: 0, opacity }}>
      <img src={seg.src} alt="" style={{
        position: 'absolute', left, top,
        width: imgW, height: imgH, display: 'block',
      }} />
    </div>
  );
}

function ScreenContent({ t }) {
  return (
    <>
      {TIMELINE.map((seg, i) => {
        const lt = t - seg.start;
        if (lt <= 0 || lt >= seg.dur) return null;
        const opacity = segOpacity(seg, t);
        if (opacity <= 0.002) return null;

        const props = { key: i, seg, lt, opacity };
        if (seg.type === 'scroll') return <ScrollLayer  {...props} />;
        if (seg.type === 'slide')  return <SlideLayer   {...props} />;
        if (seg.type === 'cover')  return <CoverLayer   {...props} />;
        return null;
      })}
    </>
  );
}

// ─── Phone ───────────────────────────────────────────────────────────────────

function Phone({ t }) {
  // Intro: scale + fade in
  let scale = 1, phoneOpacity = 1;
  if (t < INTRO) {
    const p = Easing.easeOutBack(Math.min(1, t / INTRO));
    scale = 0.82 + 0.18 * p;
    phoneOpacity = Math.min(1, (t / INTRO) * 1.6);
  }
  // Outro: fade out
  const outroStart = TOTAL - OUTRO;
  if (t > outroStart) {
    phoneOpacity *= Math.max(0, 1 - (t - outroStart) / OUTRO);
  }
  // Very subtle breathing
  scale *= 1 + 0.003 * Math.sin(t * 0.38);

  return (
    <div style={{
      position: 'absolute',
      left: (CANVAS_W - PHONE_W) / 2,
      top:  (CANVAS_H - PHONE_H) / 2,
      width: PHONE_W, height: PHONE_H,
      opacity: phoneOpacity,
      transform: `scale(${scale})`,
      transformOrigin: 'center center',
    }}>
      {/* Phone body */}
      <div style={{
        position: 'absolute', inset: 0,
        borderRadius: 58, background: '#0a0a0c',
        boxShadow: `
          inset 0 0 0 2px rgba(255,255,255,0.07),
          inset 0 0 0 3px rgba(0,0,0,0.9)
        `,
      }} />

      {/* Screen area */}
      <div style={{
        position: 'absolute',
        left: BEZEL, top: BEZEL,
        width: SCREEN_W, height: SCREEN_H,
        borderRadius: 46, overflow: 'hidden',
        background: '#fff',
        boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.12)',
      }}>
        <ScreenContent t={t} />

        {/* Dynamic Island */}
        <div style={{
          position: 'absolute', top: 11, left: '50%',
          transform: 'translateX(-50%)',
          width: 120, height: 34, borderRadius: 20,
          background: '#000', zIndex: 50,
        }} />

        {/* Status bar — 9:41 */}
        <div style={{
          position: 'absolute', top: 16, left: 28,
          fontFamily: '-apple-system, system-ui',
          fontSize: 15, fontWeight: 600, color: '#000',
          zIndex: 51, letterSpacing: -0.2,
        }}>9:41</div>
        <div style={{
          position: 'absolute', top: 18, right: 28,
          display: 'flex', gap: 5, alignItems: 'center', zIndex: 51,
        }}>
          <svg width="16" height="10" viewBox="0 0 16 10" fill="#000">
            <rect x="0"    y="6"   width="2.6" height="3.6" rx="0.5"/>
            <rect x="3.8"  y="4"   width="2.6" height="5.6" rx="0.5"/>
            <rect x="7.6"  y="2"   width="2.6" height="7.6" rx="0.5"/>
            <rect x="11.4" y="0"   width="2.6" height="9.6" rx="0.5"/>
          </svg>
          <svg width="22" height="10" viewBox="0 0 22 10">
            <rect x="0.5" y="0.5" width="19" height="9" rx="2.5" stroke="#000" strokeOpacity="0.4" fill="none"/>
            <rect x="1.8" y="1.8" width="15" height="6.4" rx="1.5" fill="#000"/>
            <path d="M20.5 3.5v3c.6-.2 1-.9 1-1.5s-.4-1.3-1-1.5z" fill="#000" fillOpacity="0.4"/>
          </svg>
        </div>

        {/* Specular highlight — shifts slightly with subtle breathing */}
        <div style={{
          position: 'absolute', inset: 0,
          background: `radial-gradient(ellipse 65% 38% at 38% 22%, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0) 55%)`,
          pointerEvents: 'none', mixBlendMode: 'screen', zIndex: 52,
        }} />
      </div>

      {/* Bezel edge ring */}
      <div style={{
        position: 'absolute', inset: 0,
        borderRadius: 58,
        border: '1px solid rgba(255,255,255,0.08)',
        pointerEvents: 'none',
      }} />

      {/* Side buttons */}
      <div style={{ position: 'absolute', left: -2, top: 140, width: 3, height: 30, borderRadius: 2, background: '#1a1a1c' }}/>
      <div style={{ position: 'absolute', left: -2, top: 190, width: 3, height: 55, borderRadius: 2, background: '#1a1a1c' }}/>
      <div style={{ position: 'absolute', left: -2, top: 260, width: 3, height: 55, borderRadius: 2, background: '#1a1a1c' }}/>
      <div style={{ position: 'absolute', right: -2, top: 200, width: 3, height: 85, borderRadius: 2, background: '#1a1a1c' }}/>

    </div>
  );
}

// ─── Title overlay (above the phone) ─────────────────────────────────────────
// Phone top is at (CANVAS_H - PHONE_H)/2 = 495px. Titles sit in the 0–495 band.

function TitleOverlay({ t }) {
  // Intro brand card
  const showIntro = t < INTRO + 0.3;
  const introPEnd = INTRO * 0.75;
  const introFEnd = INTRO + 0.3;
  const introOp = showIntro
    ? (t < introPEnd
        ? Easing.easeOutCubic(Math.min(1, t / (INTRO * 0.55)))
        : Math.max(0, 1 - (t - introPEnd) / (introFEnd - introPEnd)))
    : 0;
  const introTy = Easing.easeOutCubic(Math.min(1, t / (INTRO * 0.55)));

  // Outro card
  const outroStart = TOTAL - OUTRO + 0.1;
  const showOutro = t >= outroStart;
  const outroP = showOutro ? Easing.easeOutCubic(Math.min(1, (t - outroStart) / 0.9)) : 0;
  const outroOp = Math.min(1, outroP * 1.4);

  return (
    <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>

      {/* Intro label */}
      {introOp > 0.01 && (
        <div style={{
          position: 'absolute',
          bottom: 1445,
          left: 0, right: 0,
          display: 'flex', flexDirection: 'column', alignItems: 'center',
          opacity: introOp,
          transform: `translateY(${(1 - introTy) * 24}px)`,
        }}>
          <div style={{
            fontFamily: 'JetBrains Mono, ui-monospace, monospace',
            fontSize: 20, color: 'rgba(255,255,255,0.5)',
            letterSpacing: '0.32em', textTransform: 'uppercase', marginBottom: 18,
          }}>Now Presenting</div>
          <div style={{
            fontFamily: '"Instrument Serif", Georgia, serif',
            fontSize: 96, fontWeight: 400,
            background: 'linear-gradient(135deg, #fff 0%, #c4b4ff 55%, #ff9a6e 100%)',
            WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
            backgroundClip: 'text', color: 'transparent',
            letterSpacing: '-0.025em', lineHeight: 1, textAlign: 'center',
          }}>Event App</div>
        </div>
      )}

      {/* Per-segment feature labels (crossfading) */}
      {TIMELINE.map((seg, i) => {
        const op = Easing.easeInOutQuad(segOpacity(seg, t));
        if (op < 0.01) return null;
        return (
          <div key={i} style={{
            position: 'absolute',
            bottom: 1445,
            left: 72, right: 72,
            opacity: op,
          }}>
            {/* Counter */}
            <div style={{
              fontFamily: 'JetBrains Mono, ui-monospace, monospace',
              fontSize: 17, color: seg.accent,
              letterSpacing: '0.28em', marginBottom: 14,
            }}>
              {String(i + 1).padStart(2, '0')} / {String(TIMELINE.length).padStart(2, '0')}
            </div>
            {/* Accent bar */}
            <div style={{
              width: 48, height: 3, background: seg.accent,
              borderRadius: 2, marginBottom: 18,
              boxShadow: `0 0 14px ${seg.accent}99`,
            }} />
            {/* Label */}
            <div style={{
              fontFamily: '"Instrument Serif", Georgia, serif',
              fontSize: 72, fontWeight: 400, color: '#fff',
              letterSpacing: '-0.02em', lineHeight: 1.05,
            }}>
              {seg.label.split(' ').map((word, wi, arr) => (
                <React.Fragment key={wi}>
                  <span style={{ display: 'inline-block', whiteSpace: 'nowrap' }}>{word}</span>
                  {wi < arr.length - 1 && <span>&nbsp;</span>}
                </React.Fragment>
              ))}
            </div>
            {/* Subtitle */}
            <div style={{
              fontFamily: 'Inter, system-ui, sans-serif',
              fontSize: 22, fontWeight: 300, color: 'rgba(255,255,255,0.58)',
              letterSpacing: '-0.005em', marginTop: 14,
            }}>{seg.subtitle}</div>
          </div>
        );
      })}

      {/* Outro */}
      {outroOp > 0.01 && (
        <div style={{
          position: 'absolute',
          top: 80,
          left: 0, right: 0,
          display: 'flex', flexDirection: 'column', alignItems: 'center',
          opacity: outroOp,
          transform: `translateY(${(1 - outroP) * -18}px)`,
        }}>
          <div style={{
            fontFamily: '"Instrument Serif", Georgia, serif',
            fontSize: 88, fontWeight: 400,
            background: 'linear-gradient(135deg, #fff 0%, #c4b4ff 55%, #ff9a6e 100%)',
            WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
            backgroundClip: 'text', color: 'transparent',
            letterSpacing: '-0.025em', lineHeight: 1, textAlign: 'center',
          }}>See you there.</div>
        </div>
      )}
    </div>
  );
}

// ─── Scene ────────────────────────────────────────────────────────────────────

function ScrollScene() {
  const t = useTime();
  return (
    <>
      <Phone t={t} />
      <TitleOverlay t={t} />
    </>
  );
}

Object.assign(window, { ScrollScene, TOTAL });
