// Replied — demo video scenes
// Spec: 40s, 1920×1080, autoplay-on-scroll friendly. No VO.
// Scenes: Hook (0-4) · Thesis sort (4-18) · Drafts (18-30) · Trust (30-36) · CTA (36-40)

const { Stage, Sprite, useTime, useSprite, Easing, interpolate, animate } = window;

// ───────────────────────── Spring helpers ─────────────────────────
const overshoot = Easing.easeOutBack;
const settle = Easing.easeOutCubic;
const clamp01 = (v) => Math.max(0, Math.min(1, v));

// Bouncy entry: opacity 0→1, scale 0.7→1 with overshoot, translateY 24→0
const springIn = (t, dur = 0.5) => {
  const p = clamp01(t / dur);
  return { opacity: p, scale: 0.85 + 0.15 * overshoot(p), ty: (1 - settle(p)) * 18 };
};
const fadeOut = (t, exitStart, exitDur = 0.4) => {
  if (t < exitStart) return 1;
  return 1 - clamp01((t - exitStart) / exitDur);
};

// ───────────────────────── Color tokens ─────────────────────────
const C = {
  ink: '#0F1419', mute: '#6B7280', border: '#E5E7EB', paper: '#FFFFFF',
  teal: '#1F4D45', tealHover: '#2A6F63',
  pass: '#22C55E', review: '#F59E0B', fail: '#EF4444', grey: '#9CA3AF',
  passBg: 'rgba(34,197,94,0.14)', passText: '#166534',
  reviewBg: 'rgba(245,158,11,0.14)', reviewText: '#92400E',
  failBg: 'rgba(239,68,68,0.14)', failText: '#991B1B',
  greyBg: 'rgba(156,163,175,0.18)', greyText: '#525866',
};

const FONT = '"Inter", system-ui, -apple-system, sans-serif';

// ───────────────────────── Backgrounds ─────────────────────────
function StageBg() {
  const t = useTime();
  // Subtle drift between two warm light tints
  const a = interpolate([0, 18, 30, 40], [0, 1, 0.5, 0])(t);
  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: `radial-gradient(1200px 800px at 30% 20%, #F4F8F7, #FFFFFF 55%), radial-gradient(900px 700px at 80% 90%, rgba(31,77,69,${0.04 + a * 0.04}), transparent 60%)`,
    }} />
  );
}

// ───────────────────────── Pointer cursor ─────────────────────────
// A pointing-hand emoji-style cursor that moves to clicks; click compresses target.
function Pointer({ x, y, clickAt = null, visibleStart = 0, visibleEnd = 99 }) {
  const t = useTime();
  if (t < visibleStart || t > visibleEnd) return null;
  const click = clickAt != null && Math.abs(t - clickAt) < 0.18;
  const scale = click ? 0.85 : 1;
  return (
    <div style={{
      position: 'absolute', left: x, top: y, width: 36, height: 36,
      transform: `translate(-6px, -2px) scale(${scale})`,
      transition: 'transform 90ms ease-out',
      pointerEvents: 'none', zIndex: 999,
      filter: 'drop-shadow(0 4px 8px rgba(15,20,25,0.18))',
    }}>
      <svg viewBox="0 0 36 36" width="36" height="36">
        <path d="M9 6 L9 22 L13 19 L16 26 L20 24 L17 17 L23 17 Z"
          fill="#0F1419" stroke="#fff" strokeWidth="1.5" strokeLinejoin="round" />
      </svg>
    </div>
  );
}

// ───────────────────────── Outlook chrome ─────────────────────────
function OutlookFrame({ children, navHighlight = null, counters = { match: 0, review: 0, fail: 0 }, hideSidebar = false, fullBleed = false }) {
  const framePos = fullBleed
    ? { left: 48, top: 96, width: 1824, height: 936 }
    : { left: 64, top: 110, width: 1792, height: 940 };
  return (
    <div style={{
      position: 'absolute', ...framePos,
      background: '#fff', borderRadius: 16,
      boxShadow: '0 32px 80px rgba(15,20,25,0.18), 0 8px 24px rgba(15,20,25,0.08)',
      overflow: 'hidden', display: 'flex', flexDirection: 'column',
      border: `1px solid ${C.border}`, fontFamily: FONT,
    }}>
      {/* Title bar */}
      <div style={{ height: fullBleed ? 60 : 56, display: 'flex', alignItems: 'center', padding: '0 24px', background: '#FAFAFA', borderBottom: `1px solid ${C.border}`, gap: 10 }}>
        <span style={{ width: 14, height: 14, borderRadius: 7, background: '#FF5F57' }} />
        <span style={{ width: 14, height: 14, borderRadius: 7, background: '#FEBC2E' }} />
        <span style={{ width: 14, height: 14, borderRadius: 7, background: '#28C840' }} />
        <span style={{ marginLeft: 18, fontSize: fullBleed ? 21 : 21, color: C.mute, fontWeight: 500 }}>Outlook — jeremy@dawnvc.partners — Inbox</span>
      </div>
      <div style={{ flex: 1, display: 'flex', minHeight: 0 }}>
        {!hideSidebar && (
          <div style={{ width: 360, flexShrink: 0, borderRight: `1px solid ${C.border}`, background: '#FBFBFA', padding: '24px 16px', display: 'flex', flexDirection: 'column', gap: 4 }}>
            <NavRow label="Inbox" count={342} dim />
            <NavRow label="Drafts" count={14} dim />
            <NavRow label="Sent" dim />
            <div style={{ height: 20 }} />
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', fontSize: 19, fontWeight: 700, color: C.teal, letterSpacing: '0.08em', textTransform: 'uppercase' }}>
              <ChevronGlyph open /> Replied
            </div>
            <NavRow label="Match" count={counters.match} color={C.pass} active={navHighlight === 'match'} indent />
            <NavRow label="Review" count={counters.review} color={C.review} active={navHighlight === 'review'} indent />
            <NavRow label="Fail" count={counters.fail} color={C.fail} active={navHighlight === 'fail'} indent />
          </div>
        )}
        {/* Mail list */}
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, background: '#fff' }}>
          {children}
        </div>
      </div>
    </div>
  );
}

function ChevronGlyph({ open }) {
  return (
    <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ transform: open ? 'rotate(0)' : 'rotate(-90deg)' }}>
      <polyline points="6 9 12 15 18 9" />
    </svg>
  );
}

function NavRow({ label, count, color, active, indent, dim }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 12,
      padding: indent ? '12px 14px 12px 36px' : '12px 14px',
      borderRadius: 6, fontSize: 22,
      color: active ? C.teal : (dim ? C.mute : C.ink),
      fontWeight: active ? 600 : 500,
      background: active ? 'rgba(31,77,69,0.08)' : 'transparent',
      transition: 'background 200ms, color 200ms',
    }}>
      {color && <span style={{ width: 12, height: 12, borderRadius: 6, background: color, flexShrink: 0 }} />}
      <span style={{ flex: 1 }}>{label}</span>
      {count != null && <span style={{
        background: active ? C.teal : 'transparent',
        color: active ? '#fff' : C.mute,
        fontSize: 20, fontWeight: 600, fontVariantNumeric: 'tabular-nums',
        padding: active ? '3px 12px' : '0', borderRadius: 999, minWidth: 32, textAlign: 'right',
      }}>{count}</span>}
    </div>
  );
}

// ───────────────────────── Email row ─────────────────────────
function EmailRow({ sender, subject, preview, time = '2h', verdict, draftReady, style, size = 'normal' }) {
  const stripe = verdict === 'pass' ? C.pass : verdict === 'review' ? C.review : verdict === 'fail' ? C.fail : verdict === 'grey' ? C.grey : 'transparent';
  const VLAB = { pass: 'Match', review: 'Review', fail: 'Fail', grey: 'Not a pitch' };
  const vbg = verdict === 'pass' ? C.passBg : verdict === 'review' ? C.reviewBg : verdict === 'fail' ? C.failBg : verdict === 'grey' ? C.greyBg : null;
  const vfg = verdict === 'pass' ? C.passText : verdict === 'review' ? C.reviewText : verdict === 'fail' ? C.failText : verdict === 'grey' ? C.greyText : null;
  const big = size === 'large';
  return (
    <div style={{
      display: 'flex', alignItems: 'stretch',
      borderBottom: `1px solid ${C.border}`,
      background: '#fff',
      ...style,
    }}>
      <div style={{ width: big ? 7 : 7, background: stripe }} />
      <div style={{ flex: 1, padding: big ? '24px 36px' : '22px 30px', minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: big ? 18 : 18, marginBottom: big ? 8 : 7 }}>
          <span style={{ fontWeight: 600, fontSize: big ? 26 : 23, color: C.ink }}>{sender}</span>
          {draftReady && (
            <span style={{ fontSize: big ? 20 : 20, color: C.teal, fontWeight: 600, display: 'inline-flex', alignItems: 'center', gap: 4 }}>
              <svg width={big ? 19 : 19} height={big ? 19 : 19} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 14 4 9 9 4" /><path d="M20 20v-7a4 4 0 0 0-4-4H4" /></svg>
              Draft ready
            </span>
          )}
          {verdict && (
            <span style={{ background: vbg, color: vfg, padding: big ? '5px 16px' : '4px 14px', borderRadius: 999, fontSize: big ? 19 : 19, fontWeight: 700, letterSpacing: '0.02em', marginLeft: 'auto', marginRight: big ? 18 : 16 }}>
              {VLAB[verdict]}
            </span>
          )}
          <span style={{ fontSize: big ? 21 : 20, color: C.mute, fontVariantNumeric: 'tabular-nums', minWidth: big ? 60 : 52, textAlign: 'right' }}>{time}</span>
        </div>
        <div style={{ fontSize: big ? 24 : 22, fontWeight: 500, color: C.ink, marginBottom: big ? 6 : 5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{subject}</div>
        {preview && <div style={{ fontSize: big ? 21 : 21, color: C.mute, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{preview}</div>}
      </div>
    </div>
  );
}

// ───────────────────────── Shared inbox catalog ─────────────────────────
// Mixed feed reused across Scenes 1 & 2. verdict ∈ 'pass'|'review'|'fail'|null (non-pitch).
const INBOX = [
  { sender: 'Sarah Chen',       subject: 'Northwind — AI ops layer for in-house legal',     preview: 'ex-A&O + ex-Stripe. £180K ARR. £1.2M pre-seed.', time: '2m',  verdict: 'pass'   },
  { sender: 'TechCrunch Daily', subject: 'This week in venture — your top stories',         preview: 'Trending: AI infra, EU pre-seed, LP pullback.',  time: '6m',  verdict: null     },
  { sender: 'Tom Rhys',         subject: 'YC W25 — vertical AI for B2B sales',              preview: 'Pre-seed, $400K ARR, San Francisco.',            time: '11m', verdict: 'review' },
  { sender: 'ColdReach Sales',  subject: 'Boost your deal flow with our CRM!',              preview: 'Save 15% — book a demo today.',                  time: '14m', verdict: null     },
  { sender: 'Marcus Liu',       subject: 'RigForge — Series C hardware, $80M',              preview: 'Hardware unit margins improving.',               time: '22m', verdict: 'fail'   },
  { sender: 'Priya Patel',      subject: 'Compliance ops for fintech — pre-seed',           preview: 'ex-Plaid, technical co-founder. £900K round.',   time: '47m', verdict: 'pass'   },
  { sender: 'David Park',       subject: 'Lumen — B2B SaaS payments, pre-seed (NYC)',       preview: 'Pre-seed, $250K ARR, raising $1.5M.',            time: '1h',  verdict: 'review' },
  { sender: 'AWS',              subject: 'Your AWS bill is ready',                          preview: 'November invoice — $324.18.',                    time: '2h',  verdict: null     },
  { sender: 'Nina Volkov',      subject: 'DevOps for climate teams — pre-seed',             preview: 'EU-based, technical team. €1M round.',           time: '3h',  verdict: 'pass'   },
  { sender: 'Felix Adair',      subject: 'Series B — consumer marketplace',                 preview: '$50M Series B.',                                 time: '4h',  verdict: 'fail'   },
];

// Longer feed for Scene 1's fast-scroll effect — repeats + extras
const SCROLL_FEED = [
  ...INBOX,
  { sender: 'Yara Abboud', subject: 'Pre-seed deck — voice AI for clinics',  preview: '£500K round, EU.',           time: '5h', verdict: 'pass' },
  { sender: 'LinkedIn',    subject: '12 new connection requests',             preview: 'See who wants to connect.',  time: '5h', verdict: null  },
  { sender: 'Ben Howell',  subject: 'Series A — quick look?',                  preview: '$15M Series A.',             time: '6h', verdict: 'fail' },
  { sender: 'Jamie Ortiz', subject: 'Cold pitch — climate fintech, pre-seed', preview: 'Lisbon-based, technical.',   time: '6h', verdict: 'pass' },
  { sender: 'Notion',      subject: 'Your weekly digest',                      preview: '4 docs updated.',            time: '7h', verdict: null  },
  { sender: 'Felix Tan',   subject: 'Devtools for AI agents — pre-seed',       preview: '£800K round, London.',       time: '7h', verdict: 'pass' },
  { sender: 'GitHub',      subject: 'sarah-chen pushed to dawnvc-portal',      preview: '12 new commits.',            time: '8h', verdict: null  },
  { sender: 'Hana Aiba',   subject: 'Series B fintech — follow-on?',           preview: '$40M Series B.',             time: '8h', verdict: 'fail' },
];

// ═══════════════════════ SCENE 1 — Hook (0-5.5) ═══════════════════════
function Scene1() {
  const t = useTime();
  if (t > 5.5) return null;

  // Counter ticks fast — pitches piling up
  const counter = Math.floor(interpolate([0, 0.4, 1.6, 2.4, 3.0], [0, 22, 96, 218, 312])(t));

  // Title pop at 2.5s — holds an extra second, fades 5.3-5.5
  const titleStart = 2.5;
  const titleP = clamp01((t - titleStart) / 0.55);
  const titleScale = 0.7 + 0.3 * overshoot(titleP);
  const titleOp = clamp01((t - titleStart) / 0.4) * (1 - clamp01((t - 5.3) / 0.2));

  // Inbox blur builds in time for the title
  const blur = interpolate([2.0, 2.6], [0, 8])(t);
  const inboxOp = interpolate([2.0, 2.6, 5.3, 5.5], [1, 0.45, 0.45, 0])(t);

  // Fast scroll: list translates upward continuously
  const ROW_H = 124; // taller rows after +4pt bump
  const scrollY = -interpolate([0, 2.4], [0, ROW_H * 12])(t);

  return (
    <>
      {/* Outlook frame — Scene 1: sidebar hidden, full-bleed, large rows */}
      <div style={{ position: 'absolute', inset: 0, opacity: inboxOp, filter: `blur(${blur}px)`, transition: 'opacity 200ms' }}>
        <OutlookFrame counters={{ match: 0, review: 0, fail: 0 }} hideSidebar fullBleed>
          <div style={{ padding: '26px 40px', borderBottom: `1px solid ${C.border}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontSize: 34, fontWeight: 700, color: C.ink }}>Inbox</span>
            <span style={{ fontSize: 26, color: C.mute, fontVariantNumeric: 'tabular-nums' }}>
              <b style={{ color: C.ink, fontSize: 34, fontWeight: 700 }}>{counter}</b> &nbsp;new messages
            </span>
          </div>
          <div style={{ flex: 1, overflow: 'hidden', position: 'relative' }}>
            <div style={{ transform: `translateY(${scrollY}px)`, willChange: 'transform' }}>
              {SCROLL_FEED.map((e, i) => {
                const rowStart = 0.05 + i * 0.10;
                const p = clamp01((t - rowStart) / 0.32);
                if (p <= 0) return null;
                return (
                  <div key={i} style={{ opacity: p }}>
                    <EmailRow sender={e.sender} subject={e.subject} preview={e.preview} time={e.time} size="large" />
                  </div>
                );
              })}
            </div>
            {/* Soft motion fade at top + bottom */}
            <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(255,255,255,0.55), transparent 14%, transparent 86%, rgba(255,255,255,0.55))', pointerEvents: 'none' }} />
          </div>
        </OutlookFrame>
      </div>

      {/* Title pop */}
      {t >= titleStart && (
        <div style={{
          position: 'absolute', left: '50%', top: '46%',
          transform: `translate(-50%, -50%) scale(${titleScale})`,
          opacity: titleOp, textAlign: 'center', fontFamily: FONT,
        }}>
          <div style={{ fontSize: 116, fontWeight: 700, letterSpacing: '-0.03em', color: C.teal, lineHeight: 1, marginBottom: 18 }}>
            Replied.
          </div>
          <div style={{ fontSize: 44, fontWeight: 500, color: C.ink, letterSpacing: '-0.015em', marginBottom: 10 }}>
            Reads every pitch, drafts every reply.
          </div>
          <div style={{ fontSize: 32, fontWeight: 600, color: C.teal }}>
            Built for investors.
          </div>
        </div>
      )}
    </>
  );
}

// ═══════════════════════ SCENE 2 — Thesis sort (5-19) ═══════════════════════
function Scene2() {
  const t = useTime();
  if (t < 5 || t > 19.2) return null;
  const lt = t - 5;

  // Section title
  const titleP = clamp01(lt / 0.5);
  const titleOp = clamp01((lt - 0.05) / 0.4) * (1 - clamp01((lt - 13.6) / 0.5));
  const titleTx = (1 - settle(titleP)) * -40;

  // Thesis card 1.0 - 6.0 (lt)
  const cardStart = 1.0;
  const cardEnd = 6.0;
  const inThesis = lt >= cardStart && lt < cardEnd;
  const FULL = 'Pre-seed B2B SaaS. UK and EU. Technical founders.';
  const typeStart = cardStart + 0.45;
  const typeDur = 1.8;
  const typedLen = Math.floor(interpolate([typeStart, typeStart + typeDur], [0, FULL.length])(lt));
  const typed = FULL.slice(0, typedLen);
  const TAGS = [
    { label: 'Stage: Pre-seed', t0: 3.5 },
    { label: 'Industry: B2B SaaS', t0: 3.7 },
    { label: 'Geo: UK/EU', t0: 3.9 },
    { label: 'Team: Technical', t0: 4.1 },
  ];
  const cardP = clamp01((lt - cardStart) / 0.6);
  const cardScale = 0.7 + 0.3 * overshoot(cardP);
  const cardExitP = clamp01((lt - (cardEnd - 0.4)) / 0.4);
  const cardOp = clamp01((lt - cardStart) / 0.4) * (1 - cardExitP);

  // Inbox stays visible behind everything; dimmed under thesis card, returns clear after
  const inboxFade = clamp01((lt - 0.4) / 0.5);
  const dimBlur = interpolate([cardStart, cardStart + 0.3, cardEnd - 0.3, cardEnd], [0, 6, 6, 0])(lt);
  const dimOp = interpolate([cardStart, cardStart + 0.3, cardEnd - 0.3, cardEnd], [1, 0.35, 0.35, 1])(lt);
  const inboxOp = inboxFade * dimOp * (1 - clamp01((lt - 13.6) / 0.5));

  // Pitch metadata — pill timings then fly timings
  const pillBaseT = 6.4;
  const pillStep = 0.32;
  const flyBaseT = 9.0;
  const flyStep = 0.34;
  const PITCHES = [];
  let order = 0;
  INBOX.forEach((e, idx) => {
    if (!e.verdict) return;
    PITCHES.push({
      idx, verdict: e.verdict,
      pillT: pillBaseT + order * pillStep,
      flyT: flyBaseT + order * flyStep,
    });
    order++;
  });

  // Counters: tick once a pitch has flown
  const counters = { match: 0, review: 0, fail: 0 };
  PITCHES.forEach(p => {
    if (lt > p.flyT + 0.45) {
      const k = p.verdict === 'pass' ? 'match' : p.verdict === 'review' ? 'review' : 'fail';
      counters[k]++;
    }
  });

  // Folder pulse highlight
  const navHighlight = (() => {
    let h = null;
    PITCHES.forEach(p => {
      if (lt > p.flyT + 0.35 && lt < p.flyT + 0.85) {
        h = p.verdict === 'pass' ? 'match' : p.verdict === 'review' ? 'review' : 'fail';
      }
    });
    return h;
  })();

  // Caption
  const capStart = 12.0;
  const capP = clamp01((lt - capStart) / 0.5);
  const capOp = clamp01((lt - capStart) / 0.4) * (1 - clamp01((lt - 13.6) / 0.5));

  // Fly-out geometry
  const ROW_H = 132;
  const MAIL_TOP = 130;
  const folderTargetY = (v) => 300 + (v === 'pass' ? 0 : v === 'review' ? 54 : 108);

  return (
    <>
      {/* Section title */}
      <div style={{
        position: 'absolute', left: 120, top: 48, fontSize: 44, fontWeight: 700,
        color: C.ink, letterSpacing: '-0.02em', fontFamily: FONT,
        transform: `translateX(${titleTx}px)`, opacity: titleOp,
      }}>
        Pitches ranked by your thesis.
      </div>

      {/* Inbox — same INBOX list, pills land in place, pitches fly to folders */}
      <div style={{
        position: 'absolute', inset: 0,
        opacity: inboxOp,
        filter: `blur(${dimBlur}px)`,
        transition: 'opacity 200ms',
      }}>
        <OutlookFrame navHighlight={navHighlight} counters={counters}>
          <div style={{ padding: '22px 32px', borderBottom: `1px solid ${C.border}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontSize: 26, fontWeight: 700, color: C.ink }}>Inbox · today</span>
            {lt > 6.0 && (
              <span style={{ fontSize: 21, color: C.teal, fontWeight: 600, display: 'inline-flex', alignItems: 'center', gap: 10, opacity: clamp01((lt - 6.0) / 0.4) * (1 - clamp01((lt - 11.5) / 0.5)) }}>
                <span style={{ width: 12, height: 12, borderRadius: 6, background: C.pass, animation: 'pulse 1.4s ease-in-out infinite' }} />
                Replied is sorting
              </span>
            )}
          </div>
          <div style={{ flex: 1, padding: 0, position: 'relative', overflow: 'hidden' }}>
            {INBOX.map((e, i) => {
              const pitch = PITCHES.find(p => p.idx === i);
              const showVerdict = pitch && lt > pitch.pillT;
              let flyTx = 0, flyTy = 0, flyScale = 1, flyOp = 1;
              if (pitch && lt > pitch.flyT) {
                const fp = clamp01((lt - pitch.flyT) / 0.6);
                const ease = settle(fp);
                flyTx = -820 * ease;
                const dy = folderTargetY(pitch.verdict) - (MAIL_TOP + i * ROW_H + ROW_H / 2);
                flyTy = dy * ease;
                flyScale = 1 - 0.85 * ease;
                flyOp = 1 - clamp01((lt - pitch.flyT - 0.32) / 0.28);
              }
              const pillP = pitch ? clamp01((lt - pitch.pillT) / 0.35) : 0;
              const pulse = pillP > 0 && pillP < 1 ? 1 + 0.02 * Math.sin(pillP * Math.PI) : 1;
              return (
                <div key={i} style={{
                  transform: `translate(${flyTx}px, ${flyTy}px) scale(${flyScale * pulse})`,
                  opacity: flyOp,
                  transformOrigin: 'left center',
                  transition: 'none',
                }}>
                  <EmailRow
                    sender={e.sender}
                    subject={e.subject}
                    preview={e.preview}
                    time={e.time}
                    verdict={showVerdict ? pitch.verdict : null}
                  />
                </div>
              );
            })}
          </div>
        </OutlookFrame>
      </div>

      {/* Thesis card overlay */}
      {inThesis && (
        <div style={{
          position: 'absolute', left: '50%', top: '50%',
          transform: `translate(-50%, -50%) scale(${cardScale})`,
          opacity: cardOp, fontFamily: FONT,
          width: 980, padding: '40px 48px', borderRadius: 20,
          background: '#fff', border: `1px solid ${C.border}`,
          boxShadow: '0 32px 80px rgba(15,20,25,0.18)',
        }}>
          <div style={{ fontSize: 13, color: C.mute, letterSpacing: '0.12em', textTransform: 'uppercase', fontWeight: 700, marginBottom: 14 }}>Your thesis</div>
          <div style={{ fontSize: 38, fontWeight: 600, color: C.ink, letterSpacing: '-0.015em', lineHeight: 1.3, minHeight: 52 }}>
            {typed}
            {lt < typeStart + typeDur + 0.2 && <span style={{ display: 'inline-block', width: 3, height: 38, background: C.teal, marginLeft: 4, verticalAlign: '-6px', animation: 'cursor 0.6s steps(2) infinite' }} />}
          </div>
          <div style={{ display: 'flex', gap: 12, marginTop: 28, flexWrap: 'wrap' }}>
            {TAGS.map((tag, i) => {
              const p = clamp01((lt - tag.t0) / 0.45);
              if (p <= 0) return null;
              const sc = 0.7 + 0.3 * overshoot(p);
              return (
                <div key={i} style={{
                  background: 'rgba(31,77,69,0.08)', color: C.teal,
                  padding: '8px 18px', borderRadius: 999,
                  fontSize: 18, fontWeight: 600,
                  transform: `scale(${sc})`, opacity: clamp01((lt - tag.t0) / 0.3),
                }}>{tag.label}</div>
              );
            })}
          </div>
        </div>
      )}

      {/* Caption */}
      {lt >= capStart && (
        <div style={{
          position: 'absolute', left: '50%', top: '50%',
          transform: `translate(-50%, calc(-50% + ${(1 - settle(capP)) * 18}px)) scale(${0.85 + 0.15 * overshoot(capP)})`,
          opacity: capOp, fontSize: 100, fontWeight: 700,
          color: C.teal, fontFamily: FONT, letterSpacing: '-0.025em',
          textAlign: 'center', whiteSpace: 'nowrap',
          padding: '24px 48px', borderRadius: 24,
          background: 'rgba(255,255,255,0.92)',
          backdropFilter: 'blur(16px)',
          WebkitBackdropFilter: 'blur(16px)',
          boxShadow: '0 32px 80px rgba(15,20,25,0.18)',
        }}>
          Pitches in. Spam out.
        </div>
      )}
    </>
  );
}


// ═══════════════════════ SCENE 3 — Drafts (19-31) ═══════════════════════
function Scene3() {
  const t = useTime();
  if (t < 19 || t > 31.2) return null;
  const lt = t - 19; // 0..12

  // Section title
  const titleP = clamp01(lt / 0.5);
  const titleOp = clamp01(lt / 0.4) * (1 - clamp01((lt - 11.5) / 0.5));
  const titleTx = (1 - settle(titleP)) * -40;

  // Timing anchors
  const T_BULLET1 = 0.8;     // "Knows your thesis"
  const T_MATCH_PILL = 1.3;  // Match pill pops
  const T_BULLET2 = 2.6;     // "Fits your workflow"
  const T_HIGHLIGHT = 2.95;  // Highlight box appears around the action line
  const T_VAR2 = 4.1;        // Flip to form variant
  const T_VAR3 = 5.4;        // Flip to video variant
  const T_BULLET3 = 6.9;     // "Suggests referral funds"
  const T_SHUFFLE = 7.1;     // Marcus shuffles in from behind Sarah
  const T_REFERRAL = 8.5;    // Referral fund pulse

  // Sarah card entry + exit (Marcus replaces her in the same slot during shuffle)
  const sarahCardP = clamp01((lt - 0.2) / 0.6);
  const sarahEntryTy = (1 - overshoot(sarahCardP)) * 50;
  const sarahEntryOp = clamp01((lt - 0.2) / 0.5);
  // During shuffle: Sarah fades + tilts + slides slightly (recedes)
  const shuffleP = clamp01((lt - T_SHUFFLE) / 0.7);
  const sarahExitOp = 1 - settle(shuffleP);
  const sarahExitTx = -settle(shuffleP) * 18;
  const sarahExitRot = settle(shuffleP) * -2;
  const sarahExitScale = 1 - settle(shuffleP) * 0.04;

  // Match pill — hidden initially, pops in at T_MATCH_PILL
  const pillP = clamp01((lt - T_MATCH_PILL) / 0.45);
  const matchPillScale = pillP <= 0 ? 0 : (0.4 + 0.6 * overshoot(pillP));
  const matchPillOp = clamp01((lt - T_MATCH_PILL) / 0.3);

  // Line variants (in Sarah's draft)
  const VARIANTS = [
    { text: 'Grab a 30-min slot here:', link: 'calendly.com/replied-vc', start: 0 },
    { text: 'Please fill out the form at:', link: 'dawnvc.com/intake', start: T_VAR2 },
    { text: 'Can you upload a video from the founders here:', link: 'upload.dawnvc.com', start: T_VAR3 },
  ];

  // Highlight box opacity: fades in at T_HIGHLIGHT, fades out near end
  const highlightOp = (lt > T_HIGHLIGHT)
    ? clamp01((lt - T_HIGHLIGHT) / 0.3) * (1 - clamp01((lt - 10.6) / 0.6))
    : 0;

  // Pulse intensity — bursts when a new variant appears
  const pulseAtTime = (t0) => {
    const dt = lt - t0;
    if (dt < 0 || dt > 0.6) return 0;
    return Math.sin((dt / 0.6) * Math.PI);
  };
  const variantPulse = Math.max(
    pulseAtTime(T_HIGHLIGHT),
    pulseAtTime(T_VAR2),
    pulseAtTime(T_VAR3),
  );

  // Marcus card — appears at T_SHUFFLE, takes Sarah's slot (replaces, not stacks)
  // Pre-shuffle: tucked behind Sarah (scale 0.94, tilted right, translated, opacity 0)
  // Post-shuffle: settles to normal centered position
  const marcusP = clamp01((lt - T_SHUFFLE) / 0.7);
  const marcusOp = clamp01((lt - (T_SHUFFLE - 0.05)) / 0.55) * (1 - clamp01((lt - 11.5) / 0.5));
  const marcusTx = (1 - settle(marcusP)) * 22;
  const marcusScale = 0.94 + 0.06 * overshoot(marcusP);
  const marcusRot = (1 - settle(marcusP)) * 2.5;

  // Referral pulse (preserve existing behavior)
  const referralPulse = lt > T_REFERRAL && lt < T_REFERRAL + 2.0;

  // Bullets
  const BULLETS = [
    { label: 'Knows your thesis',          t0: T_BULLET1 },
    { label: 'Fits your workflow',         t0: T_BULLET2 },
    { label: 'Suggests referral funds',    t0: T_BULLET3 },
  ];

  return (
    <>
      <div style={{
        position: 'absolute', left: 120, top: 56, fontSize: 40, fontWeight: 700,
        color: C.ink, letterSpacing: '-0.02em', fontFamily: FONT,
        transform: `translateX(${titleTx}px)`, opacity: titleOp,
      }}>
        Replies drafted in your voice.
      </div>

      {/* Cards container — one card visible at a time; Marcus replaces Sarah during shuffle */}
      <div style={{
        position: 'absolute', left: '50%', top: 280, fontFamily: FONT,
        transform: 'translateX(-50%)', width: 1180, height: 480,
      }}>
        {/* Sarah — visible 0 → T_SHUFFLE+0.7, fades out as Marcus appears */}
        <div style={{
          position: 'absolute', top: 0, left: 0, width: '100%',
          transform: `translate(${sarahExitTx}px, ${sarahEntryTy}px) scale(${sarahExitScale}) rotate(${sarahExitRot}deg)`,
          opacity: sarahEntryOp * sarahExitOp,
          transformOrigin: 'center center',
          zIndex: 2,
        }}>
          <DraftCard
            verdict="pass"
            senderName="Sarah Chen"
            senderMeta="Building Northwind — vertical AI for legal ops"
            pillScale={matchPillScale}
            pillOpacity={matchPillOp}
            original={[
              'Hi Jeremy,',
              "We're a 3-person team — ex-Allen & Overy + ex-Stripe — building the AI ops layer for in-house legal.",
              '6 paying design partners, £180K ARR, raising £1.2M pre-seed.',
              'Deck attached — would love 30 minutes.',
            ]}
            draft={[
              'Hey Sarah —',
              "really like what you're building.",
              <VariantLine
                key="action"
                variants={VARIANTS}
                lt={lt}
                highlightOp={highlightOp}
                pulse={variantPulse}
              />,
              'Jeremy',
            ]}
          />
        </div>

        {/* Marcus — appears at T_SHUFFLE, takes Sarah's slot */}
        <div style={{
          position: 'absolute', top: 0, left: 0, width: '100%',
          transform: `translateX(${marcusTx}px) scale(${marcusScale}) rotate(${marcusRot}deg)`,
          opacity: marcusOp,
          transformOrigin: 'center center',
          zIndex: 3,
        }}>
          <DraftCard
            verdict="fail"
            senderName="Marcus Liu"
            senderMeta="Series A consumer hardware — RigForge"
            original={[
              'Hi Jeremy,',
              "We're raising a $12M Series A for our consumer hardware platform.",
              '$2.4M ARR, 18% MoM growth, hardware unit margins improving.',
              "Would love your time on a call this week.",
            ]}
            draft={[
              'Hey Marcus —',
              'not a fit at this stage.',
              <>Worth trying <Highlight on={referralPulse}>Seedcamp</Highlight>, <Highlight on={referralPulse}>Connect Ventures</Highlight>, or <Highlight on={referralPulse}>Concept Ventures</Highlight> — they back this space.</>,
              'Jeremy',
            ]}
          />
        </div>
      </div>

      {/* Annotation bullets — top centre, above the cards.
          All three render from scene start with opacity 0 so the flex row
          doesn't reflow as bullets pop in. Each pops in playfully at its t0. */}
      <div style={{
        position: 'absolute', left: '50%', top: 196,
        transform: 'translateX(-50%)',
        display: 'flex', flexDirection: 'row', gap: 44,
        fontFamily: FONT, alignItems: 'center', justifyContent: 'center',
      }}>
        {BULLETS.map((b, i) => {
          const p = clamp01((lt - b.t0) / 0.6);
          // Punchy overshoot — c1=2.5 vs default 1.70158
          const pop = (() => {
            if (p <= 0) return 0;
            if (p >= 1) return 1;
            const c1 = 2.5, c3 = c1 + 1;
            return 1 + c3 * Math.pow(p - 1, 3) + c1 * Math.pow(p - 1, 2);
          })();
          const sc = 0.3 + 0.7 * pop;
          const ty = (1 - pop) * 18;
          const rot = (1 - settle(p)) * -6;
          const op = clamp01((lt - b.t0) / 0.4) * (1 - clamp01((lt - 11.5) / 0.5));
          return (
            <div key={i} style={{
              display: 'flex', alignItems: 'center', gap: 12,
              transform: `translateY(${ty}px) scale(${sc}) rotate(${rot}deg)`,
              opacity: op, transformOrigin: 'center center',
            }}>
              <div style={{
                width: 36, height: 36, flexShrink: 0, borderRadius: 18,
                background: C.teal, color: '#fff', display: 'flex',
                alignItems: 'center', justifyContent: 'center', fontWeight: 700,
                fontSize: 22, fontFamily: FONT,
              }}>{i + 1}</div>
              <div style={{ fontSize: 26, color: C.ink, fontWeight: 500, lineHeight: 1.35, whiteSpace: 'nowrap' }}>{b.label}</div>
            </div>
          );
        })}
      </div>

    </>
  );
}

// Renders the action line in Sarah's draft. Pick the active variant (snap, no
// crossfade — variants have different wrap widths so overlay alignment is
// brittle). A teal highlight box wraps the active variant and pulses on each
// variant change.
function VariantLine({ variants, lt, highlightOp, pulse }) {
  let activeIdx = 0;
  variants.forEach((v, i) => { if (lt >= v.start) activeIdx = i; });
  const v = variants[activeIdx];
  const pulseBg = 0.10 + 0.18 * pulse;
  const pulseShadow = 1.5 + 2.0 * pulse;
  return (
    <div style={{ minHeight: 64 }}>
      <div style={{
        display: 'inline-block',
        maxWidth: '100%',
        padding: '6px 12px',
        borderRadius: 6,
        background: highlightOp > 0 ? `rgba(31,77,69,${pulseBg * highlightOp})` : 'transparent',
        boxShadow: highlightOp > 0 ? `0 0 0 ${pulseShadow}px rgba(31,77,69,${0.55 * highlightOp})` : 'none',
        transition: 'background 200ms, box-shadow 200ms',
        lineHeight: 1.55,
      }}>
        {v.text}&nbsp;<a style={{
          color: C.teal, textDecoration: 'none', fontWeight: 600,
        }}>{v.link}</a>
      </div>
    </div>
  );
}

function Highlight({ children, on }) {
  return (
    <span style={{
      background: on ? 'rgba(31,77,69,0.18)' : 'transparent',
      boxShadow: on ? `0 0 0 1.5px ${C.teal}` : 'none',
      padding: '0 4px', borderRadius: 4,
      transition: 'background 250ms, box-shadow 250ms',
      fontWeight: 600, color: C.teal,
    }}>{children}</span>
  );
}

function DraftCard({ verdict, senderName, senderMeta, original, draft, pillScale = 1, pillOpacity = 1 }) {
  const VLAB = { pass: 'Match', fail: 'Fail' };
  const vbg = verdict === 'pass' ? C.passBg : C.failBg;
  const vfg = verdict === 'pass' ? C.passText : C.failText;
  return (
    <div style={{
      width: 1180, background: '#fff', borderRadius: 22,
      border: `1px solid ${C.border}`,
      boxShadow: '0 28px 72px rgba(15,20,25,0.18)',
      overflow: 'hidden',
    }}>
      <div style={{ padding: '24px 36px 18px', borderBottom: `1px solid ${C.border}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div>
          <div style={{ fontSize: 26, fontWeight: 700, color: C.ink }}>{senderName}</div>
          <div style={{ fontSize: 19, color: C.mute, marginTop: 3 }}>{senderMeta}</div>
        </div>
        <span style={{
          background: vbg, color: vfg, padding: '6px 18px', borderRadius: 999, fontSize: 19, fontWeight: 700, letterSpacing: '0.02em',
          transform: `scale(${pillScale})`,
          opacity: pillOpacity,
          transformOrigin: 'center',
          display: 'inline-block',
        }}>
          {VLAB[verdict]}
        </span>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
        <div style={{ padding: '22px 36px', borderRight: `1px solid ${C.border}`, background: '#FBFBFA' }}>
          <div style={{ fontSize: 16, color: C.mute, letterSpacing: '0.12em', textTransform: 'uppercase', fontWeight: 700, marginBottom: 12 }}>Original</div>
          <div style={{ fontSize: 20, color: C.ink, lineHeight: 1.55, display: 'flex', flexDirection: 'column', gap: 6 }}>
            {original.map((p, i) => <div key={i}>{p}</div>)}
          </div>
        </div>
        <div style={{ padding: '22px 36px' }}>
          <div style={{ fontSize: 16, color: C.teal, letterSpacing: '0.12em', textTransform: 'uppercase', fontWeight: 700, marginBottom: 12 }}>Replied — Draft</div>
          <div style={{ fontSize: 20, color: C.ink, lineHeight: 1.6, display: 'flex', flexDirection: 'column', gap: 8 }}>
            {draft.map((p, i) => <div key={i}>{p}</div>)}
          </div>
        </div>
      </div>
    </div>
  );
}

// ═══════════════════════ SCENE 4 — Trust (31-35) ═══════════════════════
function Scene4() {
  const t = useTime();
  if (t < 31 || t > 35.2) return null;
  const lt = t - 31; // 0..4

  const BADGES = [
    { lines: ['Drafts only.', 'Never sends. Never deletes.'], glyph: 'shield', t0: 0.2 },
    { lines: ['EU-hosted.', 'GDPR compliant.'], img: '../assets/eu-shield.svg', imgHeight: 80, t0: 0.6 },
    { lines: ['Microsoft', 'Verified Publisher.'], img: '../assets/microsoft-partner.png', imgWidth: 200, t0: 1.0 },
  ];

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 56,
      fontFamily: FONT,
    }}>
      {BADGES.map((b, i) => {
        const p = clamp01((lt - b.t0) / 0.55);
        // Always render — slot is reserved in the flex row from scene start
        // so badges don't push each other around as they appear.
        const sc = 0.6 + 0.4 * overshoot(p);
        const op = clamp01((lt - b.t0) / 0.4) * (1 - clamp01((lt - 3.5) / 0.5));
        return (
          <div key={i} style={{
            width: 380, padding: '40px 32px', borderRadius: 20,
            background: '#fff', border: `1px solid ${C.border}`,
            boxShadow: '0 24px 64px rgba(15,20,25,0.12)',
            transform: `scale(${sc})`, opacity: op, textAlign: 'center',
          }}>
            <div style={{ height: 80, margin: '0 auto 20px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              {b.img ? (
                <img
                  src={b.img}
                  alt=""
                  style={{
                    height: b.imgHeight ? `${b.imgHeight}px` : 'auto',
                    width: b.imgWidth ? `${b.imgWidth}px` : 'auto',
                    maxHeight: 80,
                    objectFit: 'contain',
                  }}
                />
              ) : (
                <div style={{ width: 80, height: 80, borderRadius: 40, background: 'rgba(31,77,69,0.08)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: C.teal }}>
                  <BadgeGlyph kind={b.glyph} />
                </div>
              )}
            </div>
            {b.lines.map((line, j) => (
              <div key={j} style={{ fontSize: j === 0 ? 26 : 22, fontWeight: j === 0 ? 700 : 500, color: j === 0 ? C.ink : C.mute, letterSpacing: '-0.01em', lineHeight: 1.3 }}>
                {line}
              </div>
            ))}
          </div>
        );
      })}
    </div>
  );
}

function BadgeGlyph({ kind }) {
  const stroke = { stroke: 'currentColor', strokeWidth: 2, fill: 'none', strokeLinecap: 'round', strokeLinejoin: 'round' };
  if (kind === 'shield') return (
    <svg width="40" height="40" viewBox="0 0 24 24" {...stroke}>
      <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
      <path d="m9 12 2 2 4-4" />
    </svg>
  );
  if (kind === 'globe') return (
    <svg width="40" height="40" viewBox="0 0 24 24" {...stroke}>
      <circle cx="12" cy="12" r="10" />
      <path d="M2 12h20" /><path d="M12 2a15.3 15.3 0 0 1 0 20" /><path d="M12 2a15.3 15.3 0 0 0 0 20" />
    </svg>
  );
  return (
    <svg width="40" height="40" viewBox="0 0 24 24" {...stroke}>
      <path d="M12 2 4 5v6c0 5.5 3.8 10.7 8 11 4.2-.3 8-5.5 8-11V5z" />
      <polyline points="9 12 11 14 15 10" />
    </svg>
  );
}

// ═══════════════════════ SCENE 5 — CTA (35-39) ═══════════════════════
function Scene5() {
  const t = useTime();
  if (t < 35 || t > 39.5) return null;
  const lt = t - 35;

  const wordmarkP = clamp01(lt / 0.5);
  const wordmarkSc = 0.6 + 0.4 * overshoot(wordmarkP);
  const wordmarkOp = clamp01(lt / 0.4);

  const btnStart = 0.5;
  const btnP = clamp01((lt - btnStart) / 0.55);
  const btnSc = 0.6 + 0.4 * overshoot(btnP);
  const btnOp = clamp01((lt - btnStart) / 0.4);

  // Kicker — typed
  const kickerStart = 1.2;
  const FULL_K = 'Your deal flow, sorted —\nbefore your first coffee.';
  const len = Math.floor(interpolate([kickerStart, kickerStart + 1.4], [0, FULL_K.length])(lt));
  const kicker = FULL_K.slice(0, len);

  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', fontFamily: FONT, gap: 44 }}>
      <div style={{ transform: `scale(${wordmarkSc})`, opacity: wordmarkOp, display: 'flex', alignItems: 'center', gap: 24 }}>
        <img src="../assets/replied-glyph.png" alt="" style={{ height: 120, width: 'auto' }} />
        <span style={{ fontSize: 132, fontWeight: 700, color: C.teal, letterSpacing: '-0.03em', lineHeight: 1 }}>Replied</span>
      </div>

      <button style={{
        transform: `scale(${btnSc})`, opacity: btnOp,
        background: C.teal, color: '#fff', border: 'none',
        padding: '28px 56px', borderRadius: 14, fontSize: 32, fontWeight: 600, fontFamily: FONT,
        display: 'inline-flex', alignItems: 'center', gap: 14, cursor: 'pointer',
        boxShadow: '0 16px 32px rgba(31,77,69,0.32)',
      }}>
        Connect Outlook <span aria-hidden style={{ fontSize: 36 }}>→</span>
      </button>

      <div style={{ fontSize: 24, color: C.mute, textAlign: 'center', lineHeight: 1.5, whiteSpace: 'pre-line', minHeight: 76 }}>
        {kicker}
        {lt < kickerStart + 1.5 && <span style={{ display: 'inline-block', width: 2, height: 22, background: C.mute, marginLeft: 2, verticalAlign: '-3px', animation: 'cursor 0.6s steps(2) infinite' }} />}
      </div>
    </div>
  );
}

// ═══════════════════════ Root ═══════════════════════
function DemoRoot() {
  return (
    <Stage width={1920} height={1080} duration={39} background="#FFFFFF" persistKey="replied-demo-v2">
      <StageBg />
      <Scene1 />
      <Scene2 />
      <Scene3 />
      <Scene4 />
      <Scene5 />
    </Stage>
  );
}

window.DemoRoot = DemoRoot;
