// pitch.jsx — 38-second pitch reel for Cooking For Gains redesign

// ─── Brand palette ──────────────────────────────────────────────────────────
const C = {
  bg: '#0a0a0a',
  ink: '#f5f0e6',
  muted: '#9a9a98',
  accent: '#ff4d1c',
  accent2: '#ffb800',
  cream: '#f5f0e6',
  char: '#141414',
};
const ANTON = "'Anton', Impact, sans-serif";
const INTER = "'Inter', system-ui, sans-serif";

// ─── Orientation hook ───────────────────────────────────────────────────────
// Returns true when the viewport is taller than it is wide (phones in
// portrait, narrow windows). Re-renders on resize / orientationchange so
// scenes can swap layouts live if the user rotates mid-pitch.
function useIsPortrait() {
  const [portrait, setPortrait] = React.useState(() => {
    if (typeof window === 'undefined') return false;
    return window.innerHeight > window.innerWidth;
  });
  React.useEffect(() => {
    const onResize = () => setPortrait(window.innerHeight > window.innerWidth);
    window.addEventListener('resize', onResize);
    window.addEventListener('orientationchange', onResize);
    return () => {
      window.removeEventListener('resize', onResize);
      window.removeEventListener('orientationchange', onResize);
    };
  }, []);
  return portrait;
}

// ─── Helpers ────────────────────────────────────────────────────────────────
function FilmGrain({ opacity = 0.18 }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 50,
      opacity, mixBlendMode: 'overlay',
      backgroundImage: "url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.5'/></svg>\")",
    }} />
  );
}

function Vignette() {
  return (
    <div style={{
      position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 49,
      background: 'radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,0.55) 100%)',
    }} />
  );
}

// Animated text that types/reveals with a clip-path wipe
function WipeText({ children, delay = 0, dur = 0.6, ease = Easing.easeOutQuart, style }) {
  const { localTime } = useSprite();
  const t = clamp((localTime - delay) / dur, 0, 1);
  const e = ease(t);
  return (
    <div style={{
      ...style,
      clipPath: `inset(0 ${(1 - e) * 100}% 0 0)`,
      opacity: t > 0 ? 1 : 0,
    }}>
      {children}
    </div>
  );
}

function FadeUp({ children, delay = 0, dur = 0.5, dist = 18, style }) {
  const { localTime } = useSprite();
  const t = clamp((localTime - delay) / dur, 0, 1);
  const e = Easing.easeOutCubic(t);
  return (
    <div style={{
      ...style,
      opacity: e,
      transform: `translateY(${(1 - e) * dist}px)`,
    }}>{children}</div>
  );
}

// ─── SCENE 1: Opener "Hey, Cameron." ────────────────────────────────────────
function Opener() {
  const { localTime, duration } = useSprite();
  const isPortrait = useIsPortrait();
  const exitT = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);

  // Layout sizes per orientation
  const padX = isPortrait ? 70 : 140;
  const eyebrowSize = isPortrait ? 22 : 18;
  const headlineSize = isPortrait ? 200 : 280;
  const leadSize = isPortrait ? 32 : 28;
  const leadMaxW = isPortrait ? 880 : 760;

  return (
    <div style={{
      position: 'absolute', inset: 0, background: C.bg,
      display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'flex-start',
      padding: `0 ${padX}px`,
      opacity: 1 - exitT,
    }}>
      {/* dot/eyebrow */}
      <FadeUp delay={0} dur={0.4} style={{ marginBottom: isPortrait ? 36 : 28 }}>
        <div style={{
          display: 'inline-flex', alignItems: 'center', gap: 14,
          color: C.accent, fontFamily: INTER, fontWeight: 800,
          fontSize: eyebrowSize, letterSpacing: '0.24em', textTransform: 'uppercase',
        }}>
          <span style={{ width: 10, height: 10, borderRadius: '50%', background: C.accent, boxShadow: `0 0 22px ${C.accent}` }} />
          A pitch · for Cameron Dukes
        </div>
      </FadeUp>

      <WipeText delay={0.35} dur={0.55} style={{
        fontFamily: ANTON, fontSize: headlineSize, lineHeight: 0.88, letterSpacing: '-0.015em',
        color: C.ink, textTransform: 'uppercase',
      }}>HEY,</WipeText>

      <WipeText delay={0.7} dur={0.7} style={{
        fontFamily: ANTON, fontSize: headlineSize, lineHeight: 0.88, letterSpacing: '-0.015em',
        color: C.accent, textTransform: 'uppercase',
      }}>CAMERON.</WipeText>

      <FadeUp delay={1.5} dur={0.45} style={{ marginTop: isPortrait ? 56 : 42 }}>
        <div style={{ fontFamily: INTER, fontSize: leadSize, color: '#cfcbc1', maxWidth: leadMaxW, lineHeight: 1.4 }}>
          I saw your shop. Here's the website I think you actually deserve.
        </div>
      </FadeUp>
    </div>
  );
}

// ─── SCENE 2: Current site critique ─────────────────────────────────────────
function CurrentSite() {
  const { localTime, duration } = useSprite();
  const isPortrait = useIsPortrait();
  const exitT = clamp((localTime - (duration - 0.5)) / 0.5, 0, 1);
  const enterT = Easing.easeOutCubic(clamp(localTime / 0.5, 0, 1));

  // Pin positions hand-tuned per orientation so callouts ring the browser
  // mockup without overlapping. Landscape: 1920\u00d71080 frame ~1720 wide.
  // Portrait: 1080\u00d71920 frame ~960 wide and much taller.
  const pinsLandscape = [
    { t: 1.4, top: 130, left: 880, label: 'Generic template', dir: 'right' },
    { t: 2.2, top: 380, left: 720, label: 'No story, no voice', dir: 'right' },
    { t: 3.0, top: 680, left: 200, label: 'Programs buried in a grid', dir: 'left' },
    { t: 3.8, top: 100, left: 90,  label: 'Doesn\u2019t feel like you', dir: 'left' },
  ];
  const pinsPortrait = [
    { t: 1.4, top: 180,  left: 520, label: 'Generic template',          dir: 'right' },
    { t: 2.2, top: 560,  left: 460, label: 'No story, no voice',        dir: 'right' },
    { t: 3.0, top: 980,  left: 120, label: 'Programs buried in a grid', dir: 'left' },
    { t: 3.8, top: 1360, left: 80,  label: 'Doesn\u2019t feel like you', dir: 'left' },
  ];
  const pins = isPortrait ? pinsPortrait : pinsLandscape;

  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: '#1a1a1a',
      display: 'flex', flexDirection: 'column',
      opacity: 1 - exitT,
    }}>
      {/* label bar — side-by-side on desktop, stacked on portrait */}
      <div style={{
        padding: isPortrait ? '44px 70px 28px' : '36px 100px 24px',
        display: 'flex',
        flexDirection: isPortrait ? 'column' : 'row',
        alignItems: isPortrait ? 'flex-start' : 'baseline',
        justifyContent: 'space-between',
        gap: isPortrait ? 20 : 0,
        opacity: enterT, transform: `translateY(${(1 - enterT) * -10}px)`,
      }}>
        <div>
          <div style={{
            fontFamily: INTER, fontWeight: 800, fontSize: isPortrait ? 18 : 14,
            letterSpacing: '0.24em', textTransform: 'uppercase', color: C.accent, marginBottom: 8,
          }}>● Right now / cooking-for-gains.com</div>
          <div style={{
            fontFamily: ANTON,
            fontSize: isPortrait ? 110 : 72,
            color: C.ink, lineHeight: 0.95, textTransform: 'uppercase',
          }}>
            Honest critique.
          </div>
        </div>
        <div style={{
          fontFamily: INTER,
          fontSize: isPortrait ? 24 : 16,
          color: C.muted,
          maxWidth: isPortrait ? 900 : 420,
          textAlign: isPortrait ? 'left' : 'right',
          lineHeight: 1.5,
        }}>
          A Squarespace shop on a default template. The logo is doing all the work — your story isn't.
        </div>
      </div>

      {/* browser frame */}
      <div style={{
        flex: 1, position: 'relative',
        margin: isPortrait ? '0 60px 80px' : '0 100px 60px',
        background: '#fff', borderRadius: 14, overflow: 'hidden',
        boxShadow: '0 30px 80px rgba(0,0,0,0.6)',
        transform: `scale(${0.96 + 0.04 * enterT})`,
        transformOrigin: 'center top',
      }}>
        {/* chrome */}
        <div style={{
          height: isPortrait ? 52 : 38,
          background: '#ebebe8', display: 'flex', alignItems: 'center',
          padding: isPortrait ? '0 22px' : '0 18px',
          gap: isPortrait ? 12 : 10,
          borderBottom: '1px solid #d4d4d0',
        }}>
          <span style={{ width: isPortrait ? 16 : 12, height: isPortrait ? 16 : 12, borderRadius: '50%', background: '#ff5f57' }} />
          <span style={{ width: isPortrait ? 16 : 12, height: isPortrait ? 16 : 12, borderRadius: '50%', background: '#febc2e' }} />
          <span style={{ width: isPortrait ? 16 : 12, height: isPortrait ? 16 : 12, borderRadius: '50%', background: '#28c840' }} />
          <div style={{
            flex: 1, margin: isPortrait ? '0 40px' : '0 60px',
            background: '#fff', borderRadius: 6,
            padding: isPortrait ? '8px 16px' : '6px 14px',
            fontFamily: INTER, fontSize: isPortrait ? 16 : 13, color: '#666',
          }}>🔒 cooking-for-gains.com</div>
        </div>
        {/* iframe of the actual generic mockup */}
        <iframe
          src="before.html"
          style={{
            width: '100%',
            height: isPortrait ? 'calc(100% - 52px)' : 'calc(100% - 38px)',
            border: 0, display: 'block',
            background: '#fff',
          }}
          scrolling="no"
        />
        {/* annotation pins */}
        {pins.map((p, i) => <Pin key={i} {...p} now={localTime} portrait={isPortrait} />)}
      </div>
    </div>
  );
}

function Pin({ t, top, left, label, dir, now, portrait = false }) {
  const localT = clamp((now - t) / 0.4, 0, 1);
  const e = Easing.easeOutBack(localT);
  if (localT <= 0) return null;
  // Pin visuals are bigger on portrait so they read against a 1080×1920 canvas.
  const dotSize = portrait ? 30 : 22;
  const fontSize = portrait ? 22 : 16;
  const pad = portrait ? '14px 22px' : '10px 16px';
  const gap = portrait ? 16 : 12;
  return (
    <div style={{
      position: 'absolute', top, left,
      transform: `scale(${e})`, transformOrigin: dir === 'right' ? 'left center' : 'right center',
      opacity: localT,
      display: 'flex', alignItems: 'center', gap, zIndex: 5,
      flexDirection: dir === 'right' ? 'row' : 'row-reverse',
    }}>
      <div style={{
        width: dotSize, height: dotSize, borderRadius: '50%',
        background: C.accent,
        boxShadow: `0 0 0 4px rgba(255,77,28,0.25), 0 0 30px rgba(255,77,28,0.7)`,
        flexShrink: 0,
        animation: 'pulse 1.6s ease-in-out infinite',
      }} />
      <div style={{
        background: C.bg, color: C.ink, padding: pad, borderRadius: 6,
        fontFamily: INTER, fontWeight: 700, fontSize, whiteSpace: 'nowrap',
        border: `1px solid ${C.accent}`,
        boxShadow: '0 10px 30px rgba(0,0,0,0.5)',
      }}>{label}</div>
    </div>
  );
}

// ─── SCENE 3: Smash cut transition ──────────────────────────────────────────
function SmashCut() {
  const { localTime, duration } = useSprite();
  const isPortrait = useIsPortrait();
  // 3 quick flashes: black, orange, black, then text
  const flashes = [
    { start: 0, end: 0.12, color: '#fff' },
    { start: 0.12, end: 0.22, color: C.bg },
    { start: 0.22, end: 0.34, color: C.accent },
    { start: 0.34, end: 0.5, color: C.bg },
  ];
  let bg = C.bg;
  for (const f of flashes) {
    if (localTime >= f.start && localTime < f.end) { bg = f.color; break; }
  }

  const textT = clamp((localTime - 0.5) / 0.5, 0, 1);

  return (
    <div style={{
      position: 'absolute', inset: 0, background: bg,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      flexDirection: 'column',
      padding: isPortrait ? '0 60px' : '0',
    }}>
      <div style={{
        opacity: textT,
        transform: `scale(${0.85 + 0.15 * Easing.easeOutBack(textT)})`,
      }}>
        <div style={{
          fontFamily: INTER, fontWeight: 800, fontSize: isPortrait ? 24 : 22,
          letterSpacing: '0.28em', textTransform: 'uppercase', color: C.accent,
          marginBottom: isPortrait ? 32 : 24, textAlign: 'center',
        }}>So here's the pitch ↓</div>
        <div style={{
          fontFamily: ANTON,
          fontSize: isPortrait ? 200 : 240,
          color: C.ink, lineHeight: 0.86,
          textTransform: 'uppercase', textAlign: 'center', letterSpacing: '-0.015em',
        }}>
          {isPortrait ? (
            <React.Fragment>
              BUILD IT<br /><span style={{ color: C.accent }}>RIGHT.</span>
            </React.Fragment>
          ) : (
            <React.Fragment>
              BUILD IT <span style={{ color: C.accent }}>RIGHT.</span>
            </React.Fragment>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── SCENE 4: Redesign tour with scrolling iframe ───────────────────────────
function RedesignTour() {
  const { localTime, duration, visible } = useSprite();
  const isPortrait = useIsPortrait();
  const exitT = clamp((localTime - (duration - 0.5)) / 0.5, 0, 1);

  const ref = React.useRef(null);
  const [sections, setSections] = React.useState(null);

  const onLoad = React.useCallback(() => {
    try {
      const doc = ref.current.contentDocument;
      if (!doc) return;
      const get = (id, fallback) => {
        const el = doc.getElementById(id);
        return el ? el.offsetTop : fallback;
      };
      setSections({
        hero: 0,
        programs: get('programs', 1080),
        story: get('story', 2200),
        cookbooks: get('cookbooks', 3300),
        feed: get('feed', 4400),
        faq: get('faq', 5400),
      });
    } catch (e) { console.warn('iframe load', e); }
  }, []);

  // Tour beats: t in seconds within this sprite, label, sectionKey, sublabel
  const beats = [
    { at: 0.0,  hold: 4.0, key: 'hero',      n: '01', label: 'The hero you don\u2019t have yet', sub: 'A voice. A face. A reason to scroll.' },
    { at: 4.0,  hold: 4.0, key: 'programs',  n: '02', label: 'Programs, ranked',                 sub: 'Pricing in plain sight. One clear pick.' },
    { at: 8.0,  hold: 4.0, key: 'story',     n: '03', label: 'Your story, on the front page',    sub: 'Parking-lot stovetop. Block it in cream — it pops.' },
    { at: 12.0, hold: 4.0, key: 'cookbooks', n: '04', label: 'The cookbook, sold like a flagship', sub: 'Live updates. Bundle math. Save 25%.' },
    { at: 16.0, hold: 4.0, key: 'feed',      n: '05', label: 'Instagram, live in the grid',     sub: 'Your reels render here the moment you post. Daily content becomes daily traffic.' },
  ];

  // Compute target scrollY: hold on each beat, then quick transit to the next.
  // Each beat plateaus for (hold - transit) seconds, then slides over transit seconds.
  const TRANSIT = 0.9;
  let targetY = 0;
  if (sections) {
    const ys = beats.map(b => sections[b.key] || 0);
    const times = [];
    const values = [];
    for (let i = 0; i < beats.length; i++) {
      const start = beats[i].at;
      const transitStart = start + Math.max(0, beats[i].hold - TRANSIT);
      times.push(start, transitStart);
      values.push(ys[i], ys[i]);
      // Add transit endpoint to NEXT beat's start
      if (i < beats.length - 1) {
        times.push(beats[i + 1].at);
        values.push(ys[i + 1]);
      }
    }
    targetY = interpolate(times, values, Easing.easeInOutCubic)(localTime);
  }

  // Apply scroll
  React.useEffect(() => {
    const f = ref.current;
    if (!f || !sections) return;
    try { f.contentWindow.scrollTo(0, targetY); } catch (e) {}
  }, [targetY, sections]);

  // Subtle zoom over time so it feels alive
  const z = 1 + Math.sin(localTime / duration * Math.PI) * 0.015;

  return (
    <div style={{
      position: 'absolute', inset: 0, background: C.bg, overflow: 'hidden',
      opacity: visible ? 1 - exitT : 0,
      visibility: visible ? 'visible' : 'hidden',
      pointerEvents: visible ? 'auto' : 'none',
    }}>
      {/* iframe (the redesign) */}
      <div style={{
        position: 'absolute', inset: 0,
        transform: `scale(${z})`, transformOrigin: 'center center',
      }}>
        <iframe
          ref={ref}
          src="redesign.html"
          onLoad={onLoad}
          style={{ width: '100%', height: '100%', border: 0, display: 'block', background: C.bg }}
          scrolling="no"
        />
      </div>

      {/* top gradient + bottom gradient so callouts sit clean over it */}
      <div style={{
        position: 'absolute', inset: 0, pointerEvents: 'none',
        background: 'linear-gradient(180deg, rgba(0,0,0,0.55) 0%, transparent 18%, transparent 72%, rgba(0,0,0,0.85) 100%)',
      }} />

      {/* beat callout (bottom-left on landscape, bottom-full on portrait) */}
      {beats.map((b, i) => {
        const inT = clamp((localTime - b.at) / 0.45, 0, 1);
        const outT = clamp((localTime - (b.at + b.hold - 0.3)) / 0.3, 0, 1);
        const vis = inT * (1 - outT);
        if (vis <= 0.01) return null;
        const e = Easing.easeOutCubic(inT);
        return (
          <div key={i} style={{
            position: 'absolute',
            left: isPortrait ? 60 : 100,
            right: isPortrait ? 60 : 'auto',
            bottom: isPortrait ? 120 : 80,
            maxWidth: isPortrait ? undefined : 880,
            opacity: vis,
            transform: `translateY(${(1 - e) * 24}px)`,
          }}>
            <div style={{
              display: 'inline-flex', alignItems: 'center', gap: 14,
              marginBottom: isPortrait ? 28 : 22, padding: '8px 16px',
              background: 'rgba(255,77,28,0.15)', border: `1px solid ${C.accent}`,
              borderRadius: 999,
              color: C.accent, fontFamily: INTER, fontWeight: 800,
              fontSize: isPortrait ? 18 : 15, letterSpacing: '0.22em', textTransform: 'uppercase',
            }}>
              <span style={{ fontFamily: ANTON, fontSize: isPortrait ? 22 : 18, color: C.accent2, letterSpacing: 0 }}>{b.n}</span>
              <span style={{ width: 6, height: 6, borderRadius: '50%', background: C.accent }} />
              {b.key}
            </div>
            <div style={{
              fontFamily: ANTON,
              fontSize: isPortrait ? 110 : 96,
              color: C.ink, lineHeight: 0.92,
              textTransform: 'uppercase', letterSpacing: '-0.01em',
              textShadow: '0 4px 30px rgba(0,0,0,0.7)',
              maxWidth: isPortrait ? undefined : 1100,
            }}>{b.label}</div>
            <div style={{
              fontFamily: INTER,
              fontSize: isPortrait ? 28 : 22,
              color: '#dcd8cf', marginTop: isPortrait ? 22 : 14,
              maxWidth: isPortrait ? undefined : 700,
              lineHeight: 1.45,
              textShadow: '0 2px 12px rgba(0,0,0,0.8)',
            }}>{b.sub}</div>
          </div>
        );
      })}

      {/* progress dots: top-right on landscape, top-center on portrait */}
      <div style={{
        position: 'absolute',
        top: isPortrait ? 50 : 50,
        right: isPortrait ? undefined : 100,
        left: isPortrait ? '50%' : undefined,
        transform: isPortrait ? 'translateX(-50%)' : undefined,
        display: 'flex',
        gap: isPortrait ? 14 : 10,
        alignItems: 'center',
        background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(8px)',
        padding: isPortrait ? '18px 26px' : '14px 20px',
        borderRadius: 999, border: '1px solid rgba(255,255,255,0.1)',
      }}>
        <div style={{
          fontFamily: INTER, fontWeight: 800,
          fontSize: isPortrait ? 16 : 13,
          letterSpacing: '0.2em', textTransform: 'uppercase', color: C.ink, marginRight: 8,
        }}>The tour</div>
        {beats.map((b, i) => {
          const active = localTime >= b.at && localTime < b.at + b.hold;
          const passed = localTime >= b.at + b.hold;
          const dotH = isPortrait ? 12 : 10;
          return (
            <div key={i} style={{
              width: active ? (isPortrait ? 38 : 32) : (isPortrait ? 12 : 10),
              height: dotH, borderRadius: 999,
              background: active ? C.accent : (passed ? 'rgba(255,77,28,0.4)' : 'rgba(255,255,255,0.2)'),
              transition: 'width 0.3s, background 0.3s',
            }} />
          );
        })}
      </div>
    </div>
  );
}

// ─── SCENE 5: Closer ────────────────────────────────────────────────────────
function Closer() {
  const { localTime, duration } = useSprite();
  const isPortrait = useIsPortrait();
  const t = clamp(localTime / 0.6, 0, 1);

  return (
    <div style={{
      position: 'absolute', inset: 0, background: C.bg,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      flexDirection: 'column',
      padding: isPortrait ? '0 60px' : '0 140px',
    }}>
      {/* radial glow */}
      <div style={{
        position: 'absolute', inset: 0,
        background: `radial-gradient(50% 60% at 50% 50%, rgba(255,77,28,0.18), transparent 65%)`,
      }} />

      <div style={{ position: 'relative', textAlign: 'center', zIndex: 2 }}>
        <FadeUp delay={0} dur={0.5} style={{ marginBottom: isPortrait ? 38 : 30 }}>
          <div style={{
            display: 'inline-flex', alignItems: 'center', gap: 14,
            color: C.accent, fontFamily: INTER, fontWeight: 800,
            fontSize: isPortrait ? 22 : 18, letterSpacing: '0.28em', textTransform: 'uppercase',
          }}>
            <span style={{ width: 40, height: 1, background: C.accent }} />
            That's the pitch
            <span style={{ width: 40, height: 1, background: C.accent }} />
          </div>
        </FadeUp>

        <WipeText delay={0.3} dur={0.55} style={{
          fontFamily: ANTON,
          fontSize: isPortrait ? 170 : 220,
          lineHeight: 0.88, letterSpacing: '-0.015em',
          color: C.ink, textTransform: 'uppercase',
        }}>LET'S BUILD</WipeText>

        <WipeText delay={0.7} dur={0.6} style={{
          fontFamily: ANTON,
          fontSize: isPortrait ? 170 : 220,
          lineHeight: 0.88, letterSpacing: '-0.015em',
          color: C.accent, textTransform: 'uppercase', marginTop: 8,
        }}>IT, CHEF.</WipeText>

        <FadeUp delay={1.6} dur={0.5} style={{ marginTop: isPortrait ? 60 : 50 }}>
          <div style={{
            display: 'flex',
            flexDirection: isPortrait ? 'column' : 'row',
            gap: isPortrait ? 18 : 32,
            justifyContent: 'center', alignItems: 'center',
            fontFamily: INTER, fontSize: isPortrait ? 26 : 22, color: '#dcd8cf',
          }}>
            <span style={{ fontWeight: 700, color: C.ink }}>Two-week build · live by month-end</span>
            {!isPortrait && (
              <span style={{ width: 4, height: 4, borderRadius: '50%', background: C.muted }} />
            )}
            <span>Reply to the email and I'll send the brief</span>
          </div>
        </FadeUp>
      </div>
    </div>
  );
}

// ─── PROGRESS BAR (bottom strip) ────────────────────────────────────────────
function ProgressStrip() {
  const time = useTime();
  const { duration } = useTimeline();
  const pct = (time / duration) * 100;
  return (
    <div style={{
      position: 'absolute', left: 0, right: 0, bottom: 0, height: 4,
      background: 'rgba(255,255,255,0.06)', zIndex: 60,
    }}>
      <div style={{
        height: '100%', width: `${pct}%`, background: C.accent,
        boxShadow: `0 0 12px ${C.accent}`,
      }} />
    </div>
  );
}

// ─── AUDIO TRACK + START GATE ───────────────────────────────────────────────
// Syncs an <audio> element to the Stage timeline and gates initial playback
// behind a user gesture (browsers block audio autoplay).
function AudioTrack({ src, volume = 0.85 }) {
  const { time, playing, setPlaying, setTime } = useTimeline();
  const audioRef = React.useRef(null);
  const [started, setStarted] = React.useState(false);
  // Pause the stage until the user starts (one-shot, only on first mount).
  React.useEffect(() => {
    if (!started) setPlaying(false);
     // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Sync play/pause
  React.useEffect(() => {
    const a = audioRef.current;
    if (!a || !started) return;
    if (playing) { a.play().catch(() => {}); } else { a.pause(); }
  }, [playing, started]);

  // Sync time if drift > 0.25s (handles scrubbing + loop wrap)
  React.useEffect(() => {
    const a = audioRef.current;
    if (!a || !started) return;
    if (Math.abs(a.currentTime - time) > 0.25) {
      try { a.currentTime = time; } catch (e) {}
    }
  }, [time, started]);

  React.useEffect(() => {
    const a = audioRef.current;
    if (a) a.volume = volume;
  }, [volume]);

  const start = () => {
    const a = audioRef.current;
    if (!a) return;
    a.muted = false;
    a.volume = volume;
    a.currentTime = 0;
    setTime(0);
    a.play().then(() => {
      setStarted(true);
      setPlaying(true);
    }).catch(() => {
      // fallback: still kick off the stage
      setStarted(true);
      setPlaying(true);
    });
  };

  return (
    <React.Fragment>
      <audio ref={audioRef} src={src} preload="auto" playsInline />
      {!started && <StartGate onStart={start} />}
    </React.Fragment>
  );
}

function StartGate({ onStart }) {
  const [hover, setHover] = React.useState(false);
  return (
    <div
      onClick={onStart}
      style={{
        position: 'absolute', inset: 0, zIndex: 200,
        background: 'rgba(10,10,10,0.92)',
        backdropFilter: 'blur(4px)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        flexDirection: 'column', cursor: 'pointer', userSelect: 'none',
      }}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
    >
      {/* radial glow */}
      <div style={{
        position: 'absolute', inset: 0,
        background: `radial-gradient(40% 50% at 50% 50%, rgba(255,77,28,${hover ? 0.32 : 0.22}), transparent 65%)`,
        transition: 'all 0.3s',
      }} />
      <div style={{ position: 'relative', textAlign: 'center' }}>
        <div style={{
          fontFamily: INTER, fontWeight: 800, fontSize: 16,
          letterSpacing: '0.28em', textTransform: 'uppercase', color: C.accent,
          marginBottom: 28,
        }}>● A pitch for Cameron Dukes</div>
        <div style={{
          width: 140, height: 140, margin: '0 auto 36px',
          borderRadius: '50%', background: C.accent,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          transform: `scale(${hover ? 1.06 : 1})`,
          transition: 'transform 0.25s',
          boxShadow: `0 0 0 ${hover ? 14 : 8}px rgba(255,77,28,0.18), 0 30px 80px rgba(255,77,28,0.5)`,
        }}>
          <div style={{
            width: 0, height: 0,
            borderLeft: '34px solid #0a0a0a',
            borderTop: '22px solid transparent',
            borderBottom: '22px solid transparent',
            marginLeft: 10,
          }} />
        </div>
        <div style={{
          fontFamily: ANTON, fontSize: 92, color: C.ink, lineHeight: 0.92,
          textTransform: 'uppercase', letterSpacing: '-0.01em',
          whiteSpace: 'nowrap',
        }}>Play the pitch</div>
        <div style={{
          fontFamily: INTER, fontSize: 18, color: '#9a9a98', marginTop: 18, letterSpacing: '0.06em',
        }}>38 seconds · sound on</div>
      </div>
    </div>
  );
}

// ─── ROOT ───────────────────────────────────────────────────────────────────
function PitchReel() {
  const isPortrait = useIsPortrait();
  // Canvas dimensions follow the viewport orientation. Stage's built-in
  // auto-scale fits the canvas to the screen, so scenes get a clean
  // coordinate space (1920×1080 or 1080×1920) to design against.
  const W = isPortrait ? 1080 : 1920;
  const H = isPortrait ? 1920 : 1080;
  return (
    <Stage width={W} height={H} duration={38} background={C.bg} autoplay={false} loop={false}>
      {/* keyframes */}
      <style>{`@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.18); } }`}</style>
      <AudioTrack src="assets/soundtrack.mp3" volume={0.85} />

      <Sprite start={0}    end={3.4}>  <Opener /></Sprite>
      <Sprite start={3.4}  end={9.5}>  <CurrentSite /></Sprite>
      <Sprite start={9.5}  end={11.0}> <SmashCut /></Sprite>
      <Sprite start={11.0} end={31.5} keepMounted={true}> <RedesignTour /></Sprite>
      <Sprite start={31.5} end={38}>   <Closer /></Sprite>

      <FilmGrain opacity={0.12} />
      <Vignette />
      <ProgressStrip />
    </Stage>
  );
}

// Always start the pitch from t=0 on load (Stage's persistKey would otherwise
// resume mid-pitch on reload, which is wrong for a client-facing reel).
try { localStorage.removeItem('animstage:t'); } catch (e) {}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<PitchReel />);
