// scenes.jsx — Vaomi Fellowships: one continuous sentence, told in chapters.
//
//   0  INVOCATION      Institutionalize your conviction.
//   1  PROLOGUE        Some conviction cannot wait.
//   2  PARADOX         Capital without conviction / conviction without capital
//   3  QUESTION        So here is the question — rotating globe (right)
//   4  SIGNAL          We track signals across every sector…
//   5  TURN            We already do this. Now we hand you the instrument.
//   6  PATRONS         Built for those who already have conviction.
//   7  INSIDE/OUTSIDE  Innovation from within takes decades.
//   8  SYSTEM          Here is how we do it.
//   9  LEDGER          What conviction produces — a short ledger.
//  10  AIRBNB          A single story: the first cheque.
//  11  OUTCOME         A constellation that compounds.
//  12  OUTRO           Build your Fellowship.

const INK = '#f2f0ec';
const BG = '#0d0f13';
const MUTED = '#75787f';
const LINE = 'rgba(242,240,236,0.18)';

const HELV = "'Helvetica Neue', Helvetica, Arial, sans-serif";
const MONO = "'JetBrains Mono', ui-monospace, SFMono-Regular, monospace";

function formatNum(n, opts = {}) {
  const { decimals = 0, commas = true } = opts;
  const fixed = n.toFixed(decimals);
  if (!commas) return fixed;
  const [int, dec] = fixed.split('.');
  const withCommas = int.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return dec ? `${withCommas}.${dec}` : withCommas;
}

function rng(seed) {
  let s = seed;
  return () => { s = (s * 16807) % 2147483647; return (s - 1) / 2147483646; };
}

function fade(localTime, duration, entryDur = 0.5, exitDur = 0.5) {
  const exitStart = Math.max(0, duration - exitDur);
  if (localTime < entryDur) return Easing.easeOutCubic(clamp(localTime / entryDur, 0, 1));
  if (localTime > exitStart) return 1 - Easing.easeInCubic(clamp((localTime - exitStart) / exitDur, 0, 1));
  return 1;
}

function SceneChrome({ label }) {
  return (
    <>
      {label && (
        <div style={{
          position: 'absolute', top: 32, right: 40,
          fontFamily: MONO, fontSize: 11, letterSpacing: '0.22em',
          color: INK, opacity: 0.55, textTransform: 'uppercase',
          textAlign: 'right',
        }}>
          {label}
        </div>
      )}
      <div style={{
        position: 'absolute', left: 40, right: 40, bottom: 32,
        height: 1, background: LINE,
      }}/>
    </>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 0 — INVOCATION
// ════════════════════════════════════════════════════════════════════════════
function SceneInvocation({ start, end }) {
  const dots = React.useMemo(() => {
    const r = rng(7);
    const arr = [];
    for (let i = 0; i < 180; i++) {
      const theta = r() * Math.PI * 2;
      const startR = 450 + r() * 500;
      const orbitR = 140 + Math.pow(r(), 0.7) * 260;
      const orbitTheta = r() * Math.PI * 2;
      arr.push({
        theta, startR, orbitR, orbitTheta,
        size: 1 + r() * 3,
        opacity: 0.25 + r() * 0.55,
        delay: r() * 1.6,
        twinkle: r() * Math.PI * 2,
      });
    }
    return arr;
  }, []);

  const stars = React.useMemo(() => {
    const r = rng(91);
    const arr = [];
    for (let i = 0; i < 90; i++) {
      arr.push({
        x: r() * 1280, y: r() * 720,
        size: 0.8 + r() * 1.6,
        opacity: 0.1 + r() * 0.25,
        twinkle: r() * Math.PI * 2,
      });
    }
    return arr;
  }, []);

  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        // No entry fade — scene is fully visible at t=0 (0% scroll)
        const exitStart = Math.max(0, duration - 0.6);
        const o = localTime > exitStart
          ? 1 - Easing.easeInCubic(clamp((localTime - exitStart) / 0.6, 0, 1))
          : 1;
        const cx = 640, cy = 360;

        const seedGrow = Easing.easeOutBack(clamp(localTime / 0.8, 0, 1));
        const ringCount = 4;
        const orbitRot = Math.max(0, localTime - 2.0) * 0.08;

        // Title and subtitle appear immediately at t=0
        const titleOp = Easing.easeOutCubic(clamp((localTime + 0.9) / 0.9, 0, 1));
        const subOp   = Easing.easeOutCubic(clamp((localTime + 0.5) / 0.9, 0, 1));


        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o, background: BG }}>
            {/* Far starfield */}
            {stars.map((s, i) => (
              <div key={`s-${i}`} style={{
                position: 'absolute',
                left: s.x, top: s.y,
                width: s.size, height: s.size,
                borderRadius: '50%', background: INK,
                opacity: s.opacity * (0.7 + 0.3 * Math.sin(localTime * 1.2 + s.twinkle)),
              }}/>
            ))}

            {/* Expanding rings */}
            <svg width="1280" height="720" style={{ position: 'absolute', inset: 0 }}>
              {[...Array(ringCount)].map((_, i) => {
                const t = clamp((localTime - 0.4 - i * 0.4) / 1.8, 0, 1);
                if (t <= 0) return null;
                const r = t * 420;
                const op = (1 - t) * 0.3;
                return <circle key={i} cx={cx} cy={cy} r={r} fill="none" stroke={INK} strokeWidth="0.75" opacity={op}/>;
              })}
            </svg>

            {/* Drifting particles */}
            {dots.map((d, i) => {
              const t = clamp((localTime - 0.6 - d.delay) / 2.2, 0, 1);
              const e = Easing.easeInOutCubic(t);
              const cur = d.startR + (d.orbitR - d.startR) * e;
              const thetaNow = d.theta + (d.orbitTheta + orbitRot - d.theta) * e;
              const x = cx + Math.cos(thetaNow) * cur;
              const y = cy + Math.sin(thetaNow) * cur;
              const tw = 0.7 + 0.3 * Math.sin(localTime * 1.6 + d.twinkle);
              return (
                <div key={i} style={{
                  position: 'absolute',
                  left: x - d.size / 2, top: y - d.size / 2,
                  width: d.size, height: d.size,
                  borderRadius: '50%', background: INK,
                  opacity: (0.15 + 0.85 * e) * d.opacity * tw,
                }}/>
              );
            })}

            {/* Outer orbit hint */}
            {localTime > 2.4 && (
              <div style={{
                position: 'absolute', left: cx - 200, top: cy - 200,
                width: 400, height: 400, borderRadius: '50%',
                border: `1px solid ${INK}`,
                opacity: Easing.easeOutCubic(clamp((localTime - 2.4) / 1.0, 0, 1)) * 0.1,
              }}/>
            )}


            {/* Centre title */}
            <div style={{
              position: 'absolute', left: '50%', top: '50%',
              transform: 'translate(-50%, -50%)',
              width: 1100, textAlign: 'center',
              fontFamily: HELV, color: INK,
              pointerEvents: 'none',
            }}>
              <div style={{
                fontSize: 108, fontWeight: 700, letterSpacing: '-0.045em', lineHeight: 0.92,
                opacity: titleOp,
                transform: `translateY(${(1 - titleOp) * 20}px)`,
                color: INK,
              }}>
                Institutionalize<br/>your conviction.
              </div>
              <div style={{
                marginTop: 34,
                fontFamily: HELV, fontSize: 22, letterSpacing: '-0.005em',
                color: MUTED, opacity: subOp,
                fontStyle: 'italic', fontWeight: 400,
              }}>
                Where the next generation is chosen,<br/>before the world learns their names.
              </div>
            </div>

            <div style={{
              position: 'absolute', bottom: 32, left: 40, right: 40,
              display: 'flex', justifyContent: 'space-between',
              fontFamily: MONO, fontSize: 11, letterSpacing: '0.22em', color: INK, opacity: 0.55,
              textTransform: 'uppercase',
            }}>
              <span>The next great fellowship begins with you</span>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 1 — PROLOGUE
// ════════════════════════════════════════════════════════════════════════════
function ScenePrologue({ start, end }) {
  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const lineOp = Easing.easeOutCubic(clamp((localTime - 0.3) / 0.9, 0, 1));
        const tailOp = Easing.easeOutCubic(clamp((localTime - 2.2) / 0.9, 0, 1));
        const w = Easing.easeInOutCubic(clamp((localTime - 1.8) / 1.6, 0, 1)) * 320;

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome />
            <div style={{
              position: 'absolute', left: '50%', top: '50%',
              transform: 'translate(-50%, -50%)',
              textAlign: 'center', width: 1100,
            }}>
              <div style={{
                fontFamily: HELV, fontSize: 120, fontWeight: 700,
                letterSpacing: '-0.048em', lineHeight: 0.94, color: INK,
                opacity: lineOp,
                transform: `translateY(${(1 - lineOp) * 16}px)`,
              }}>
                Some conviction<br/>cannot wait.
              </div>
              <div style={{ marginTop: 56, display: 'flex', justifyContent: 'center' }}>
                <div style={{ width: w, height: 1, background: INK, opacity: 0.5 }}/>
              </div>
              <div style={{
                marginTop: 40,
                fontFamily: HELV, fontSize: 20, lineHeight: 1.5, color: MUTED,
                maxWidth: 640, marginLeft: 'auto', marginRight: 'auto',
                opacity: tailOp,
              }}>
                The rarest moments in building happen<br/>
                before the world begins to look.
              </div>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 2 — PARADOX
// ════════════════════════════════════════════════════════════════════════════
function SceneParadox({ start, end }) {
  const swirl = React.useMemo(() => {
    const r = rng(31);
    const arr = [];
    for (let i = 0; i < 120; i++) {
      arr.push({
        baseTheta: r() * Math.PI * 2,
        radius: 80 + r() * 90,
        yScale: 0.25 + r() * 0.35,
        speed: 0.25 + r() * 0.4,
        size: 1.5 + r() * 2.2,
        opacity: 0.25 + r() * 0.55,
        phase: r() * Math.PI * 2,
        tilt: r() * Math.PI * 2,
      });
    }
    return arr;
  }, []);

  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const lqCX = 250, lqCY = 260;
        const gmCX = 450, gmCY = 440;
        const bigTextOp = Easing.easeOutCubic(clamp((localTime - 0.9) / 0.8, 0, 1));
        const swirlAppear = Easing.easeOutCubic(clamp(localTime / 1.0, 0, 1));
        const gemAppear = Easing.easeOutBack(clamp((localTime - 1.6) / 1.0, 0, 1));
        const gemRot = localTime * 0.25;

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome />

            {/* LIQUIDITY swirl */}
            <div style={{
              position: 'absolute', left: lqCX - 180, top: lqCY - 140,
              width: 360, height: 280,
            }}>
              {swirl.map((p, i) => {
                const theta = p.baseTheta + localTime * p.speed;
                const x = 180 + Math.cos(theta + p.phase) * p.radius;
                const y = 140 + Math.sin(theta + p.phase) * p.radius * p.yScale + Math.sin(localTime + p.tilt) * 6;
                const z = Math.sin(theta + p.phase);
                const scale = 0.6 + 0.4 * (z + 1) / 2;
                return (
                  <div key={i} style={{
                    position: 'absolute',
                    left: x - p.size / 2, top: y - p.size / 2,
                    width: p.size * scale, height: p.size * scale,
                    borderRadius: '50%', background: INK,
                    opacity: swirlAppear * p.opacity * (0.5 + 0.5 * ((z + 1) / 2)),
                    filter: z < -0.2 ? 'blur(0.6px)' : 'none',
                  }}/>
                );
              })}
              <div style={{
                position: 'absolute', left: 160, top: 120,
                width: 40, height: 40, borderRadius: '50%',
                background: `radial-gradient(circle at 50% 50%, ${INK}55, transparent 70%)`,
                opacity: swirlAppear * 0.6,
              }}/>
              <div style={{
                position: 'absolute', left: -10, bottom: -26,
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.24em', color: INK,
                textTransform: 'uppercase',
                opacity: Easing.easeOutCubic(clamp((localTime - 0.4) / 0.6, 0, 1)) * 0.8,
              }}>
                LIQUIDITY
                <span style={{ marginLeft: 8, opacity: 0.5 }}>/ MONEY WITHOUT A NAME</span>
              </div>
            </div>

            {/* CONVICTION gem */}
            <div style={{
              position: 'absolute', left: gmCX, top: gmCY,
              transform: 'translate(-50%, -50%)',
              width: 180, height: 180,
              opacity: gemAppear,
            }}>
              <svg viewBox="-100 -100 200 200" width="180" height="180"
                style={{ transform: `rotate(${gemRot}rad)` }}>
                <polygon points="0,-72 62,-22 40,58 -40,58 -62,-22"
                  fill={INK} opacity="0.95"/>
                <polygon points="0,-72 62,-22 40,58 -40,58 -62,-22"
                  fill="none" stroke={INK} strokeWidth="0.5" opacity="0.3"
                  transform="scale(1.25)"/>
                <polygon points="0,-72 62,-22 40,58 -40,58 -62,-22"
                  fill="none" stroke={INK} strokeWidth="0.5" opacity="0.15"
                  transform="scale(1.55)"/>
              </svg>
              <div style={{
                position: 'absolute', left: '50%', top: '100%',
                transform: 'translateX(-50%)', marginTop: 18,
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.24em', color: INK,
                textTransform: 'uppercase', whiteSpace: 'nowrap',
              }}>
                CONVICTION
                <span style={{ marginLeft: 8, opacity: 0.5 }}>/ A NAME WITHOUT A CHEQUE</span>
              </div>
            </div>

            {/* Connector */}
            <svg width="1280" height="720" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
              <line x1={lqCX + 80} y1={lqCY + 60}
                    x2={gmCX - 80} y2={gmCY - 60}
                    stroke={INK} strokeDasharray="3 5" strokeWidth="0.8"
                    opacity={Easing.easeOutCubic(clamp((localTime - 2.0) / 0.8, 0, 1)) * 0.4}/>
            </svg>

            {/* TEXT — right column, no bridging lines */}
            <div style={{
              position: 'absolute', right: 60, top: '50%',
              transform: 'translateY(-50%)', maxWidth: 560,
              fontFamily: HELV, color: INK,
              opacity: bigTextOp,
            }}>
              <div style={{
                fontFamily: MONO, fontSize: 11, letterSpacing: '0.26em',
                color: INK, opacity: 0.55, textTransform: 'uppercase', marginBottom: 18,
              }}>THE PARADOX</div>
              <div style={{
                fontSize: 52, fontWeight: 700, letterSpacing: '-0.03em', lineHeight: 1.04,
              }}>
                Capital without conviction<br/>
                is just liquidity.
              </div>
              <div style={{
                marginTop: 28,
                fontSize: 44, fontWeight: 400, letterSpacing: '-0.02em', lineHeight: 1.08,
                color: MUTED, fontStyle: 'italic',
              }}>
                Conviction without capital<br/>
                is just a dream.
              </div>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 3 — QUESTION  —  globe on RIGHT, text + list on LEFT
// ════════════════════════════════════════════════════════════════════════════
function SceneQuestion({ start, end }) {
  const cx = 920, cy = 360, R = 200;

  const pins = [
    { lat:  19, lon:  73, name: 'Mehta Family Office',          label: 'MUMBAI',    delay: 0.9 },
    { lat:   1, lon: 104, name: 'Farview Capital Collective',   label: 'SINGAPORE', delay: 1.2 },
    { lat:  37, lon:-122, name: 'Redwood Angel Collective',     label: 'PALO ALTO', delay: 1.5 },
    { lat:  51, lon:  -0, name: 'Cavendish Patron Syndicate',   label: 'LONDON',    delay: 1.8 },
    { lat:  25, lon:  55, name: 'Gulf Sovereign Cohort',        label: 'DUBAI',     delay: 2.1 },
    { lat: -34, lon: 151, name: 'Pacific Family Group',         label: 'SYDNEY',    delay: 2.4 },
    { lat:  35, lon: 139, name: 'Kyosei Industrial Heirs',      label: 'TOKYO',     delay: 2.7 },
    { lat:  40, lon: -74, name: 'Bridgewater Wealth Trust',     label: 'NEW YORK',  delay: 3.0 },
  ];

  const gridDots = React.useMemo(() => {
    const arr = [];
    for (let lat = -60; lat <= 60; lat += 20) {
      for (let lon = -180; lon < 180; lon += 15) {
        arr.push({ lat, lon });
      }
    }
    return arr;
  }, []);

  // Simplified land-mass reference dots for a world-map feel
  const landPoints = React.useMemo(() => [
    // North America
    [70,-140],[60,-130],[55,-128],[50,-122],[45,-75],[42,-83],[40,-75],[37,-76],[32,-80],
    [25,-100],[22,-98],[20,-90],[50,-110],[45,-108],[40,-100],[35,-95],[30,-90],[48,-90],
    // South America
    [10,-75],[5,-60],[0,-62],[-5,-55],[-10,-55],[-15,-52],[-20,-43],[-25,-48],[-30,-52],[-35,-58],[-40,-62],[-50,-68],[-55,-68],
    // Europe
    [70,28],[65,22],[60,10],[56,10],[52,5],[50,5],[48,8],[45,12],[41,14],[38,15],[37,0],[43,-5],[50,-5],[55,-4],[60,5],[65,14],[55,22],[50,18],
    // Africa
    [37,10],[30,10],[25,15],[15,15],[5,5],[0,12],[-5,14],[-10,15],[-20,22],[-25,25],[-30,28],[-34,18],[30,32],[22,37],[10,42],[0,38],[35,36],
    // Middle East
    [25,45],[30,47],[35,38],[37,36],
    // Asia (Central/East)
    [35,60],[40,65],[45,72],[50,80],[55,85],[60,70],[65,72],[70,70],[55,100],[50,100],[45,100],
    [55,115],[50,120],[45,128],[42,142],[40,140],[38,128],[35,105],[30,107],[25,115],[20,110],[15,108],[10,107],
    // SE Asia
    [22,114],[20,108],[15,100],[13,100],[5,103],[1,104],[-5,105],[-8,115],
    // Oceania
    [-15,130],[-20,135],[-25,133],[-30,132],[-35,139],[-38,147],[-34,151],[-26,152],[-16,145],[-22,114],[-32,115],
    // Japan / UK
    [36,138],[38,141],[40,140],[43,142],[52,-2],[58,-4],[60,-1],
  ], []);

  const [userSpin, setUserSpin] = React.useState(0);
  const dragRef = React.useRef({ active: false, startX: 0, baseSpin: 0 });

  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const spin = localTime * 0.35 + userSpin;

        const project = (lat, lon) => {
          const phi = (90 - lat) * Math.PI / 180;
          const theta = (lon * Math.PI / 180) + spin;
          const x = R * Math.sin(phi) * Math.cos(theta);
          const y = R * Math.cos(phi) * -1;
          const z = R * Math.sin(phi) * Math.sin(theta);
          return { x: cx + x, y: cy + y, z, facing: z > -40 };
        };

        const q1Op = Easing.easeOutCubic(clamp((localTime - 0.2) / 0.8, 0, 1));
        const bodyOp = Easing.easeOutCubic(clamp((localTime - 3.0) / 0.7, 0, 1));

        return (
          <div
            style={{ position: 'absolute', inset: 0, opacity: o }}
            onMouseMove={e => {
              if (!dragRef.current.active) return;
              setUserSpin(dragRef.current.baseSpin + (e.clientX - dragRef.current.startX) * 0.01);
            }}
            onMouseUp={() => { dragRef.current.active = false; }}
            onMouseLeave={() => { dragRef.current.active = false; }}
          >
            <SceneChrome label="THE QUESTION" />

            {/* LEFT — question text */}
            <div style={{
              position: 'absolute', left: 40, top: 108, maxWidth: 490,
              fontFamily: MONO, fontSize: 11, letterSpacing: '0.28em',
              color: INK, opacity: q1Op * 0.6, textTransform: 'uppercase',
            }}>So here is the question that follows</div>

            <div style={{
              position: 'absolute', left: 40, top: 148, maxWidth: 490,
              fontFamily: HELV, fontSize: 42, fontWeight: 700,
              letterSpacing: '-0.028em', lineHeight: 1.04, color: INK,
              opacity: q1Op,
            }}>
              What if the next great<br/>
              program was not<br/>
              built by a firm?
            </div>

            {/* Globe — SVG on right */}
            <svg width="1280" height="720" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
              <circle cx={cx} cy={cy} r={R} fill="none" stroke={INK} strokeWidth="1" opacity="0.25"/>
              {[0,30,60,90,120,150].map((deg, i) => (
                <ellipse key={`mer-${i}`}
                  cx={cx} cy={cy}
                  rx={Math.abs(R * Math.cos((deg + (spin * 180 / Math.PI)) * Math.PI / 180))}
                  ry={R}
                  fill="none" stroke={INK} strokeWidth="0.5" opacity="0.1"/>
              ))}
              {[-60,-30,0,30,60].map((lat, i) => {
                const phi = (90 - lat) * Math.PI / 180;
                const rY = R * Math.sin(phi);
                return (
                  <ellipse key={`par-${i}`}
                    cx={cx} cy={cy - R * Math.cos(phi)}
                    rx={rY} ry={rY * 0.12}
                    fill="none" stroke={INK} strokeWidth="0.5" opacity="0.12"/>
                );
              })}

              {/* Grid dots */}
              {gridDots.map((g, i) => {
                const p = project(g.lat, g.lon);
                if (!p.facing) return null;
                const op = 0.08 + ((p.z + R) / (2 * R)) * 0.12;
                return <circle key={i} cx={p.x} cy={p.y} r="1" fill={INK} opacity={op}/>;
              })}

              {/* Land mass dots — slightly larger/darker */}
              {landPoints.map((g, i) => {
                const p = project(g[0], g[1]);
                if (!p.facing) return null;
                const depth = (p.z + R) / (2 * R);
                return <circle key={`land-${i}`} cx={p.x} cy={p.y} r="2" fill={INK} opacity={0.18 + depth * 0.22}/>;
              })}

              {/* Arcs between visible pins */}
              {pins.map((a, i) => pins.slice(i + 1).map((b, j) => {
                const pa = project(a.lat, a.lon);
                const pb = project(b.lat, b.lon);
                if (!pa.facing || !pb.facing) return null;
                const appearA = clamp((localTime - a.delay) / 0.6, 0, 1);
                const appearB = clamp((localTime - b.delay) / 0.6, 0, 1);
                const op = Math.min(appearA, appearB) * 0.12;
                if (op < 0.01) return null;
                const mx = (pa.x + pb.x) / 2;
                const my = (pa.y + pb.y) / 2 - 20;
                return (
                  <path key={`arc-${i}-${j}`}
                    d={`M ${pa.x} ${pa.y} Q ${mx} ${my} ${pb.x} ${pb.y}`}
                    stroke={INK} strokeWidth="0.5" fill="none" opacity={op}/>
                );
              }))}
            </svg>

            {/* Pin dots + name labels */}
            {pins.map((pin, i) => {
              const p = project(pin.lat, pin.lon);
              if (!p.facing) return null;
              const appear = Easing.easeOutBack(clamp((localTime - pin.delay) / 0.5, 0, 1));
              const pulse = 1 + 0.25 * Math.sin(localTime * 2.5 + i);
              const depth = (p.z + R) / (2 * R);
              return (
                <div key={i} style={{
                  position: 'absolute', left: p.x, top: p.y,
                  transform: 'translate(-50%, -50%)',
                  opacity: appear,
                }}>
                  <div style={{
                    width: 9, height: 9, borderRadius: '50%',
                    background: INK,
                    transform: `scale(${pulse})`,
                    opacity: 0.5 + 0.5 * depth,
                    boxShadow: `0 0 0 ${3 * pulse}px rgba(242,240,236,0.1)`,
                  }}/>
                  <div style={{
                    position: 'absolute', left: 14, top: -8,
                    fontFamily: MONO, fontSize: 8.5, letterSpacing: '0.14em',
                    color: INK, whiteSpace: 'nowrap',
                    opacity: 0.75 + 0.25 * depth,
                  }}>
                    {pin.name}
                  </div>
                </div>
              );
            })}

            {/* Globe drag overlay — transparent circle over globe */}
            <div
              style={{
                position: 'absolute',
                left: cx - R, top: cy - R,
                width: R * 2, height: R * 2,
                borderRadius: '50%',
                cursor: 'grab',
                zIndex: 10,
              }}
              onMouseDown={e => {
                e.preventDefault();
                dragRef.current = { active: true, startX: e.clientX, baseSpin: userSpin };
              }}
            />

            {/* LEFT — list column */}
            <div style={{
              position: 'absolute', left: 40, top: 340,
              width: 460,
              fontFamily: HELV, color: INK,
              opacity: bodyOp,
            }}>
              <div style={{
                fontFamily: MONO, fontSize: 11, letterSpacing: '0.24em',
                color: INK, opacity: 0.6, textTransform: 'uppercase', marginBottom: 16,
              }}>Here's what is already happening quietly</div>
              {[
                '14 family offices launched private fellowships last year',
                '6 sovereign arms backing founder cohorts directly',
                '22 angel collectives running selection in-house',
                '9 patron syndicates with named scholars of their own',
                '4 sovereign funds recruiting for sector cohorts',
              ].map((line, i) => {
                const appear = Easing.easeOutCubic(clamp((localTime - 3.2 - i * 0.15) / 0.5, 0, 1));
                return (
                  <div key={i} style={{
                    padding: '10px 0',
                    borderTop: i === 0 ? `1px solid ${LINE}` : 'none',
                    borderBottom: `1px solid ${LINE}`,
                    opacity: appear,
                    transform: `translateX(${(1 - appear) * 10}px)`,
                    fontSize: 14, lineHeight: 1.45, color: INK,
                  }}>{line}</div>
                );
              })}
              <div style={{
                marginTop: 18,
                fontSize: 14, lineHeight: 1.55, color: MUTED, fontStyle: 'italic',
              }}>
                The next YC is not a firm. It is a patron with a name.
              </div>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 4 — SIGNAL
// ════════════════════════════════════════════════════════════════════════════
function SceneSignal({ start, end }) {
  const sectors = [
    'ARTIFICIAL INTELLIGENCE',
    'CLIMATE + ENERGY',
    'BIOTECH + LONGEVITY',
    'DEFENSE + SPACE',
    'FINTECH + CRYPTO',
    'ROBOTICS + HARDWARE',
    'CONSUMER + CULTURE',
    'INDUSTRY + SUPPLY',
  ];

  const waveParams = React.useMemo(() => {
    const r = rng(17);
    return sectors.map(() => ({
      baseAmp: 0.35 + r() * 0.4,
      speed: 0.8 + r() * 1.2,
      phase: r() * Math.PI * 2,
      spikeAt: 0.3 + r() * 0.5,
      spikeWidth: 0.08 + r() * 0.06,
    }));
  }, []);

  // Minimal SVG icons per sector
  const SectorIcon = ({ index }) => {
    const shapes = [
      // AI: neural nodes
      <><circle cx="7" cy="3" r="1.8" fill={INK}/><circle cx="2" cy="11" r="1.8" fill={INK}/><circle cx="12" cy="11" r="1.8" fill={INK}/><line x1="7" y1="3" x2="2" y2="11" stroke={INK} strokeWidth="0.8"/><line x1="7" y1="3" x2="12" y2="11" stroke={INK} strokeWidth="0.8"/></>,
      // Climate: triangle
      <><polygon points="7,1 13,13 1,13" fill="none" stroke={INK} strokeWidth="1.2"/></>,
      // Biotech: double arc
      <><path d="M4,2 Q10,7 4,12 M10,2 Q4,7 10,12" fill="none" stroke={INK} strokeWidth="1.2"/></>,
      // Defense: pentagon
      <><polygon points="7,1 13,5 11,12 3,12 1,5" fill="none" stroke={INK} strokeWidth="1.2"/></>,
      // Fintech: circle + bar
      <><circle cx="7" cy="7" r="5" fill="none" stroke={INK} strokeWidth="1.2"/><line x1="4" y1="7" x2="10" y2="7" stroke={INK} strokeWidth="1.2"/></>,
      // Robotics: dashed circle
      <><circle cx="7" cy="7" r="3" fill="none" stroke={INK} strokeWidth="1.2"/><circle cx="7" cy="7" r="5.5" fill="none" stroke={INK} strokeWidth="1" strokeDasharray="1.5 1.5"/></>,
      // Consumer: diamond
      <><polygon points="7,1 13,7 7,13 1,7" fill="none" stroke={INK} strokeWidth="1.2"/></>,
      // Industry: 2x2 grid
      <><rect x="2" y="2" width="4" height="4" fill="none" stroke={INK} strokeWidth="0.8"/><rect x="8" y="2" width="4" height="4" fill="none" stroke={INK} strokeWidth="0.8"/><rect x="2" y="8" width="4" height="4" fill="none" stroke={INK} strokeWidth="0.8"/><rect x="8" y="8" width="4" height="4" fill="none" stroke={INK} strokeWidth="0.8"/></>,
    ];
    return (
      <svg width="14" height="14" viewBox="0 0 14 14" style={{ display: 'inline-block', verticalAlign: 'middle', marginRight: 5, flexShrink: 0 }}>
        {shapes[index % shapes.length]}
      </svg>
    );
  };

  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const headerOp = Easing.easeOutCubic(clamp(localTime / 0.6, 0, 1));
        const bodyOp = Easing.easeOutCubic(clamp((localTime - 2.4) / 0.7, 0, 1));

        const laneLeft = 720;
        const laneRight = 1230;
        const laneTop = 128;
        const laneH = 58;

        const tScrub = clamp((localTime - 0.4) / 4.0, 0, 1);

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome />

            {/* LEFT — text column */}
            <div style={{
              position: 'absolute', left: 40, top: 110, width: 450,
              fontFamily: HELV, color: INK,
              opacity: headerOp,
            }}>
              <div style={{
                fontSize: 44, fontWeight: 700, letterSpacing: '-0.028em', lineHeight: 1.04, marginTop: 12,
              }}>
                We listen to every<br/>sector, in every market,<br/>24/7.
              </div>
              <div style={{
                marginTop: 18, fontSize: 16, lineHeight: 1.6, color: MUTED,
                opacity: bodyOp,
              }}>
                Shifts begin as whispers — a new lab, a stranger cohort, a tool nobody named yet. We study how conviction moves before capital does, so the fellowship you launch catches the wave at its origin, not its crest.
              </div>
              <div style={{
                marginTop: 26, display: 'flex', gap: 28,
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.22em', color: INK,
                textTransform: 'uppercase',
                opacity: bodyOp,
              }}>
                <div>
                  <div style={{ opacity: 0.55 }}>MARKETS SCANNED</div>
                  <div style={{ fontSize: 22, letterSpacing: '-0.01em', marginTop: 4 }}>42</div>
                </div>
                <div>
                  <div style={{ opacity: 0.55 }}>SECTORS TRACKED</div>
                  <div style={{ fontSize: 22, letterSpacing: '-0.01em', marginTop: 4 }}>180+</div>
                </div>
                <div>
                  <div style={{ opacity: 0.55 }}>SIGNAL CADENCE</div>
                  <div style={{ fontSize: 22, letterSpacing: '-0.01em', marginTop: 4 }}>24/7</div>
                </div>
              </div>
            </div>

            {/* RIGHT — waveforms */}
            {sectors.map((name, i) => {
              const y = laneTop + i * laneH + laneH / 2;
              const appear = Easing.easeOutCubic(clamp((localTime - 0.5 - i * 0.08) / 0.5, 0, 1));
              const laneW = laneRight - laneLeft;
              const w = waveParams[i];

              const segments = 100;
              const points = [];
              for (let s = 0; s <= segments; s++) {
                const u = s / segments;
                const x = laneLeft + u * laneW;
                const base = Math.sin(u * 9 + localTime * w.speed + w.phase) * 0.5;
                const jitter = Math.sin(u * 27 + localTime * 0.6 + w.phase * 1.3) * 0.18;
                const d = u - w.spikeAt;
                const spike = Math.exp(-(d * d) / (w.spikeWidth * w.spikeWidth)) *
                              (1.2 * w.baseAmp * Easing.easeOutCubic(tScrub));
                const v = (base + jitter) * w.baseAmp - spike;
                const py = y + v * (laneH * 0.35);
                points.push(`${x.toFixed(1)},${py.toFixed(1)}`);
              }

              return (
                <div key={i} style={{ opacity: appear }}>
                  {/* Label with icon */}
                  <div style={{
                    position: 'absolute', left: laneLeft - 30, top: y - 7,
                    transform: 'translateX(-100%)',
                    display: 'flex', alignItems: 'center',
                    fontFamily: MONO, fontSize: 9.5, letterSpacing: '0.18em',
                    color: INK, opacity: 0.7, textTransform: 'uppercase',
                    whiteSpace: 'nowrap',
                  }}>
                    <SectorIcon index={i}/>
                    {name}
                  </div>
                  <svg
                    style={{ position: 'absolute', left: laneLeft, top: laneTop + i * laneH, width: laneW, height: laneH, overflow: 'visible' }}
                    viewBox={`${laneLeft} ${laneTop + i * laneH} ${laneW} ${laneH}`}>
                    <line x1={laneLeft} y1={y} x2={laneRight} y2={y}
                      stroke={INK} strokeWidth="0.4" opacity="0.12"/>
                    <polyline points={points.join(' ')} fill="none" stroke={INK} strokeWidth="1.2" opacity={0.8}/>
                    {(() => {
                      const u = tScrub;
                      const x = laneLeft + u * laneW;
                      const base = Math.sin(u * 9 + localTime * w.speed + w.phase) * 0.5;
                      const jitter = Math.sin(u * 27 + localTime * 0.6 + w.phase * 1.3) * 0.18;
                      const d = u - w.spikeAt;
                      const spike = Math.exp(-(d * d) / (w.spikeWidth * w.spikeWidth)) * 1.2 * w.baseAmp;
                      const v = (base + jitter) * w.baseAmp - spike;
                      const py = y + v * (laneH * 0.35);
                      return <circle cx={x} cy={py} r="2.5" fill={INK}/>;
                    })()}
                  </svg>
                </div>
              );
            })}
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 5 — TURN
// ════════════════════════════════════════════════════════════════════════════
function SceneTurn({ start, end }) {
  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const cx = 340, cy = 360;

        const pairs = [];
        const extraCount = 10;
        for (let i = 0; i < extraCount; i++) {
          const theta = (i / extraCount) * Math.PI * 2 + 0.3;
          const r = 210;
          pairs.push({
            ax: cx + Math.cos(theta) * r - 24,
            ay: cy + Math.sin(theta) * r * 0.7,
            bx: cx + Math.cos(theta) * r + 24,
            by: cy + Math.sin(theta) * r * 0.7,
            delay: 0.2 + i * 0.08,
          });
        }

        const founderIn = Easing.easeOutBack(clamp((localTime - 0.2) / 0.5, 0, 1));
        const investorIn = Easing.easeOutBack(clamp((localTime - 0.5) / 0.5, 0, 1));
        const centerLineOp = Easing.easeOutCubic(clamp((localTime - 0.7) / 0.6, 0, 1));
        const handoff = Easing.easeInOutCubic(clamp((localTime - 1.9) / 1.6, 0, 1));
        const scale = 1 - 0.5 * handoff;
        const arrowOp = Easing.easeOutCubic(clamp((localTime - 2.6) / 0.7, 0, 1));
        const bigTextOp = Easing.easeOutCubic(clamp((localTime - 2.9) / 0.7, 0, 1));

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome label="THE TURN" />

            {pairs.map((p, i) => {
              const appear = Easing.easeOutCubic(clamp((localTime - p.delay) / 0.5, 0, 1));
              const fd = 1 - handoff * 0.6;
              return (
                <React.Fragment key={i}>
                  <div style={{
                    position: 'absolute', left: p.ax - 5, top: p.ay - 5,
                    width: 10, height: 10, borderRadius: '50%', background: INK,
                    opacity: appear * fd * 0.55,
                  }}/>
                  <div style={{
                    position: 'absolute', left: p.bx - 5, top: p.by - 5,
                    width: 10, height: 10, borderRadius: '50%',
                    border: `1.5px solid ${INK}`, background: 'transparent',
                    opacity: appear * fd * 0.5,
                  }}/>
                  <div style={{
                    position: 'absolute',
                    left: Math.min(p.ax, p.bx) + 7, top: p.ay - 0.5,
                    width: (Math.abs(p.bx - p.ax) - 14) * appear, height: 1,
                    background: INK, opacity: appear * fd * 0.3,
                  }}/>
                </React.Fragment>
              );
            })}

            <div style={{
              position: 'absolute', left: cx, top: cy,
              transform: `translate(-50%, -50%) scale(${scale})`,
              transformOrigin: 'center', width: 0, height: 0,
            }}>
              <div style={{
                position: 'absolute', left: -60 - 14, top: -14,
                width: 28, height: 28, borderRadius: '50%', background: INK,
                transform: `scale(${founderIn})`, transformOrigin: 'center',
              }}/>
              <div style={{
                position: 'absolute', left: -60, top: 36,
                transform: 'translateX(-50%)',
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.18em', color: INK, textTransform: 'uppercase',
                opacity: founderIn,
              }}>FOUNDER</div>
              <div style={{
                position: 'absolute', left: 60 - 14, top: -14,
                width: 28, height: 28, borderRadius: '50%',
                background: 'transparent', border: `1.5px solid ${INK}`,
                transform: `scale(${investorIn})`, transformOrigin: 'center',
              }}/>
              <div style={{
                position: 'absolute', left: 60, top: 36,
                transform: 'translateX(-50%)',
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.18em', color: INK, textTransform: 'uppercase',
                opacity: investorIn,
              }}>CAPITAL</div>
              <div style={{
                position: 'absolute', left: -46, top: -0.5,
                width: 92 * centerLineOp, height: 1, background: INK,
              }}/>
              <div style={{
                position: 'absolute', left: 0, top: -30,
                transform: 'translateX(-50%)',
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.16em',
                color: INK, textTransform: 'uppercase', opacity: centerLineOp * 0.7,
                whiteSpace: 'nowrap',
              }}>CONVICTION</div>
            </div>

            <svg width="1280" height="720" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
              <defs>
                <marker id="arrowHead" viewBox="0 0 10 10" refX="9" refY="5"
                        markerWidth="6" markerHeight="6" orient="auto">
                  <path d="M 0 0 L 10 5 L 0 10 z" fill={INK}/>
                </marker>
              </defs>
              <path
                d={`M ${cx + 80} ${cy} Q 520 200 620 160`}
                stroke={INK} strokeWidth="1.2" fill="none"
                opacity={arrowOp * 0.8}
                strokeDasharray="360"
                strokeDashoffset={360 * (1 - arrowOp)}
                markerEnd="url(#arrowHead)"
              />
            </svg>

            <div style={{
              position: 'absolute', left: 620, top: 160,
              transform: 'translate(-50%, -50%)',
              opacity: arrowOp,
            }}>
              <div style={{
                width: 56, height: 56, borderRadius: '50%',
                border: `2px solid ${INK}`, background: BG,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.2em',
                color: INK, textTransform: 'uppercase',
              }}>YOU</div>
            </div>

            <div style={{
              position: 'absolute', right: 60, top: '50%',
              transform: `translateY(-50%) translateY(${(1 - bigTextOp) * 14}px)`,
              maxWidth: 500,
              fontFamily: HELV, color: INK,
              opacity: bigTextOp,
            }}>
              <div style={{
                fontSize: 38, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.1,
              }}>
                We already do this.<br/>
                One founder, one conviction,<br/>
                at a time.<br/>
                Now we hand you<br/>
                the instrument.
              </div>
              <div style={{
                marginTop: 20, fontSize: 15, lineHeight: 1.6, color: MUTED,
              }}>
                Built for you. Named after you. Tuned to the sector you know best and the founders only you would recognise.
              </div>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 6 — PATRONS
// ════════════════════════════════════════════════════════════════════════════
function ScenePatrons({ start, end }) {
  const groups = [
    { cx: 260, cy: 360, label: 'FAMILY OFFICES',
      sub: 'SECOND-GENERATION STEWARDS',
      pts: [[0,0],[50,-30],[-40,-40],[30,30],[-50,20],[60,0],[-30,50],[20,-60]] },
    { cx: 640, cy: 300, label: 'HIGH NET-WORTH BACKERS',
      sub: 'OPERATORS TURNED ALLOCATORS',
      pts: [[0,0],[40,-50],[-50,-30],[55,20],[-40,40],[25,-70],[-25,-60],[60,-10],[-60,10]] },
    { cx: 1020, cy: 360, label: 'NEW ASSET-CLASS SEEKERS',
      sub: 'INSTITUTIONS BUILDING ACCESS',
      pts: [[0,0],[-45,-30],[30,-40],[50,30],[-55,25],[0,-60]] },
  ];

  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const headerOp = Easing.easeOutCubic(clamp(localTime / 0.6, 0, 1));
        const bigTextOp = Easing.easeOutCubic(clamp((localTime - 3.2) / 0.7, 0, 1));

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome label="THE PATRONS" />

            <div style={{
              position: 'absolute', left: 40, top: 110, right: 40,
              display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end',
              fontFamily: HELV, color: INK,
              opacity: headerOp,
            }}>
              <div>
                <div style={{
                  fontSize: 40, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.06, marginTop: 8,
                }}>
                  Built for those who already know.
                </div>
              </div>
            </div>

            <svg width="1280" height="720" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
              {groups.map((g, gi) => {
                const appear = Easing.easeOutCubic(clamp((localTime - 0.4 - gi * 0.3) / 0.8, 0, 1));
                const fx = Math.sin(localTime * 0.4 + gi) * 6;
                const fy = Math.cos(localTime * 0.4 + gi * 1.3) * 4;
                const pairs = [[0,1],[0,2],[1,3],[2,4],[0,5],[4,6],[3,7]];
                return (
                  <g key={gi} opacity={appear * 0.9}>
                    {pairs.map(([a, b], pi) => {
                      if (!g.pts[a] || !g.pts[b]) return null;
                      return (
                        <line key={pi}
                          x1={g.cx + g.pts[a][0] + fx} y1={g.cy + g.pts[a][1] + fy}
                          x2={g.cx + g.pts[b][0] + fx} y2={g.cy + g.pts[b][1] + fy}
                          stroke={INK} strokeWidth="0.5" opacity="0.25"/>
                      );
                    })}
                    {g.pts.map((p, pi) => {
                      const r = pi === 0 ? 7 : 3 + (pi % 3);
                      const pulse = pi === 0 ? 1 + 0.15 * Math.sin(localTime * 2 + gi) : 1;
                      return (
                        <circle key={pi}
                          cx={g.cx + p[0] + fx} cy={g.cy + p[1] + fy}
                          r={r * pulse} fill={INK}
                          opacity={pi === 0 ? 1 : 0.8}/>
                      );
                    })}
                  </g>
                );
              })}
            </svg>

            {groups.map((g, i) => {
              const appear = Easing.easeOutCubic(clamp((localTime - 0.8 - i * 0.3) / 0.6, 0, 1));
              return (
                <div key={i} style={{
                  position: 'absolute', left: g.cx, top: g.cy + 110,
                  transform: 'translateX(-50%)', textAlign: 'center',
                  fontFamily: MONO, color: INK, opacity: appear,
                }}>
                  <div style={{ fontSize: 11, letterSpacing: '0.22em', textTransform: 'uppercase' }}>{g.label}</div>
                  <div style={{ fontSize: 10, opacity: 0.55, letterSpacing: '0.18em', marginTop: 4, textTransform: 'uppercase' }}>{g.sub}</div>
                </div>
              );
            })}

            <div style={{
              position: 'absolute', left: 40, right: 40, bottom: 86,
              textAlign: 'center',
              opacity: bigTextOp,
              transform: `translateY(${(1 - bigTextOp) * 14}px)`,
            }}>
              <div style={{
                fontFamily: HELV, fontSize: 36, fontWeight: 700, letterSpacing: '-0.025em',
                lineHeight: 1.08, color: INK, maxWidth: 900, margin: '0 auto',
              }}>
                The instinct was always yours.<br/>Now the rails are too.
              </div>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 7 — INSIDE / OUTSIDE
// ════════════════════════════════════════════════════════════════════════════
function SceneInsideOutside({ start, end }) {
  const blooms = React.useMemo(() => {
    const r = rng(53);
    const arr = [];
    const bx = 940, by = 340;
    for (let i = 0; i < 36; i++) {
      const theta = r() * Math.PI * 2;
      const rad = Math.pow(r(), 0.5) * 200;
      arr.push({
        x: bx + Math.cos(theta) * rad,
        y: by + Math.sin(theta) * rad * 0.75,
        rot: r() * Math.PI * 2,
        size: 5 + r() * 10,
        delay: 0.7 + r() * 2.2,
      });
    }
    return arr;
  }, []);

  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const headerOp = Easing.easeOutCubic(clamp(localTime / 0.6, 0, 1));
        const bigTextOp = Easing.easeOutCubic(clamp((localTime - 2.8) / 0.7, 0, 1));

        const insideCX = 280, insideCY = 340;
        const floors = 5, floorW = 220, floorH = 26;
        const totalH = floors * floorH;
        const progress = Easing.easeInOutCubic(clamp((localTime - 0.4) / 5.0, 0, 1));
        const filled = progress * floors;

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome label="INSIDE / OUTSIDE" />

            <div style={{
              position: 'absolute', left: 40, top: 110, right: 40,
              fontFamily: HELV, color: INK, opacity: headerOp,
            }}>
              <div style={{
                fontSize: 40, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.06, marginTop: 8, maxWidth: 1000,
              }}>
                One takes decades.<br/>The other is happening right now.
              </div>
            </div>

            <svg width="600" height="320"
              style={{ position: 'absolute', left: insideCX - 300, top: insideCY - 140, pointerEvents: 'none' }}>
              {[...Array(floors)].map((_, i) => {
                const fi = floors - 1 - i;
                const y = insideCY - 140 + i * floorH + 40;
                const isFilled = fi < filled;
                const partial = Math.max(0, Math.min(1, filled - fi));
                return (
                  <g key={i}>
                    <rect x={insideCX - floorW / 2 - (insideCX - 300)} y={y - (insideCY - 140)}
                      width={floorW} height={floorH - 3}
                      fill="none" stroke={INK} strokeWidth="1" opacity="0.45"/>
                    <rect x={insideCX - floorW / 2 - (insideCX - 300)} y={y - (insideCY - 140)}
                      width={floorW * (isFilled ? 1 : partial)} height={floorH - 3}
                      fill={INK} opacity={isFilled ? 0.9 : 0.55}/>
                    <text x={insideCX - floorW / 2 - (insideCX - 300) + floorW + 12}
                          y={y - (insideCY - 140) + (floorH - 3) / 2 + 3}
                          fill={INK} fontFamily={MONO} fontSize="9" letterSpacing="2" opacity="0.6">
                      YR {(fi + 1) * 2}
                    </text>
                  </g>
                );
              })}
              <line x1={insideCX - floorW / 2 - (insideCX - 300)} y1={40}
                    x2={insideCX + floorW / 2 - (insideCX - 300)} y2={40}
                    stroke={INK} strokeWidth="1.5" opacity="0.7"/>
              <text x={insideCX - (insideCX - 300)} y={totalH + 80}
                    textAnchor="middle" fill={INK} fontFamily={MONO} fontSize="11" letterSpacing="3" opacity="0.8">
                INSIDE THE BUILDING
              </text>
              <text x={insideCX - (insideCX - 300)} y={totalH + 98}
                    textAnchor="middle" fill={INK} fontFamily={MONO} fontSize="9" letterSpacing="2" opacity="0.5">
                INCREMENTAL. APPROVAL-BOUND.
              </text>
            </svg>

            {blooms.map((b, i) => {
              const appear = Easing.easeOutBack(clamp((localTime - b.delay) / 0.5, 0, 1));
              const sway = Math.sin(localTime * 1.4 + i) * 3;
              const s = b.size;
              return (
                <svg key={i} width={s * 2} height={s * 2}
                  style={{
                    position: 'absolute', left: b.x - s + sway, top: b.y - s,
                    opacity: appear * 0.9,
                    transform: `rotate(${b.rot}rad) scale(${appear})`,
                  }}>
                  <circle cx={s} cy={s} r={s * 0.35} fill={INK}/>
                  <line x1={s} y1={0} x2={s} y2={s * 2} stroke={INK} strokeWidth="1" opacity="0.6"/>
                  <line x1={0} y1={s} x2={s * 2} y2={s} stroke={INK} strokeWidth="1" opacity="0.6"/>
                </svg>
              );
            })}

            <div style={{
              position: 'absolute', left: 940, top: 520,
              transform: 'translateX(-50%)', textAlign: 'center',
              fontFamily: MONO, fontSize: 11, letterSpacing: '0.22em',
              color: INK, textTransform: 'uppercase',
              opacity: Easing.easeOutCubic(clamp((localTime - 1.2) / 0.5, 0, 1)),
            }}>
              OUTSIDE THE BUILDING
              <div style={{ fontSize: 9, opacity: 0.55, letterSpacing: '0.18em', marginTop: 4 }}>
                UNRULY. ALREADY SHIPPING.
              </div>
            </div>

            <div style={{
              position: 'absolute', left: 620, top: 270, bottom: 240,
              width: 1, background: INK, opacity: 0.18,
            }}/>

            <div style={{
              position: 'absolute', left: 40, right: 40, bottom: 80,
              textAlign: 'center',
              opacity: bigTextOp,
              transform: `translateY(${(1 - bigTextOp) * 14}px)`,
            }}>
              <div style={{
                fontFamily: HELV, fontSize: 28, fontWeight: 700, letterSpacing: '-0.022em',
                lineHeight: 1.2, color: INK, maxWidth: 980, margin: '0 auto',
              }}>
                A fellowship surfaces what the world is already building,<br/>
                and puts your name on the relationship first.
              </div>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 8 — SYSTEM
// ════════════════════════════════════════════════════════════════════════════
function SceneSystem({ start, end }) {
  const steps = [
    { num: '01', title: 'Define your thesis',
      body: 'What do you believe in before the world does? We map your conviction to founder pools across 40+ sectors.',
      diagram: 'thesis' },
    { num: '02', title: 'Signal scan',
      body: 'Our engine surfaces founders whose trajectory matches your thesis — weeks before anyone else sees them.',
      diagram: 'signal' },
    { num: '03', title: 'Fellowship design',
      body: 'Your brand, your name, your terms. We build the identity, selection, and architecture around you.',
      diagram: 'design' },
    { num: '04', title: 'Cohort selection',
      body: 'Application scoring and shortlisting. You make the final call. The best founders come to you.',
      diagram: 'select' },
    { num: '05', title: 'Run and compound',
      body: 'Operations, mentorship, alumni OS. Every cohort compounds the last. Your network grows stronger each year.',
      diagram: 'compound' },
  ];

  const Diagram = ({ kind, t }) => {
    const size = 90, c = size / 2;
    switch (kind) {
      case 'thesis':
        return (
          <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
            <circle cx={c} cy={c} r={40} fill="none" stroke={INK} strokeWidth="1" opacity="0.3"/>
            <circle cx={c} cy={c} r={28} fill="none" stroke={INK} strokeWidth="0.5" opacity="0.2"/>
            {[0,45,90,135,180,225,270,315].map((deg, i) => (
              <line key={i} x1={c} y1={c - 42} x2={c} y2={c - 38}
                stroke={INK} strokeWidth="0.8" opacity="0.4"
                transform={`rotate(${deg} ${c} ${c})`}/>
            ))}
            <g transform={`rotate(${-60 + 120 * Math.min(1, t / 1.4)} ${c} ${c})`}>
              <line x1={c} y1={c + 6} x2={c} y2={c - 34} stroke={INK} strokeWidth="2"/>
              <polygon points={`${c},${c - 40} ${c - 5},${c - 30} ${c + 5},${c - 30}`} fill={INK}/>
            </g>
            <circle cx={c} cy={c} r="3" fill={INK}/>
          </svg>
        );
      case 'signal': {
        const pts = [];
        for (let i = 0; i <= 40; i++) {
          const u = i / 40;
          const y = c + Math.sin(u * 10 + t * 2) * 14 + Math.sin(u * 3 + t) * 4;
          pts.push(`${10 + u * (size - 20)},${y}`);
        }
        return (
          <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
            <line x1="8" y1={c} x2={size - 8} y2={c} stroke={INK} strokeWidth="0.4" opacity="0.2"/>
            <polyline points={pts.join(' ')} fill="none" stroke={INK} strokeWidth="1.2"/>
            <circle cx={size - 14} cy={c + Math.sin(10 + t * 2) * 14 + Math.sin(3 + t) * 4} r="3" fill={INK}/>
          </svg>
        );
      }
      case 'design':
        return (
          <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
            <rect x="8" y="8" width={size - 16} height={size - 16} fill="none" stroke={INK} strokeWidth="1" opacity="0.35"/>
            <rect x="18" y="18" width={size - 36} height={size - 36} fill="none" stroke={INK} strokeWidth="1" opacity="0.6"/>
            <rect x="28" y="28" width={size - 56} height={size - 56} fill={INK} opacity={0.4 + 0.5 * Math.min(1, t / 2)}/>
            <line x1="8" y1="8" x2="18" y2="18" stroke={INK} strokeWidth="0.5" opacity="0.3"/>
            <line x1={size - 8} y1="8" x2={size - 18} y2="18" stroke={INK} strokeWidth="0.5" opacity="0.3"/>
            <line x1="8" y1={size - 8} x2="18" y2={size - 18} stroke={INK} strokeWidth="0.5" opacity="0.3"/>
            <line x1={size - 8} y1={size - 8} x2={size - 18} y2={size - 18} stroke={INK} strokeWidth="0.5" opacity="0.3"/>
          </svg>
        );
      case 'select': {
        const out = [];
        for (let i = 0; i < 8; i++) {
          const passed = i < 3;
          out.push(<circle key={`r1-${i}`} cx={10 + i * 10} cy={16} r="3" fill={passed ? INK : 'none'} stroke={INK} strokeWidth="1" opacity={passed ? 1 : 0.35}/>);
        }
        for (let i = 0; i < 3; i++) {
          out.push(<circle key={`r2-${i}`} cx={30 + i * 14} cy={72} r="5" fill={INK}/>);
        }
        return (
          <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
            <line x1="6" y1="24" x2="30" y2="64" stroke={INK} strokeWidth="0.8" opacity="0.4"/>
            <line x1={size - 6} y1="24" x2={size - 30} y2="64" stroke={INK} strokeWidth="0.8" opacity="0.4"/>
            {out}
          </svg>
        );
      }
      case 'compound': {
        const ddots = [];
        const n = 24;
        const grown = Math.min(n, Math.floor(t * 14 + 3));
        const golden = Math.PI * (3 - Math.sqrt(5));
        for (let i = 0; i < grown; i++) {
          const r = Math.sqrt(i / n) * 36;
          const theta = i * golden;
          const x = c + Math.cos(theta) * r;
          const y = c + Math.sin(theta) * r;
          ddots.push(<circle key={i} cx={x} cy={y} r={1.5 + (i / n) * 2} fill={INK} opacity={0.4 + 0.6 * (i / n)}/>);
        }
        return (
          <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
            <circle cx={c} cy={c} r="40" fill="none" stroke={INK} strokeWidth="0.4" opacity="0.15"/>
            {ddots}
          </svg>
        );
      }
    }
    return null;
  };

  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const headerOp = Easing.easeOutCubic(clamp(localTime / 0.6, 0, 1));
        const railX = 140, firstY = 240, rowH = 78;

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome label="THE SYSTEM" />

            <div style={{
              position: 'absolute', left: 40, top: 140,
              fontFamily: HELV, color: INK, opacity: headerOp,
            }}>
              <div style={{
                fontSize: 40, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.05, marginTop: 8,
              }}>
                Here is how we do it.
              </div>
            </div>

            <div style={{
              position: 'absolute',
              left: railX - 0.5, top: firstY,
              width: 1, height: (steps.length - 1) * rowH * Easing.easeInOutCubic(clamp((localTime - 0.2) / 2.4, 0, 1)),
              background: INK, opacity: 0.35,
            }}/>

            {steps.map((step, i) => {
              const y = firstY + i * rowH;
              const delay = 0.4 + i * 0.2;
              const appear = Easing.easeOutCubic(clamp((localTime - delay) / 0.6, 0, 1));
              const filled = i % 2 === 0;
              return (
                <React.Fragment key={i}>
                  <div style={{
                    position: 'absolute', left: railX - 8, top: y - 8,
                    width: 16, height: 16, borderRadius: '50%',
                    background: filled ? INK : BG,
                    border: filled ? 'none' : `1.5px solid ${INK}`,
                    transform: `scale(${appear})`,
                  }}/>
                  <div style={{
                    position: 'absolute', left: 40, top: y - 8,
                    fontFamily: MONO, fontSize: 12, letterSpacing: '0.22em',
                    color: INK, opacity: appear * 0.55,
                  }}>{step.num}</div>
                  <div style={{
                    position: 'absolute', left: railX + 38, top: y - 24,
                    width: 760, opacity: appear,
                    transform: `translateY(${(1 - appear) * 10}px)`,
                  }}>
                    <div style={{
                      fontFamily: HELV, fontSize: 22, fontWeight: 700, letterSpacing: '-0.018em',
                      color: INK, lineHeight: 1.2,
                    }}>{step.title}</div>
                    <div style={{
                      marginTop: 6, fontFamily: HELV, fontSize: 14, lineHeight: 1.5, color: MUTED, maxWidth: 720,
                    }}>{step.body}</div>
                  </div>
                  <div style={{
                    position: 'absolute', right: 80, top: y - 45,
                    width: 90, height: 90, opacity: appear,
                    transform: `translateY(${(1 - appear) * 8}px)`,
                  }}>
                    <Diagram kind={step.diagram} t={Math.max(0, localTime - delay)}/>
                  </div>
                </React.Fragment>
              );
            })}
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 9 — LEDGER
// ════════════════════════════════════════════════════════════════════════════
function SceneLedger({ start, end }) {
  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const headerOp = Easing.easeOutCubic(clamp(localTime / 0.6, 0, 1));
        const t1 = Easing.easeOutCubic(clamp((localTime - 0.5) / 2.0, 0, 1));
        const t2 = Easing.easeOutCubic(clamp((localTime - 1.0) / 2.5, 0, 1));
        const t3 = Easing.easeOutCubic(clamp((localTime - 1.5) / 2.2, 0, 1));
        const bar1 = 1.3 * t1, bar2 = t2, bar3 = t3;

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome label="THE LEDGER" />
            <div style={{
              position: 'absolute', left: 40, top: 110,
              fontFamily: HELV, color: INK, maxWidth: 1040, opacity: headerOp,
            }}>
              <div style={{ fontFamily: MONO, fontSize: 11, letterSpacing: '0.22em', opacity: 0.55, textTransform: 'uppercase' }}>
                A short ledger
              </div>
              <div style={{
                fontSize: 48, fontWeight: 700, letterSpacing: '-0.028em', lineHeight: 1.04,
                marginTop: 12, maxWidth: 1000,
              }}>
                When conviction meets capital early,<br/>
                the outcome is not a return. It is a category.
              </div>
            </div>
            <div style={{
              position: 'absolute', left: 40, right: 40, top: 320, bottom: 96,
              display: 'flex', gap: 20,
            }}>
              {[
                { num: '$1.3T', sub: 'COMBINED VALUE OF CONVICTION-BACKED COHORTS', t: t1, bar: bar1 / 1.3 },
                { num: '700+',  sub: 'THIEL FELLOWS BACKED SINCE 2011',             t: t2, bar: bar2 },
                { num: '15yr',  sub: 'OF CAPITAL-FLOW INTELLIGENCE BEHIND YOU',     t: t3, bar: bar3 },
              ].map((s, i) => (
                <div key={i} style={{
                  flex: 1, border: `1px solid ${LINE}`, padding: '36px 32px',
                  display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
                  position: 'relative', overflow: 'hidden',
                  opacity: s.t, transform: `translateY(${(1 - s.t) * 20}px)`,
                }}>
                  <svg width="100%" height="140" style={{ position: 'absolute', left: 0, bottom: 0, opacity: 0.15 }}>
                    {[...Array(20)].map((_, j) => (
                      <line key={j} x1={10 + j * 18} y1={140} x2={10 + j * 18} y2={140 - s.bar * (40 + (j * 13 % 70))} stroke={INK} strokeWidth="1"/>
                    ))}
                  </svg>
                  <div style={{
                    fontFamily: HELV, color: INK, fontSize: 92, fontWeight: 700, letterSpacing: '-0.045em', lineHeight: 0.92,
                    fontVariantNumeric: 'tabular-nums', position: 'relative', zIndex: 1,
                  }}>{s.num}</div>
                  <div style={{
                    fontFamily: MONO, fontSize: 10, letterSpacing: '0.22em', color: INK, opacity: 0.7,
                    textTransform: 'uppercase', lineHeight: 1.5, position: 'relative', zIndex: 1,
                  }}>{s.sub}</div>
                </div>
              ))}
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 10 — AIRBNB STORY
// ════════════════════════════════════════════════════════════════════════════
function SceneAirbnb({ start, end }) {
  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.5);
        const headerOp = Easing.easeOutCubic(clamp(localTime / 0.6, 0, 1));
        const founderIn = Easing.easeOutBack(clamp((localTime - 0.4) / 0.5, 0, 1));
        const patronIn = Easing.easeOutBack(clamp((localTime - 0.9) / 0.5, 0, 1));
        const chequeT = clamp((localTime - 1.6) / 1.4, 0, 1);
        const stampOp = Easing.easeOutBack(clamp((localTime - 2.8) / 0.5, 0, 1));
        const companyOp = Easing.easeOutCubic(clamp((localTime - 3.4) / 0.7, 0, 1));
        const quoteOp = Easing.easeOutCubic(clamp((localTime - 0.8) / 0.8, 0, 1));

        const fx = 220, fy = 400;
        const px = 900, py = 400;
        const cx_ = fx + (px - fx) * chequeT;

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome label="A FIRST CHEQUE" />

            <div style={{
              position: 'absolute', left: 40, top: 110, maxWidth: 900,
              fontFamily: HELV, color: INK, opacity: headerOp,
            }}>
              <div style={{
                fontSize: 40, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.05, marginTop: 10,
              }}>
                Every category begins<br/>before the market names it.
              </div>
            </div>

            <svg width="1280" height="720" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
              <line x1={fx} y1={fy} x2={px} y2={py} stroke={INK} strokeWidth="1" strokeDasharray="3 5" opacity={0.3}/>
            </svg>

            <div style={{
              position: 'absolute', left: fx, top: fy,
              transform: `translate(-50%, -50%) scale(${founderIn})`,
            }}>
              <div style={{ width: 30, height: 30, borderRadius: '50%', background: INK }}/>
              <div style={{
                position: 'absolute', left: '50%', top: 40, transform: 'translateX(-50%)',
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.22em', color: INK, textTransform: 'uppercase',
                whiteSpace: 'nowrap', textAlign: 'center',
              }}>
                FOUNDER
                <div style={{ opacity: 0.55, marginTop: 3, fontSize: 9 }}>2008</div>
              </div>
            </div>

            <div style={{
              position: 'absolute', left: px, top: py,
              transform: `translate(-50%, -50%) scale(${patronIn})`,
            }}>
              <div style={{ width: 30, height: 30, borderRadius: '50%', background: 'transparent', border: `2px solid ${INK}` }}/>
              <div style={{
                position: 'absolute', left: '50%', top: 40, transform: 'translateX(-50%)',
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.22em', color: INK, textTransform: 'uppercase',
                whiteSpace: 'nowrap', textAlign: 'center',
              }}>
                PATRON
                <div style={{ opacity: 0.55, marginTop: 3, fontSize: 9 }}>ONE CHEQUE</div>
              </div>
            </div>

            {chequeT > 0 && chequeT < 1 && (
              <div style={{
                position: 'absolute', left: cx_, top: fy,
                transform: 'translate(-50%, -50%)',
              }}>
                <div style={{
                  width: 42, height: 22, background: BG, border: `1.5px solid ${INK}`,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontFamily: MONO, fontSize: 8, letterSpacing: '0.14em', color: INK,
                  boxShadow: '2px 2px 0 rgba(242,240,236,0.15)',
                }}>$20K</div>
              </div>
            )}

            {stampOp > 0 && (
              <div style={{
                position: 'absolute', left: px - 60, top: py - 80,
                fontFamily: MONO, fontSize: 10, letterSpacing: '0.2em',
                color: INK, textTransform: 'uppercase',
                opacity: stampOp, transform: `rotate(-6deg) scale(${stampOp})`,
              }}>
                <div style={{ border: `1.5px solid ${INK}`, padding: '6px 10px' }}>CONVICTION BACKED</div>
              </div>
            )}

            <div style={{
              position: 'absolute', left: px + 50, top: py + 40,
              fontFamily: HELV, color: INK,
              opacity: companyOp,
              transform: `translateY(${(1 - companyOp) * 10}px)`,
              maxWidth: 300,
            }}>
              <div style={{
                fontFamily: MONO, fontSize: 9, letterSpacing: '0.28em', color: INK, opacity: 0.55,
                textTransform: 'uppercase', marginBottom: 10,
              }}>AIRBNB — 2009</div>
              <div style={{
                fontSize: 30, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.05,
              }}>
                $20K became<br/>$100B.
              </div>
              <div style={{
                marginTop: 10, fontSize: 13, color: MUTED, lineHeight: 1.5,
              }}>
                The earliest cheque didn't buy equity.<br/>It bought belief.
              </div>
            </div>

            <div style={{
              position: 'absolute', left: 40, bottom: 96, maxWidth: 520,
              fontFamily: HELV, color: INK, opacity: quoteOp,
            }}>
              <div style={{
                fontSize: 26, fontStyle: 'italic', lineHeight: 1.35, letterSpacing: '-0.01em',
              }}>
                Airbnb&apos;s first cheque came<br/>
                from someone who saw the founder<br/>
                before they saw the business.
              </div>
              <div style={{
                marginTop: 14, fontFamily: MONO, fontSize: 10, letterSpacing: '0.22em',
                color: INK, opacity: 0.55, textTransform: 'uppercase',
              }}>— conviction, earlier than the market</div>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 11 — OUTCOME
// ════════════════════════════════════════════════════════════════════════════
function SceneOutcome({ start, end }) {
  const fellows = React.useMemo(() => {
    const arr = [];
    const fcx = 420, fcy = 370;
    const n = 80;
    const golden = Math.PI * (3 - Math.sqrt(5));
    for (let i = 0; i < n; i++) {
      const r = Math.sqrt(i / n) * 220;
      const theta = i * golden;
      arr.push({
        x: fcx + Math.cos(theta) * r,
        y: fcy + Math.sin(theta) * r * 0.85,
        size: 3 + (i % 4),
        delay: 0.4 + (i / n) * 2.4,
      });
    }
    return arr;
  }, []);

  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.5, 0.6);
        const cx = 420, cy = 370;
        const youIn = Easing.easeOutBack(clamp(localTime / 0.7, 0, 1));
        const textOp = Easing.easeOutCubic(clamp((localTime - 1.6) / 0.8, 0, 1));
        const countT = Easing.easeOutCubic(clamp((localTime - 2.0) / 2.5, 0, 1));
        const years = ['YR 1', 'YR 2', 'YR 3', 'YR 4', 'YR 5'];
        const year = Math.min(years.length, Math.floor(1 + localTime * 0.8));

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o }}>
            <SceneChrome />

            <svg width="1280" height="720" style={{ position: 'absolute', inset: 0 }}>
              {fellows.map((f, i) => {
                const appear = Easing.easeOutCubic(clamp((localTime - f.delay) / 0.5, 0, 1));
                return (
                  <line key={i} x1={cx} y1={cy} x2={f.x} y2={f.y}
                    stroke={INK} strokeWidth="0.5" opacity={appear * 0.14}/>
                );
              })}
            </svg>

            {fellows.map((f, i) => {
              const appear = Easing.easeOutCubic(clamp((localTime - f.delay) / 0.5, 0, 1));
              return (
                <div key={i} style={{
                  position: 'absolute',
                  left: f.x - f.size / 2, top: f.y - f.size / 2,
                  width: f.size, height: f.size,
                  borderRadius: '50%', background: INK,
                  opacity: appear * 0.92,
                  transform: `scale(${appear})`,
                }}/>
              );
            })}

            <div style={{
              position: 'absolute', left: cx - 16, top: cy - 16,
              width: 32, height: 32, borderRadius: '50%', background: INK,
              transform: `scale(${youIn})`, transformOrigin: 'center',
            }}/>
            <div style={{ position: 'absolute', left: cx - 40, top: cy - 40, width: 80, height: 80, borderRadius: '50%', border: `1px solid ${INK}`, opacity: 0.25 }}/>
            <div style={{ position: 'absolute', left: cx - 60, top: cy - 60, width: 120, height: 120, borderRadius: '50%', border: `1px solid ${INK}`, opacity: 0.12 }}/>

            {/* RIGHT text lane */}
            <div style={{
              position: 'absolute', right: 60, top: 140, width: 520,
              fontFamily: HELV, color: INK,
              opacity: textOp,
              transform: `translateY(${(1 - textOp) * 14}px)`,
            }}>
              <div style={{
                fontSize: 48, fontWeight: 700, letterSpacing: '-0.028em', lineHeight: 1.04,
                marginTop: 12,
              }}>
                Back a constellation<br/>
                that compounds.
              </div>
              <div style={{
                marginTop: 16, fontSize: 15, lineHeight: 1.55, color: MUTED,
              }}>
                Every founder you back, every alumnus who compounds, makes the next cohort stronger. Your name becomes the reason they apply.
              </div>

              <div style={{ marginTop: 60, display: 'flex', gap: 40 }}>
                <div>
                  <div style={{ fontFamily: MONO, fontSize: 10, letterSpacing: '0.22em', opacity: 0.55, textTransform: 'uppercase' }}>
                    NETWORK GROWTH
                  </div>
                  <div style={{
                    fontFamily: HELV, fontSize: 44, fontWeight: 700, letterSpacing: '-0.028em',
                    fontVariantNumeric: 'tabular-nums', marginTop: 4,
                  }}>
                    {formatNum(80 * countT)}<span style={{ fontSize: 16, opacity: 0.5 }}> fellows</span>
                  </div>
                </div>
                <div>
                  <div style={{ fontFamily: MONO, fontSize: 10, letterSpacing: '0.22em', opacity: 0.55, textTransform: 'uppercase' }}>
                    COHORTS
                  </div>
                  <div style={{ fontFamily: MONO, fontSize: 14, marginTop: 8, letterSpacing: '0.18em' }}>
                    {years.map((y, i) => (
                      <span key={i} style={{ display: 'inline-block', marginRight: 10, opacity: i < year ? 0.9 : 0.25 }}>
                        {y}
                        <span style={{ display: 'inline-block', width: 6, height: 6, borderRadius: '50%', background: INK, marginLeft: 4, verticalAlign: 'middle', opacity: i < year ? 1 : 0.15 }}/>
                      </span>
                    ))}
                  </div>
                </div>
              </div>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// 12 — OUTRO
// ════════════════════════════════════════════════════════════════════════════
function SceneOutro({ start, end }) {
  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const o = fade(localTime, duration, 0.3, 0);

        return (
          <div style={{ position: 'absolute', inset: 0, opacity: o, background: BG }}>
            {/* Center block */}
            <div style={{
              position: 'absolute', left: '50%', top: '50%',
              transform: 'translate(-50%, -50%)',
              textAlign: 'center', width: 1000,
            }}>
              {/* Infinity emblem */}
              <div style={{
                marginBottom: 26,
                opacity: Easing.easeOutCubic(clamp(localTime / 0.25, 0, 1)),
              }}>
                <svg width="54" height="27" viewBox="0 0 240 120" style={{ display: 'inline-block' }}>
                  <path
                    d="M 120,60 C 134,36 172,36 172,60 C 172,84 134,84 120,60 C 106,36 68,36 68,60 C 68,84 106,84 120,60"
                    fill="none" stroke={INK} strokeWidth="8" strokeLinecap="round" opacity="0.85"
                  />
                </svg>
              </div>

              {/* Primary line */}
              {(() => {
                const t1 = Easing.easeOutCubic(clamp((localTime - 0.1) / 0.4, 0, 1));
                return (
                  <div style={{
                    fontFamily: HELV, fontSize: 58, fontWeight: 700, letterSpacing: '-0.035em',
                    lineHeight: 1.02, color: INK,
                    opacity: t1,
                    transform: `translateY(${(1 - t1) * 16}px)`,
                  }}>
                    The next great conviction engine<br/>begins with you.
                  </div>
                );
              })()}

              {/* Secondary line */}
              {(() => {
                const t2 = Easing.easeOutCubic(clamp((localTime - 0.2) / 0.4, 0, 1));
                return (
                  <div style={{
                    marginTop: 10,
                    fontFamily: HELV, fontSize: 44, fontWeight: 700, letterSpacing: '-0.03em',
                    lineHeight: 1.0, color: INK,
                    opacity: t2,
                    transform: `translateY(${(1 - t2) * 12}px)`,
                  }}>
                    Build your Fellowship.
                  </div>
                );
              })()}

              {/* Body */}
              <div style={{
                marginTop: 22,
                fontFamily: HELV, fontSize: 17, color: MUTED, lineHeight: 1.5,
                opacity: Easing.easeOutCubic(clamp((localTime - 0.35) / 0.35, 0, 1)),
                maxWidth: 560, marginLeft: 'auto', marginRight: 'auto',
                fontStyle: 'italic',
              }}>
                We bring the engine, the signal and the network.<br/>You bring the conviction.
              </div>

              {/* CTA */}
              <div style={{
                marginTop: 36,
                display: 'inline-flex', alignItems: 'center', gap: 12,
                padding: '15px 28px',
                border: `1.5px solid ${INK}`, borderRadius: 999,
                fontFamily: MONO, fontSize: 11, letterSpacing: '0.24em',
                textTransform: 'uppercase', color: INK,
                opacity: Easing.easeOutCubic(clamp((localTime - 0.55) / 0.35, 0, 1)),
                cursor: 'pointer',
              }}>
                Learn more
                <span style={{ fontSize: 13 }}>→</span>
              </div>
            </div>

            {/* Footer */}
            <div style={{
              position: 'absolute', bottom: 32, left: 40, right: 40,
              display: 'flex', justifyContent: 'space-between', alignItems: 'center',
              fontFamily: MONO, fontSize: 10, letterSpacing: '0.22em', color: INK, opacity: 0.5,
              textTransform: 'uppercase',
            }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <svg width="22" height="13" viewBox="0 0 240 120">
                  <path
                    d="M 120,60 C 134,36 172,36 172,60 C 172,84 134,84 120,60 C 106,36 68,36 68,60 C 68,84 106,84 120,60"
                    fill="none" stroke={INK} strokeWidth="16" strokeLinecap="round"
                  />
                </svg>
                <span>Vaomi AI</span>
              </div>
              <span>© 2026 Vaomi. All rights reserved</span>
            </div>
          </div>
        );
      }}
    </Sprite>
  );
}

// SceneEngine is an alias for backward compatibility
const SceneEngine = SceneSignal;

Object.assign(window, {
  SceneInvocation, ScenePrologue, SceneParadox, SceneQuestion, SceneSignal, SceneEngine,
  SceneTurn, ScenePatrons, SceneInsideOutside, SceneSystem, SceneLedger,
  SceneAirbnb, SceneOutcome, SceneOutro,
  INK, BG, MUTED, LINE, HELV, MONO, formatNum,
});
