// ════════════════════════════════════════════════════════════════
// Agentic Forge · Engineer Day · v2 · 180s
// All AF output renders as chat messages inside Copilot Chat panel.
// No toasts, no popups, no banners. Burned-in subtitles throughout.
// ════════════════════════════════════════════════════════════════

const T = window.TOKENS;

const ED = {
  carbon: '#080c14',
  charcoal: '#1c1e24',
  charcoalLine: '#2a2d35',
  textWhite: '#ffffff',
  textGray: '#a0a4ab',
  textDim: '#6e7178',
  green: '#00da41',
  blue: '#3b82f6',
  red: '#ff6b6b',
  amber: '#f4b400',
};

const ease = t => { if (t<=0) return 0; if (t>=1) return 1; return 1 - Math.pow(1-t, 3); };
const fade = (time, start, dur=0.2) => Math.max(0, Math.min(1, ease((time-start)/dur)));
const slideUp = (time, start, dur=0.2, dist=4) => (1 - fade(time, start, dur)) * dist;
// Gradient — display-size emphasized green text. Reference as the "gradient" style.
const gradientStyle = {
  background: 'linear-gradient(135deg, #00E87B 0%, #00da41 50%, #00B35A 100%)',
  WebkitBackgroundClip: 'text',
  WebkitTextFillColor: 'transparent',
  backgroundClip: 'text',
  textShadow: 'none',
};
// Chat font — system stack so the Copilot chat reads as native VS Code (not Geologica).
const chatFont = 'system-ui, -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif';

// ─── Chip ──────────────────────────────────────────────────────
function AFChip({ skill, start = 0 }) {
  const time = useTime();
  const o = fade(time, start, 0.18);
  return (
    <div style={{
      display:'inline-flex', alignItems:'center', gap:8,
      background: ED.charcoal,
      borderLeft:`2px solid ${ED.green}`,
      borderRadius:4, padding:'4px 10px',
      fontFamily:T.sans, fontSize:11, fontWeight:500, color:ED.textGray,
      letterSpacing:'0.01em',
      opacity:o, transform:`translateY(${slideUp(time,start)}px)`,
    }}>
      <span style={{ width:6, height:6, borderRadius:3, background:ED.green, flexShrink:0 }} />
      <span>Agentic Forge · {skill}</span>
    </div>
  );
}

// ─── Card ──────────────────────────────────────────────────────
function AFCard({ children, accent, start = 0, style }) {
  const time = useTime();
  const o = fade(time, start, 0.22);
  return (
    <div style={{
      position:'relative',
      background: ED.charcoal,
      border:`1px solid ${ED.charcoalLine}`,
      borderRadius:12, padding:16,
      fontFamily:T.sans, color:ED.textWhite,
      opacity:o, transform:`translateY(${slideUp(time,start)}px)`,
      overflow:'hidden',
      ...style,
    }}>
      {accent && <div style={{ position:'absolute', left:0, top:0, bottom:0, width:4, background:accent }} />}
      {children}
    </div>
  );
}
function CardTitleRow({ id, badge, badgeColor }) {
  return (
    <div style={{ display:'flex', alignItems:'baseline', gap:8, fontSize:12, color:ED.textGray, fontWeight:400 }}>
      <span>{id}</span>
      <span style={{ color:ED.textDim }}>·</span>
      <span style={{ color:badgeColor, fontWeight:500 }}>{badge}</span>
    </div>
  );
}
function CardDivider() { return <div style={{ height:1, background:ED.charcoalLine, margin:'10px 0' }} />; }
function SectionLabel({ children }) { return <div style={{ fontSize:13, color:ED.textGray, fontWeight:500, marginBottom:6 }}>{children}</div>; }

// ─── Message ──────────────────────────────────────────────────
// AF messages use the chip AS the sender label. Copilot messages keep Copilot identity.
function AFMessage({ chip, start = 0, children }) {
  const time = useTime();
  const o = fade(time, start, 0.22);
  return (
    <div style={{ padding:'14px 16px', borderBottom:'1px solid #2b2b2b', opacity:o, transform:`translateY(${slideUp(time,start)}px)` }}>
      <div style={{ marginBottom:10 }}><AFChip skill={chip} start={start + 0.05} /></div>
      <div style={{ fontSize:14, color:'#cccccc', fontFamily:chatFont, lineHeight:1.55, display:'flex', flexDirection:'column', gap:10 }}>
        {children}
      </div>
    </div>
  );
}

function CopilotMessage({ start = 0, children, time = '13:15' }) {
  const t = useTime();
  const o = fade(t, start, 0.2);
  return (
    <div style={{ padding:'12px 16px', borderBottom:'1px solid #2b2b2b', opacity:o }}>
      <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:6 }}>
        <CopilotIcon />
        <span style={{ fontSize:12, color:'#fff', fontWeight:500 }}>GitHub Copilot</span>
        <span style={{ marginLeft:'auto', fontSize:11, color:'#858585', fontFamily:T.mono }}>{time}</span>
      </div>
      <div style={{ paddingLeft:26, fontSize:14, color:'#cccccc', fontFamily:chatFont, lineHeight:1.55 }}>{children}</div>
    </div>
  );
}

function UserMessage({ text, start = 0, time='13:15' }) {
  const t = useTime();
  const o = fade(t, start, 0.15);
  return (
    <div style={{ padding:'12px 16px', borderBottom:'1px solid #2b2b2b', opacity:o }}>
      <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:4 }}>
        <div style={{ width:18, height:18, borderRadius:9, background:'#5b5f66', color:'#fff', display:'flex', alignItems:'center', justifyContent:'center', fontSize:10, fontWeight:500 }}>C</div>
        <span style={{ fontSize:12, color:'#fff', fontWeight:500 }}>Carlos</span>
        <span style={{ marginLeft:'auto', fontSize:11, color:'#858585', fontFamily:T.mono }}>{time}</span>
      </div>
      <div style={{ paddingLeft:26, fontSize:14, color:'#cccccc', fontFamily:chatFont }}>{text}</div>
    </div>
  );
}

function CopilotIcon() {
  return (
    <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
      <path d="M9 2.5C6.5 2.5 4.8 3.7 4.8 5.6V7.4C3.4 7.9 2.5 9.1 2.5 10.6C2.5 12.4 4 14 5.8 14H12.2C14 14 15.5 12.4 15.5 10.6C15.5 9.1 14.6 7.9 13.2 7.4V5.6C13.2 3.7 11.5 2.5 9 2.5Z" fill="#cccccc" fillOpacity="0.15" stroke="#cccccc" strokeWidth="1.3" strokeLinejoin="round"/>
      <circle cx="6.8" cy="10.5" r="0.9" fill="#cccccc"/>
      <circle cx="11.2" cy="10.5" r="0.9" fill="#cccccc"/>
    </svg>
  );
}

// ─── Spinner / check (animated for TOC) ───────────────────────
function SpinCheck({ checked, checkedAt, time }) {
  const o = checked ? fade(time, checkedAt, 0.3) : 0;
  // spinner rotation
  const angle = (time * 360) % 360;
  return (
    <div style={{ width:14, height:14, position:'relative', flexShrink:0 }}>
      {/* spinner */}
      <svg width="14" height="14" viewBox="0 0 14 14" style={{ position:'absolute', inset:0, opacity: 1 - o, transform:`rotate(${angle}deg)`, transformOrigin:'center' }}>
        <circle cx="7" cy="7" r="5.5" stroke="#444" strokeWidth="1.4" fill="none" />
        <path d="M7 1.5 A5.5 5.5 0 0 1 12.5 7" stroke={ED.textGray} strokeWidth="1.4" fill="none" strokeLinecap="round" />
      </svg>
      {/* check */}
      <svg width="14" height="14" viewBox="0 0 14 14" style={{ position:'absolute', inset:0, opacity:o }}>
        <path d="M3 7.2 L6 10 L11.2 4.4" stroke={ED.green} strokeWidth="1.7" fill="none" strokeLinecap="round" strokeLinejoin="round" />
      </svg>
    </div>
  );
}

// ─── IDE Frame ────────────────────────────────────────────────
function IDEFrame({ activeFile, branch, fileTree, children, terminal, chatChildren, lineCount }) {
  return (
    <div style={{ position:'absolute', inset:0, background:'#1e1e1e', color:'#cccccc', fontFamily:T.sans, display:'flex', flexDirection:'column' }}>
      <TitleBar activeFile={activeFile} />
      <div style={{ flex:1, display:'flex', minHeight:0 }}>
        <ActivityBar />
        <FileTree activeFile={activeFile} branch={branch} />
        <div style={{ flex:1, display:'flex', flexDirection:'column', minWidth:0 }}>
          <TabBar activeFile={activeFile} />
          <div style={{ flex:1, position:'relative', minHeight:0, overflow:'hidden' }}>
            {children}
          </div>
          {terminal}
        </div>
        <ChatPanel>{chatChildren}</ChatPanel>
      </div>
      <StatusBar branch={branch} />
    </div>
  );
}

function TitleBar({ activeFile }) {
  return (
    <div style={{ height:30, background:'#3c3c3c', display:'flex', alignItems:'center', padding:'0 12px', flexShrink:0, fontSize:12, color:'#cccccc' }}>
      <div style={{ display:'flex', gap:8, marginRight:16 }}>
        <div style={{ width:12, height:12, borderRadius:6, background:'#ff5f57' }} />
        <div style={{ width:12, height:12, borderRadius:6, background:'#febc2e' }} />
        <div style={{ width:12, height:12, borderRadius:6, background:'#28c840' }} />
      </div>
      <div style={{ flex:1, textAlign:'center', fontFamily:T.mono, fontSize:12 }}>{activeFile} — platform</div>
    </div>
  );
}

function ActivityBar() {
  return (
    <div style={{ width:48, background:'#181818', borderRight:'1px solid #2b2b2b', display:'flex', flexDirection:'column', alignItems:'center', paddingTop:10, gap:14, flexShrink:0 }}>
      {[0,1,2,3,4,5].map(i => (
        <div key={i} style={{ width:32, height:32, display:'flex', alignItems:'center', justifyContent:'center', borderLeft: i===0 ? '2px solid #fff' : '2px solid transparent', color: i===0 ? '#fff' : '#858585' }}>
          <ActivityIcon i={i} />
        </div>
      ))}
    </div>
  );
}
function ActivityIcon({ i }) {
  const c='currentColor', sw=1.4, s=18;
  switch(i){
    case 0: return <svg width={s} height={s} viewBox="0 0 18 18" fill="none"><path d="M2 3h4l1.5 1.5H16v11H2V3z" stroke={c} strokeWidth={sw}/></svg>;
    case 1: return <svg width={s} height={s} viewBox="0 0 18 18" fill="none"><circle cx="8" cy="8" r="5" stroke={c} strokeWidth={sw}/><path d="M12 12l3 3" stroke={c} strokeWidth={sw} strokeLinecap="round"/></svg>;
    case 2: return <svg width={s} height={s} viewBox="0 0 18 18" fill="none"><circle cx="5" cy="4" r="2" stroke={c} strokeWidth={sw}/><circle cx="5" cy="14" r="2" stroke={c} strokeWidth={sw}/><circle cx="13" cy="8" r="2" stroke={c} strokeWidth={sw}/><path d="M5 6v6M13 10c0 2-2 3-4 3" stroke={c} strokeWidth={sw}/></svg>;
    case 3: return <svg width={s} height={s} viewBox="0 0 18 18" fill="none"><circle cx="9" cy="9" r="4" stroke={c} strokeWidth={sw}/></svg>;
    case 4: return <svg width={s} height={s} viewBox="0 0 18 18" fill="none"><rect x="3" y="3" width="5" height="5" stroke={c} strokeWidth={sw}/><rect x="10" y="3" width="5" height="5" stroke={c} strokeWidth={sw}/><rect x="3" y="10" width="5" height="5" stroke={c} strokeWidth={sw}/></svg>;
    case 5: return <svg width={s} height={s} viewBox="0 0 18 18" fill="none"><path d="M9 2c-3 0-4.5 1.5-4.5 3.5v2C3 8 2 9 2 11c0 2 1.5 3.5 3.5 3.5h7C14.5 14.5 16 13 16 11c0-2-1-3-2.5-3.5v-2C13.5 3.5 12 2 9 2z" stroke={c} strokeWidth={sw}/></svg>;
  }
}

function FileTree({ activeFile, branch }) {
  return (
    <div style={{ width:220, background:'#181818', borderRight:'1px solid #2b2b2b', flexShrink:0, fontSize:13, padding:'8px 0', color:'#cccccc' }}>
      <div style={{ padding:'4px 14px', fontSize:11, color:'#858585', letterSpacing:'0.06em', fontWeight:500 }}>PLATFORM</div>
      {[
        { d:0, k:'folder', n:'src', open:true },
        { d:1, k:'folder', n:'api' },
        { d:1, k:'folder', n:'services', open:true },
        { d:2, k:'folder', n:'payment', open:true },
        { d:3, k:'file', n:'index.ts', f:'src/services/payment/index.ts' },
        { d:3, k:'file', n:'retry.ts', f:'src/services/payment/retry.ts' },
        { d:3, k:'file', n:'service.ts' },
        { d:3, k:'file', n:'types.ts' },
        { d:2, k:'folder', n:'notifications' },
        { d:1, k:'folder', n:'lib' },
        { d:0, k:'folder', n:'tests' },
        { d:0, k:'file', n:'package.json' },
      ].map((it, i) => {
        const isActive = it.f === activeFile;
        return (
          <div key={i} style={{ display:'flex', alignItems:'center', gap:6, padding:'3px 0', paddingLeft: 12 + it.d * 12, background: isActive ? '#2a2d2e' : 'transparent', color: isActive ? '#fff' : '#cccccc', fontSize:13 }}>
            {it.k === 'folder' ? <span style={{ color:'#858585', fontSize:9, width:8 }}>{it.open ? '▾' : '▸'}</span> : <span style={{ width:8 }} />}
            <span>{it.n}</span>
          </div>
        );
      })}
    </div>
  );
}

function TabBar({ activeFile }) {
  const name = activeFile.split('/').pop();
  return (
    <div style={{ height:34, background:'#181818', borderBottom:'1px solid #2b2b2b', display:'flex', flexShrink:0 }}>
      <div style={{ padding:'0 14px', display:'flex', alignItems:'center', gap:8, background:'#1e1e1e', borderRight:'1px solid #2b2b2b', fontFamily:T.mono, fontSize:12, color:'#fff' }}>
        <span style={{ color:'#519aba' }}>◦</span>{name}
      </div>
    </div>
  );
}

function StatusBar({ branch }) {
  return (
    <div style={{ height:22, background:'#007acc', display:'flex', alignItems:'center', padding:'0 10px', gap:12, fontSize:11, color:'#fff', fontFamily:T.sans, flexShrink:0 }}>
      <span>◉ {branch}</span>
      <span style={{ opacity:0.85 }}>✓ 0</span>
      <span style={{ marginLeft:'auto', opacity:0.85 }}>TypeScript</span>
      <span style={{ opacity:0.85 }}>UTF-8</span>
    </div>
  );
}

function ChatPanel({ children }) {
  return (
    <div style={{ width:480, background:'#1f1f1f', borderLeft:'1px solid #2b2b2b', display:'flex', flexDirection:'column', flexShrink:0 }}>
      {/* Chat header with persistent AF connected indicator */}
      <div style={{ height:40, background:'#181818', borderBottom:'1px solid #2b2b2b', display:'flex', alignItems:'center', padding:'0 14px', gap:10, flexShrink:0 }}>
        <CopilotIcon />
        <span style={{ fontSize:13, color:'#fff', fontWeight:500 }}>GitHub Copilot</span>
        <div style={{ marginLeft:'auto', display:'inline-flex', alignItems:'center', gap:6, fontSize:11, color:ED.textGray, fontFamily:T.sans, fontWeight:400 }}>
          <span style={{ width:6, height:6, borderRadius:3, background:ED.green }} />
          Agentic Forge connected
        </div>
      </div>
      <div className="chat-scroll" style={{ flex:1, overflow:'hidden', display:'flex', flexDirection:'column' }}>
        {children}
      </div>
      <div style={{ padding:10, borderTop:'1px solid #2b2b2b', flexShrink:0 }}>
        <div style={{ background:'#252526', border:'1px solid #2b2b2b', borderRadius:4, padding:'8px 10px', fontSize:13, color:'#858585', fontFamily:chatFont }}>Ask Copilot…</div>
      </div>
    </div>
  );
}

// ─── Editor view ──────────────────────────────────────────────
function EditorView({ lines }) {
  return (
    <div style={{ background:'#1e1e1e', height:'100%', padding:'10px 0', overflow:'hidden' }}>
      {lines.map((l, i) => (
        <div key={i} style={{ display:'flex', minHeight:21 }}>
          <div style={{ width:48, textAlign:'right', color:'#858585', paddingRight:14, fontFamily:T.mono, fontSize:13, lineHeight:'21px', userSelect:'none' }}>{i+1}</div>
          <div style={{ flex:1, fontFamily:T.mono, fontSize:13, lineHeight:'21px', whiteSpace:'pre' }}>{tokenize(l).map((t,j) => <span key={j} style={{ color: t.c || '#d4d4d4' }}>{t.t}</span>)}</div>
        </div>
      ))}
    </div>
  );
}
function tokenize(s) {
  const out = [];
  const patterns = [
    [/^\/\/[^\n]*/, '#6a9955'],
    [/^(import|export|from|const|let|return|async|await|function|class|interface|type|if|else|try|catch|throw|new|for|of|in|private|public)\b/, '#c586c0'],
    [/^'[^']*'/, '#ce9178'], [/^"[^"]*"/, '#ce9178'], [/^`[^`]*`/, '#ce9178'],
    [/^\b\d[\d_]*\b/, '#b5cea8'],
    [/^\b[A-Z][A-Za-z0-9_]*\b/, '#4ec9b0'],
    [/^\b[a-z_][A-Za-z0-9_]*(?=\()/, '#dcdcaa'],
    [/^\s+/, null], [/^./, '#d4d4d4'],
  ];
  let r = s;
  while (r.length) {
    let m = false;
    for (const [re, c] of patterns) {
      const x = r.match(re);
      if (x) { out.push({ t:x[0], c }); r = r.slice(x[0].length); m=true; break; }
    }
    if (!m) { out.push({ t:r[0], c:null }); r = r.slice(1); }
  }
  return out;
}

// ─── Terminal ─────────────────────────────────────────────────
function TerminalPanel({ start, cmd, branchAfter }) {
  const time = useTime();
  const e = Math.max(0, time - start);
  const n = Math.min(cmd.length, Math.floor(e * 22));
  const typed = cmd.slice(0, n);
  const showResult = e > cmd.length / 22 + 0.4;
  return (
    <div style={{ height:170, borderTop:'1px solid #2b2b2b', background:'#1e1e1e', display:'flex', flexDirection:'column', flexShrink:0 }}>
      <div style={{ height:30, background:'#181818', display:'flex', alignItems:'center', padding:'0 14px', gap:14, fontSize:11, color:'#858585', borderBottom:'1px solid #2b2b2b' }}>
        <span style={{ color:'#fff', borderBottom:'1px solid #fff', paddingBottom:6 }}>TERMINAL</span>
        <span>PROBLEMS</span><span>OUTPUT</span>
      </div>
      <div style={{ flex:1, padding:'10px 14px', fontFamily:T.mono, fontSize:13, color:'#cccccc', lineHeight:1.6 }}>
        <div>
          <span style={{ color:ED.green }}>carlos@ledgerline</span>
          <span style={{ color:'#858585' }}>:</span>
          <span style={{ color:'#519aba' }}>~/platform</span>
          <span style={{ color:'#858585' }}> ({branchAfter && showResult ? branchAfter : 'main'}) $ </span>
          <span>{typed}</span>
          {!showResult && e > 0 && Math.floor(e*2)%2===0 && <span style={{ background:'#cccccc', color:'#cccccc' }}>&nbsp;</span>}
        </div>
        {showResult && branchAfter && (
          <div style={{ color:'#858585' }}>Switched to a new branch '<span style={{ color:'#cccccc' }}>{branchAfter}</span>'</div>
        )}
      </div>
    </div>
  );
}

// ─── Subtitles ────────────────────────────────────────────────
const SUBS = [
  { t: 0.5,   end: 4.0,   text: "Carlos is starting his Monday at Ledgerline." },
  { t: 4.2,   end: 8.0,   text: "He's been using Agentic Forge for six weeks." },
  { t: 25.0,  end: 30.5,  text: "He's not asking an AI for an opinion." },
  { t: 30.7,  end: 36.0,  text: "He's asking the framework what's actually ready to work on" },
  { t: 36.1,  end: 41.0,  text: "with effort calibrated against six prior payment stories." },
  { t: 42.5,  end: 47.5,  text: "Carlos creates a branch." },
  { t: 47.7,  end: 55.0,  text: "He didn't ask Copilot for context — the framework saw the branch name and supplied it." },
  { t: 85.0,  end: 89.0,  text: "He opens a file. There's a known issue here." },
  { t: 89.2,  end: 95.0,  text: "The framework mentions it, doesn't block him," },
  { t: 95.1,  end: 100.0, text: "and tells him exactly which story already covers the remediation." },
  { t: 120.0, end: 125.0, text: "Carlos opens a pull request." },
  { t: 125.2, end: 131.0, text: "Four agents review it — criteria, security, governance, coverage." },
  { t: 131.2, end: 138.0, text: "Each one names what it found, with file and line citations." },
  { t: 168.0, end: 172.5, text: "Carlos didn't change tools." },
  { t: 172.7, end: 178.0, text: "Copilot just got informed." },
];

function Subtitles() {
  const time = useTime();
  const active = SUBS.find(s => time >= s.t && time <= s.end);
  if (!active) return null;
  // wrap on max 42 chars
  const words = active.text.split(' ');
  const lines = [];
  let cur = '';
  for (const w of words) {
    const next = cur ? cur + ' ' + w : w;
    if (next.length > 42) { lines.push(cur); cur = w; } else cur = next;
  }
  if (cur) lines.push(cur);
  return (
    <div style={{ position:'absolute', left:0, right:0, bottom:80, display:'flex', justifyContent:'center', pointerEvents:'none', zIndex:50 }}>
      <div style={{ background:'rgba(28, 30, 36, 0.78)', borderRadius:4, padding:'8px 14px', maxWidth:'70%' }}>
        {lines.map((l, i) => (
          <div key={i} style={{ fontFamily:T.sans, fontWeight:500, fontSize:22, color:'#fff', textAlign:'center', lineHeight:1.35 }}>{l}</div>
        ))}
      </div>
    </div>
  );
}

// ════════════════════════════════════════════════════════════════
// SCENE 1 — Monday priority queue (0-42s)
// ════════════════════════════════════════════════════════════════
function Scene1() {
  const time = useTime();
  // Beats
  const typeStart = 5.0;
  const sendT = 12.5;
  const respT = 13.2;     // shimmer ~0.4s before content
  const proseT = 13.6;
  const card1T = 14.2;
  const card2T = 14.4;
  const card3T = 14.6;
  const recoT = 16.0;

  const fullQ = 'what should I work on';
  // realistic typing — ~5 cps with pause before "work"
  const typed = (() => {
    if (time < typeStart) return '';
    let acc = '';
    let t = 0;
    for (let i = 0; i < fullQ.length; i++) {
      const ch = fullQ[i];
      const baseDelay = 0.18;
      const pause = (fullQ.slice(0, i) === 'what should I ') ? 0.45 : 0;
      t += baseDelay + pause;
      if (t > sendT - typeStart - 0.2) break;
      if (time - typeStart < t) break;
      acc += ch;
    }
    return acc;
  })();
  const inputActive = time >= typeStart && time < sendT;

  return (
    <IDEFrame
      activeFile="src/services/payment/index.ts"
      branch="main"
      chatChildren={
        <div style={{ flex:1, display:'flex', flexDirection:'column', overflow:'hidden' }}>
          {time > sendT && <UserMessage start={sendT} text="what should I work on" time="13:15" />}

          {time > respT && time < respT + 0.4 && <ShimmerLine />}

          {time > respT + 0.3 && (
            <AFMessage chip="work-prioritization" start={respT + 0.3}>
              <div style={{ opacity: fade(time, proseT, 0.2) }}>Three top priorities:</div>

              <AFCard accent={ED.blue} start={card1T}>
                <CardTitleRow id="STORY-041" badge="Critical" badgeColor={ED.red} />
                <CardDivider />
                <div style={{ fontSize:14, color:ED.textWhite }}>Payment retry logic for failed charges</div>
                <div style={{ marginTop:10, display:'flex', flexDirection:'column', gap:3 }}>
                  <Meta label="Dependencies" value="satisfied" />
                  <Meta label="Estimate" value="4h (calibrated 1.0× · N=6 prior)" />
                </div>
              </AFCard>

              <AFCard start={card2T}>
                <CardTitleRow id="STORY-038" badge="High" badgeColor={ED.amber} />
                <CardDivider />
                <div style={{ fontSize:14, color:ED.textWhite }}>Email notification preferences API</div>
                <div style={{ marginTop:10, display:'flex', flexDirection:'column', gap:3 }}>
                  <Meta label="Dependencies" value="satisfied" />
                  <Meta label="Estimate" value="3h (calibrated 1.15× · N=4 prior)" />
                </div>
              </AFCard>

              <AFCard start={card3T}>
                <CardTitleRow id="STORY-044" badge="High" badgeColor={ED.amber} />
                <CardDivider />
                <div style={{ fontSize:14, color:ED.textWhite }}>Dashboard loading optimization</div>
                <div style={{ marginTop:12, display:'flex', flexDirection:'column', gap:4 }}>
                  <div style={{ fontSize:13, color:ED.red, fontWeight:500 }}>⚠ BLOCKED by DECISION-019 (caching strategy)</div>
                  <div style={{ fontSize:13, color:ED.textGray, fontWeight:500, paddingLeft:18 }}>Pending 21 days · $31K/week across 3 stories</div>
                </div>
              </AFCard>

              <div style={{ opacity: fade(time, recoT, 0.3) }}>Recommend STORY-041.</div>
            </AFMessage>
          )}
        </div>
      }
    >
      <EditorView lines={[
        "// src/services/payment/index.ts",
        "import { PaymentService } from './service';",
        "import { logger } from '../../lib/logger';",
        "import type { Charge, RetryResult } from './types';",
        "",
        "export const payment = new PaymentService(logger);",
        "",
        "export * from './types';",
      ]} />
      {inputActive && <TypingInputOverlay typed={typed} />}
    </IDEFrame>
  );
}

function Meta({ label, value }) {
  return (
    <div style={{ fontSize:12, fontFamily:T.sans }}>
      <span style={{ color:ED.textDim }}>{label}: </span>
      <span style={{ color:ED.textGray }}>{value}</span>
    </div>
  );
}

function ShimmerLine() {
  const time = useTime();
  return (
    <div style={{ padding:'14px 16px' }}>
      <div style={{ display:'flex', gap:6 }}>
        {[0,1,2].map(i => (
          <div key={i} style={{
            width:6, height:6, borderRadius:3, background:ED.textGray,
            opacity: 0.3 + 0.7 * (0.5 + 0.5 * Math.sin(time*6 - i*0.6))
          }} />
        ))}
      </div>
    </div>
  );
}

// Renders the chat input with what's being typed (covers default empty input)
function TypingInputOverlay({ typed }) {
  return (
    <div style={{ position:'fixed', right:11, bottom:33, width:458, background:'#252526', border:`1px solid ${ED.blue}`, borderRadius:4, padding:'8px 10px', fontSize:13, color:'#cccccc', fontFamily:chatFont, zIndex:30, boxSizing:'border-box' }}>
      {typed || <span style={{ color:'#858585' }}>Ask Copilot…</span>}
      <span style={{ background:'#cccccc', color:'#cccccc', display:'inline-block', width:2, marginLeft:1 }}>&nbsp;</span>
    </div>
  );
}

// ════════════════════════════════════════════════════════════════
// SCENE 2 — Branch creation loads context (42-68s)
// ════════════════════════════════════════════════════════════════
function Scene2({ sceneStart = 42 }) {
  const time = useTime();
  const local = time - sceneStart;
  const cmdT = sceneStart + 1.0;       // begin typing terminal cmd
  const branchOk = local > 4.5;        // command completes
  const msgT = sceneStart + 6.0;       // AF message appears ~1s after branch
  const cardT = msgT + 0.5;
  return (
    <IDEFrame
      activeFile="src/services/payment/index.ts"
      branch={branchOk ? 'feature/STORY-041-payment-retry' : 'main'}
      chatChildren={
        <div style={{ flex:1, display:'flex', flexDirection:'column', overflow:'hidden' }}>
          {/* prior priority message — collapsed/persistent context, dimmed */}
          <PriorPriorityCondensed time="13:15" opacity={0.35} />

          {time > msgT - 0.1 && (
            <AFMessage chip="context loaded" start={msgT}>
              <div style={{ opacity: fade(time, msgT + 0.2, 0.25) }}>STORY-041 context loaded.</div>
              <AFCard start={cardT}>
                <CardTitleRow id="STORY-041" badge="Payment retry logic" badgeColor={ED.textGray} />
                <CardDivider />
                <SectionLabel>Acceptance criteria (4)</SectionLabel>
                <ul style={{ margin:0, paddingLeft:18, fontSize:13, color:ED.textWhite, lineHeight:1.6 }}>
                  <li>Charge retried up to 3× with backoff</li>
                  <li>Idempotent on charge_id</li>
                  <li>Failure logged with reason</li>
                  <li>Customer notified after 3 failures</li>
                </ul>
                <div style={{ height:1, background:ED.charcoalLine, margin:'12px 0 10px' }} />
                <SectionLabel>Related decisions</SectionLabel>
                <div style={{ fontSize:13, color:ED.textWhite, lineHeight:1.6 }}>
                  <div>· DECISION-008 · Stripe primary</div>
                  <div>· DECISION-015 · Bull queue</div>
                  <div>· DECISION-018 · Backoff (30s, 120s, 600s)</div>
                </div>
                <div style={{ height:1, background:ED.charcoalLine, margin:'12px 0 10px' }} />
                <SectionLabel>Pattern reference</SectionLabel>
                <div style={{ fontSize:13, color:ED.textWhite, lineHeight:1.6 }}>
                  <div>src/services/payment/* uses repository pattern</div>
                  <div>PaymentRetryService scaffold exists</div>
                </div>
                <div style={{ height:1, background:ED.charcoalLine, margin:'12px 0 10px' }} />
                <SectionLabel>Existing tests</SectionLabel>
                <div style={{ fontSize:13, color:ED.textWhite, lineHeight:1.6 }}>
                  12 in payment.test.ts (87% coverage on module)
                </div>
              </AFCard>
            </AFMessage>
          )}
        </div>
      }
      terminal={<TerminalPanel start={cmdT} cmd="git checkout -b feature/STORY-041-payment-retry" branchAfter="feature/STORY-041-payment-retry" />}
    >
      <EditorView lines={[
        "// src/services/payment/index.ts",
        "import { PaymentService } from './service';",
        "import { logger } from '../../lib/logger';",
        "import type { Charge, RetryResult } from './types';",
        "",
        "export const payment = new PaymentService(logger);",
        "",
        "export * from './types';",
      ]} />
    </IDEFrame>
  );
}

function PriorPriorityCondensed({ time, opacity = 0.4 }) {
  return (
    <div style={{ padding:'10px 16px', borderBottom:'1px solid #2b2b2b', opacity }}>
      <UserMessage text="what should I work on" start={0} time={time} />
    </div>
  );
}

// ════════════════════════════════════════════════════════════════
// SCENE 3 — Mid-implementation finding (68-100s)
// ════════════════════════════════════════════════════════════════
function Scene3({ sceneStart = 68 }) {
  const time = useTime();
  const local = time - sceneStart;
  const fileOpenT = sceneStart + 0.5;     // file opens immediately on cut
  const msgT = sceneStart + 6.5;          // ~1s after the visible scroll/edit settling
  const cardT = msgT + 0.5;
  return (
    <IDEFrame
      activeFile="src/services/payment/retry.ts"
      branch="feature/STORY-041-payment-retry"
      chatChildren={
        <div style={{ flex:1, display:'flex', flexDirection:'column', overflow:'hidden' }}>
          {/* prior context message — condensed bar */}
          <CondensedAFBar chip="context loaded" subtitle="STORY-041 context · 4 AC · 3 decisions" />

          {time > msgT - 0.1 && (
            <AFMessage chip="finding surfaced" start={msgT}>
              <div style={{ opacity: fade(time, msgT + 0.2, 0.25) }}>
                There's a known finding in this file. Not blocking your current story; flagging for awareness.
              </div>
              <AFCard start={cardT}>
                <CardTitleRow id="FINDING-023" badge="medium" badgeColor={ED.textGray} />
                <CardDivider />
                <div style={{ fontSize:14, color:ED.textWhite, lineHeight:1.5 }}>
                  retry.ts catches all exceptions but only logs <span style={{ fontFamily:T.mono, fontSize:12, color:ED.textGray, background:'#0f1216', padding:'1px 6px', borderRadius:3 }}>error.message</span>. Stack traces are lost.
                </div>
                <div style={{ height:1, background:ED.charcoalLine, margin:'12px 0 10px' }} />
                <div style={{ display:'flex', flexDirection:'column', gap:4 }}>
                  <Meta label="Remediation tracked" value="STORY-052" />
                  <Meta label="Status" value="not blocking your current work" />
                  <Meta label="Source" value="codebase scan, week 2" />
                </div>
              </AFCard>
            </AFMessage>
          )}
        </div>
      }
    >
      <EditorView lines={[
        "// src/services/payment/retry.ts",
        "import { Charge } from './types';",
        "import { logger } from '../../lib/logger';",
        "",
        "export class PaymentRetryService {",
        "  async retry(charge: Charge): Promise<void> {",
        "    try {",
        "      await this.attempt(charge);",
        "    } catch (error) {",
        "      logger.error('retry failed', { message: error.message });",
        "    }",
        "  }",
        "",
        "  private async attempt(charge: Charge) {",
        "    return await this.gateway.charge(charge);",
        "  }",
        "}",
      ]} />
    </IDEFrame>
  );
}

function CondensedAFBar({ chip, subtitle }) {
  return (
    <div style={{ padding:'10px 16px', borderBottom:'1px solid #2b2b2b', opacity:0.45, display:'flex', flexDirection:'column', gap:4 }}>
      <AFChip skill={chip} />
      <div style={{ fontSize:12, color:ED.textGray, paddingLeft:2 }}>{subtitle}</div>
    </div>
  );
}

// ════════════════════════════════════════════════════════════════
// SCENE 4 — PR review chain (100-168s)
// ════════════════════════════════════════════════════════════════
function Scene4({ sceneStart = 100 }) {
  const time = useTime();
  const t = time - sceneStart;

  // Beats
  // 0-8 establish PR creation (we show a brief PR-create snapshot, then cut)
  // 8-18 chain-started TOC message
  // 18-34 criteria-verification (16s)
  // 34-45 code-security-review (11s)
  // 45-56 governance (11s)
  // 56-64 coverage (8s)
  // 64-68 review complete summary (4s)

  const prCreateEnd = sceneStart + 8;
  const tocT = sceneStart + 8.5;
  const c1T = sceneStart + 18;
  const c2T = sceneStart + 34;
  const c3T = sceneStart + 45;
  const c4T = sceneStart + 56;
  const finalT = sceneStart + 64;

  // chip TOC ticking moments — match each card's chip time
  const tocTicks = [c1T, c2T, c3T, c4T];

  // PR creation moment (brief screen)
  if (time < prCreateEnd) {
    return <PRCreateView start={sceneStart} />;
  }

  return (
    <IDEFrame
      activeFile="src/services/payment/retry.ts"
      branch="feature/STORY-041-payment-retry"
      chatChildren={
        <div className="chat-scroll" style={{ flex:1, overflow:'auto', display:'flex', flexDirection:'column' }}>
          {/* Persisted condensed context */}
          <CondensedAFBar chip="context loaded" subtitle="STORY-041 · loaded" />

          {/* TOC message */}
          {time > tocT - 0.1 && (
            <AFMessage chip="review chain started" start={tocT}>
              <div style={{ opacity: fade(time, tocT + 0.2, 0.25) }}>PR #312 opened. Running review chain:</div>
              <div style={{ background:'#0f1216', border:`1px solid ${ED.charcoalLine}`, borderRadius:8, padding:14, display:'flex', flexDirection:'column', gap:10, fontFamily:T.sans }}>
                {[
                  { n:1, name:'criteria-verification', agent:'Code Reviewer' },
                  { n:2, name:'code-security-review', agent:'Security Auditor' },
                  { n:3, name:'governance-validation', agent:'Governance Engine' },
                  { n:4, name:'coverage-analysis',     agent:'Test Strategist' },
                ].map((row, i) => {
                  const checked = time > tocTicks[i];
                  return (
                    <div key={i} style={{ display:'flex', alignItems:'center', gap:10, fontSize:13 }}>
                      <span style={{ color:ED.textDim, fontFamily:T.mono, width:12 }}>{row.n}.</span>
                      <SpinCheck checked={checked} checkedAt={tocTicks[i]} time={time} />
                      <span style={{ fontFamily:T.mono, fontSize:12, color: checked ? ED.textWhite : ED.textGray, minWidth:200 }}>{row.name}</span>
                      <span style={{ color:ED.textDim, fontSize:12 }}>·</span>
                      <span style={{ fontSize:12, color:ED.textGray }}>{row.agent}</span>
                    </div>
                  );
                })}
              </div>
            </AFMessage>
          )}

          {/* Check 1 — criteria */}
          {time > c1T - 0.1 && (
            <AFMessage chip="criteria-verification" start={c1T}>
              <div>Criteria verification (PASS 4/4):</div>
              <AFCard start={c1T + 0.4}>
                <CardTitleRow id="STORY-041" badge="acceptance criteria" badgeColor={ED.textGray} />
                <CardDivider />
                <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
                  {[
                    ['1. Retry up to 3× with backoff', 'src/services/payment/retry.ts:42-58'],
                    ['2. Idempotent on charge_id', 'src/services/payment/retry.ts:31'],
                    ['3. Failure logged with reason', 'src/services/payment/retry.ts:71'],
                    ['4. Customer notified after 3 failures', 'src/services/payment/retry.ts:93'],
                  ].map(([text, cite], i) => (
                    <div key={i} style={{ display:'flex', flexDirection:'column', gap:2 }}>
                      <div style={{ fontSize:13, color:ED.textWhite }}>
                        <span style={{ color:ED.green, marginRight:6 }}>✓</span>{text}
                      </div>
                      <div style={{ fontFamily:T.mono, fontSize:11, color:ED.textGray, paddingLeft:18 }}>{cite}</div>
                    </div>
                  ))}
                </div>
              </AFCard>
            </AFMessage>
          )}

          {/* Check 2 — security */}
          {time > c2T - 0.1 && (
            <AFMessage chip="code-security-review" start={c2T}>
              <div>Security review (PASS):</div>
              <AFCard start={c2T + 0.4}>
                <SectionLabel>Security checks</SectionLabel>
                <div style={{ height:1, background:ED.charcoalLine, marginBottom:10 }} />
                <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
                  <CheckRowAF text="No hardcoded secrets" />
                  <CheckRowAF text="Webhook signature verification present" cite="src/services/payment/retry.ts:108" />
                  <CheckRowAF text="No PII logged" />
                  <CheckRowAF text="Idempotency key validated" />
                </div>
              </AFCard>
            </AFMessage>
          )}

          {/* Check 3 — governance */}
          {time > c3T - 0.1 && (
            <AFMessage chip="governance-validation" start={c3T}>
              <div>Governance (PASS): all rules pass.</div>
              <AFCard start={c3T + 0.4}>
                <SectionLabel>Governance rules evaluated</SectionLabel>
                <div style={{ height:1, background:ED.charcoalLine, marginBottom:10 }} />
                <div style={{ display:'flex', flexDirection:'column', gap:6, fontFamily:T.mono, fontSize:12 }}>
                  <GovRow rule="test_coverage" value="89%" limit="limit 75%" />
                  <GovRow rule="complexity_limit" value="max 6" limit="limit 12" />
                  <GovRow rule="decision_consistency" value="3 decisions cited" />
                  <GovRow rule="pii_logging" value="clean" />
                  <GovRow rule="no_hardcoded_secrets" value="clean" />
                  <GovRow rule="jwt_validation" value="no auth routes touched" />
                </div>
              </AFCard>
            </AFMessage>
          )}

          {/* Check 4 — coverage */}
          {time > c4T - 0.1 && (
            <AFMessage chip="coverage-analysis" start={c4T}>
              <div>Coverage analysis:</div>
              <AFCard start={c4T + 0.4}>
                <SectionLabel>Test coverage</SectionLabel>
                <div style={{ height:1, background:ED.charcoalLine, marginBottom:10 }} />
                <div style={{ display:'flex', flexDirection:'column', gap:5, fontSize:13, color:ED.textWhite, lineHeight:1.5 }}>
                  <CovRow label="Changed files" value="89% covered" />
                  <CovRow label="New test cases" value="+8" />
                  <CovRow label="Edge case coverage" value="complete" />
                  <CovRow label="Network timeout" value="integration-tested" />
                  <CovRow label="Idempotency replay" value="unit-tested" />
                  <CovRow label="3-failure threshold" value="unit-tested" />
                </div>
              </AFCard>
            </AFMessage>
          )}

          {/* Final summary */}
          {time > finalT - 0.1 && (
            <AFMessage chip="review complete" start={finalT}>
              <div style={{ fontSize:14, color:ED.textWhite }}>
                4/4 checks passed. <span style={{ color:ED.green, fontWeight:500 }}>Ready to merge.</span>
              </div>
            </AFMessage>
          )}

          <ChatAutoScroll />
        </div>
      }
    >
      <EditorView lines={[
        "export class PaymentRetryService {",
        "  constructor(",
        "    private queue: BullQueue,",
        "    private repo: ChargeRepository,",
        "    private notifier: CustomerNotifier,",
        "  ) {}",
        "",
        "  async retry(charge: Charge): Promise<RetryResult> {",
        "    const idempotencyKey = charge.id;",
        "    for (const delay of [30_000, 120_000, 600_000]) {",
        "      try {",
        "        const result = await this.attempt(charge, idempotencyKey);",
        "        if (result.ok) return result;",
        "      } catch (error) {",
        "        logger.error('retry failed', { chargeId: charge.id, error });",
        "      }",
        "      await sleep(delay);",
        "    }",
        "    await this.notifier.notify(charge);",
        "    return { ok: false, attempts: 3 };",
        "  }",
        "}",
      ]} />
    </IDEFrame>
  );
}

function CheckRowAF({ text, cite }) {
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:2 }}>
      <div style={{ fontSize:13, color:ED.textWhite }}>
        <span style={{ color:ED.green, marginRight:6 }}>✓</span>{text}
      </div>
      {cite && <div style={{ fontFamily:T.mono, fontSize:11, color:ED.textGray, paddingLeft:18 }}>{cite}</div>}
    </div>
  );
}
function GovRow({ rule, value, limit }) {
  return (
    <div style={{ display:'flex', alignItems:'baseline', gap:10, color:ED.textWhite }}>
      <span style={{ color:ED.green }}>✓</span>
      <span style={{ minWidth:200, color:ED.textWhite }}>{rule}</span>
      <span style={{ color:ED.textWhite }}>{value}</span>
      {limit && <span style={{ color:ED.textGray }}>({limit})</span>}
    </div>
  );
}
function CovRow({ label, value }) {
  return (
    <div style={{ display:'flex', justifyContent:'space-between', gap:10 }}>
      <span style={{ color:ED.textGray }}>{label}</span>
      <span style={{ color:ED.textWhite }}>{value}</span>
    </div>
  );
}

// auto-scroll the chat as new messages arrive
function ChatAutoScroll() {
  const time = useTime();
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const parent = el.parentElement;
    if (parent) parent.scrollTop = parent.scrollHeight;
  });
  return <div ref={ref} style={{ height:1 }} />;
}

// ─── PR creation snapshot (Scene 4 lead-in) ────────────────────
function PRCreateView({ start }) {
  const time = useTime();
  const local = time - start;
  // we render a GitHub-flavored PR-create form pane on the editor area
  return (
    <IDEFrame
      activeFile="GitHub Pull Request"
      branch="feature/STORY-041-payment-retry"
      chatChildren={
        <div style={{ flex:1, display:'flex', flexDirection:'column' }}>
          <CondensedAFBar chip="context loaded" subtitle="STORY-041 · loaded" />
        </div>
      }
    >
      <div className="chat-scroll" style={{ position:'absolute', inset:0, background:'#0d1117', color:'#e6edf3', padding:'30px 40px', overflow:'auto', fontFamily:T.sans }}>
        <div style={{ maxWidth:760 }}>
          <div style={{ fontSize:11, color:'#7d8590', fontFamily:T.mono, marginBottom:12 }}>ledgerline / platform</div>
          <div style={{ fontSize:24, color:'#e6edf3', fontWeight:500, marginBottom:18 }}>Open a pull request</div>
          <div style={{ display:'flex', gap:10, alignItems:'center', marginBottom:18, fontSize:13, color:'#7d8590' }}>
            <span style={{ background:'#1f6feb', color:'#fff', padding:'2px 10px', borderRadius:12, fontSize:12 }}>main</span>
            <span>←</span>
            <span style={{ background:'#21262d', color:'#7ee787', padding:'2px 10px', borderRadius:12, fontSize:12, fontFamily:T.mono }}>feature/STORY-041-payment-retry</span>
          </div>

          <div style={{ background:'#161b22', border:'1px solid #30363d', borderRadius:6, padding:'10px 12px', marginBottom:10, fontSize:14, color:'#e6edf3' }}>
            STORY-041 payment retry
          </div>
          <div style={{ background:'#161b22', border:'1px solid #30363d', borderRadius:6, padding:12, marginBottom:14, fontSize:13, color:'#e6edf3', minHeight:90, fontFamily:T.sans }}>
            Implements retry with backoff per DECISION-018. Idempotent on charge_id.
            <br/>Closes STORY-041.
          </div>

          <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
            <button style={{ background:'#21262d', color:'#e6edf3', border:'1px solid #30363d', borderRadius:6, padding:'5px 12px', fontSize:13 }}>Cancel</button>
            <button style={{
              background: local > 5 ? '#1f883d' : '#238636',
              color:'#fff', border:'1px solid rgba(240,246,252,0.1)', borderRadius:6, padding:'5px 16px', fontSize:13, fontWeight:500,
              boxShadow: local > 5 ? '0 0 0 3px rgba(31,136,61,0.4)' : 'none',
              transition:'box-shadow 0.2s',
            }}>Create pull request</button>
          </div>
        </div>
      </div>
    </IDEFrame>
  );
}

// ════════════════════════════════════════════════════════════════
// SCENE 5 — Closing card (168-180s)
// ════════════════════════════════════════════════════════════════
function Scene5({ sceneStart = 168 }) {
  const time = useTime();
  const a = fade(time, sceneStart + 0.3, 0.7);
  const b = fade(time, sceneStart + 1.6, 0.7);
  const c = fade(time, sceneStart + 3.0, 0.7);
  const bgO = fade(time, sceneStart, 0.6);
  return (
    <div style={{ position:'absolute', inset:0, background:ED.carbon, color:ED.textWhite, fontFamily:T.sans, display:'flex', alignItems:'center', justifyContent:'center', textAlign:'center' }}>
      <div style={{ position:'absolute', inset:0, background:'url(/assets/images/bg_hero.jpg) center/cover no-repeat', opacity:bgO }} />
      <div style={{ position:'relative', display:'flex', flexDirection:'column', alignItems:'center', gap:54, textShadow:'0 2px 12px rgba(0,0,0,0.7)' }}>
        <div style={{ fontSize:36, fontWeight:300, color:ED.textWhite, lineHeight:1.4, opacity:a, transform:`translateY(${(1-a)*8}px)` }}>
          Carlos used Copilot all day.<br/>
          <span style={gradientStyle}>He never opened Agentic Forge.</span>
        </div>
        <div style={{ fontSize:32, fontWeight:300, color:ED.textWhite, lineHeight:1.45, opacity:b, transform:`translateY(${(1-b)*8}px)` }}>
          Engineers don't change tools.<br/>
          <span style={gradientStyle}>The tools become informed.</span>
        </div>
      </div>
      <div style={{ position:'absolute', bottom:80, left:0, right:0, display:'flex', flexDirection:'column', alignItems:'center', gap:24, opacity:c }}>
        <span style={{ fontSize:19, fontWeight:500, ...gradientStyle }}>Agentic Forge</span>
        <img src="/assets/logo.svg" alt="Quantivex" style={{ height:28, display:'block' }} />
      </div>
    </div>
  );
}

// ════════════════════════════════════════════════════════════════
// APP — 180s timeline
// ════════════════════════════════════════════════════════════════
function App() {
  return (
    <DemoStage duration={180} captions={[]}>
      <Sprite start={0}   end={42} ><Scene1 /></Sprite>
      <Sprite start={42}  end={68} ><Scene2 sceneStart={42} /></Sprite>
      <Sprite start={68}  end={100}><Scene3 sceneStart={68} /></Sprite>
      <Sprite start={100} end={168}><Scene4 sceneStart={100} /></Sprite>
      <Sprite start={168} end={180}><Scene5 sceneStart={168} /></Sprite>
      {/* <Subtitles /> — disabled: no voiceover. Re-enable when audio lands. */}
    </DemoStage>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
