/* ============================================================
   Marketing video — primitives
   Reusable building blocks (bg chrome, brand mark, orbit, tool
   bus, post card, services, CTA, audio cues). All driven by
   useTime() / useSprite() from animations.jsx.
   ============================================================ */

const BRAND = {
  bg:       '#0d0c0b',
  bg1:      '#131210',
  bg2:      '#1a1815',
  line:     'rgba(236, 232, 224, 0.10)',
  line2:    'rgba(236, 232, 224, 0.18)',
  text:     '#ece8e0',
  textDim:  '#a39c90',
  textMute: '#6e685e',
  accent:   '#4f8cff',
  accent2:  '#7fa9ff',
  accentRgb:'79, 140, 255',
  grad:     'linear-gradient(100deg, #4f8cff 0%, #7c8bff 25%, #9b6cff 50%, #b594ff 70%, #4f8cff 100%)',
  fontSans: "'Space Grotesk', system-ui, sans-serif",
  fontMono: "'JetBrains Mono', ui-monospace, monospace",
};

/* ---------- Background chrome (always on) ---------- */
function BgChrome(){
  const t = useTime();
  // very slow parallax drift on the grid
  const gx = (Math.sin(t * 0.18) * 14).toFixed(2);
  const gy = (Math.cos(t * 0.14) * 10).toFixed(2);
  return (
    <>
      <div style={{ position:'absolute', inset:0, background: BRAND.bg }} />
      {/* grid */}
      <div style={{
        position:'absolute', inset:-60,
        backgroundImage:`
          linear-gradient(${BRAND.line} 1px, transparent 1px),
          linear-gradient(90deg, ${BRAND.line} 1px, transparent 1px)`,
        backgroundSize: '96px 96px',
        transform: `translate(${gx}px, ${gy}px)`,
        maskImage:'radial-gradient(circle at 50% 38%, #000 0%, transparent 80%)',
        WebkitMaskImage:'radial-gradient(circle at 50% 38%, #000 0%, transparent 80%)',
        opacity:.45,
        pointerEvents:'none',
      }} />
      {/* accent glows */}
      <div style={{
        position:'absolute', inset:0,
        background:`
          radial-gradient(70% 50% at 72% 8%,  rgba(155,108,255,.18), transparent 60%),
          radial-gradient(60% 50% at 8% 100%, rgba(79,140,255,.14), transparent 65%)`,
        pointerEvents:'none',
      }} />
      {/* vignette */}
      <div style={{
        position:'absolute', inset:0,
        background:'radial-gradient(120% 80% at 50% 50%, transparent 55%, rgba(0,0,0,.45) 100%)',
        pointerEvents:'none',
      }} />
    </>
  );
}

/* ---------- Brand badge (persistent corner mark) ---------- */
function BrandBadge({ x, y }){
  const t = useTime();
  const opacity = clamp(t * 1.4 - 0.3, 0, 1);
  return (
    <div style={{
      position:'absolute', left:x, top:y, opacity,
      display:'flex', alignItems:'center', gap:12,
      fontFamily: BRAND.fontMono, fontSize:18, fontWeight:600,
      letterSpacing:'.04em', color: BRAND.text,
    }}>
      <span style={{
        width:10, height:10, borderRadius:'50%',
        background: BRAND.accent,
        boxShadow:`0 0 12px ${BRAND.accent}`,
      }} />
      maha.naeem<span style={{color: BRAND.textMute}}>()</span>
    </div>
  );
}

/* ---------- Type-on text ---------- */
function TypeOnText({ text, x, y, size=72, color=BRAND.text, font=BRAND.fontMono,
                     weight=500, startDelay=0, charDur=0.04, cursor=true }){
  const { localTime } = useSprite();
  const elapsed = Math.max(0, localTime - startDelay);
  const n = Math.min(text.length, Math.floor(elapsed / charDur));
  const shown = text.slice(0, n);
  const blink = Math.floor(localTime * 2) % 2 === 0;
  return (
    <div style={{
      position:'absolute', left:x, top:y,
      fontFamily:font, fontSize:size, fontWeight:weight, color,
      letterSpacing:'.02em', whiteSpace:'pre',
    }}>
      {shown}
      {cursor && <span style={{
        display:'inline-block', width:'0.55ch', height:'1em',
        marginLeft:6, verticalAlign:'-0.1em',
        background: BRAND.accent,
        opacity: blink ? 1 : 0.25,
        boxShadow:`0 0 8px ${BRAND.accent}`,
      }} />}
    </div>
  );
}

/* ---------- Gradient sweep span (for "AI brain") ---------- */
function GradientText({ children, size, weight=600 }){
  const t = useTime();
  const pos = ((t * 30) % 200).toFixed(1);
  return (
    <span style={{
      background: BRAND.grad,
      backgroundSize:'200% auto',
      backgroundPosition:`${pos}% 50%`,
      WebkitBackgroundClip:'text', backgroundClip:'text',
      color:'transparent',
      fontSize:size, fontWeight:weight,
    }}>{children}</span>
  );
}

/* ---------- Agent loop orbit ---------- */
function AgentOrbit({ x, y, size=560 }){
  const { localTime, duration } = useSprite();
  // active node hops every 1s, with smooth fade
  const labels = ['Observe', 'Reason', 'Act', 'Reflect'];
  const cx = 200, cy = 200, R = 150;
  const positions = labels.map((_, i) => {
    const a = (-Math.PI / 2) + (i * (Math.PI * 2 / labels.length));
    return { x: cx + R * Math.cos(a), y: cy + R * Math.sin(a) };
  });

  // entrance build-in (0..1.2s): ring draws, nodes pop
  const buildT = clamp(localTime / 1.2, 0, 1);
  const ringT = Easing.easeOutCubic(buildT);

  // rotation: very slow, 12s/turn
  const rot = (localTime * 30) % 360;

  // active node logic — starts after build, hops each 1s, 4 hops total
  const cycleStart = 1.2;
  const cycleTime = Math.max(0, localTime - cycleStart);
  const active = Math.min(3, Math.floor(cycleTime));
  const sub = clamp(cycleTime - active, 0, 1);

  // outer circumference (for stroke-dash draw-in)
  const circ = 2 * Math.PI * R;
  const innerCirc = 2 * Math.PI * (R - 38);

  return (
    <div style={{
      position:'absolute', left:x, top:y, width:size, height:size,
    }}>
      <svg viewBox="0 0 400 400" style={{ width:'100%', height:'100%', overflow:'visible' }}>
        <defs>
          <radialGradient id="coreGrad" cx="50%" cy="50%" r="50%">
            <stop offset="0%"  stopColor="rgba(79,140,255,.30)" />
            <stop offset="60%" stopColor="rgba(79,140,255,.04)" />
            <stop offset="100%" stopColor="rgba(0,0,0,0)" />
          </radialGradient>
          <filter id="nodeGlow" x="-50%" y="-50%" width="200%" height="200%">
            <feGaussianBlur stdDeviation="6" />
          </filter>
        </defs>

        {/* core halo */}
        <circle cx={cx} cy={cy} r={120} fill="url(#coreGrad)" opacity={ringT} />

        {/* outer dashed track — draws in */}
        <circle
          cx={cx} cy={cy} r={R}
          fill="none"
          stroke={BRAND.line}
          strokeWidth="1"
          strokeDasharray="2 6"
          transform={`rotate(${rot}, ${cx}, ${cy})`}
          opacity={ringT}
        />
        {/* outer solid ring — draws in via dashoffset */}
        <circle
          cx={cx} cy={cy} r={R}
          fill="none"
          stroke={BRAND.line2}
          strokeWidth="1.5"
          strokeDasharray={circ}
          strokeDashoffset={circ * (1 - ringT)}
          transform={`rotate(-90, ${cx}, ${cy})`}
        />
        {/* inner ring */}
        <circle
          cx={cx} cy={cy} r={R - 38}
          fill="none"
          stroke={BRAND.line}
          strokeWidth="1"
          strokeDasharray={innerCirc}
          strokeDashoffset={innerCirc * (1 - ringT)}
          transform={`rotate(90, ${cx}, ${cy})`}
        />

        {/* core */}
        <circle cx={cx} cy={cy} r={52}
          fill={BRAND.bg1}
          stroke={BRAND.line2}
          strokeWidth="1"
          opacity={ringT}
        />
        <text x={cx} y={cy - 4} textAnchor="middle"
          fontFamily={BRAND.fontMono} fontSize="14"
          fill={BRAND.textDim} letterSpacing=".18em"
          opacity={ringT}>AGENT</text>
        <text x={cx} y={cy + 16} textAnchor="middle"
          fontFamily={BRAND.fontMono} fontSize="14"
          fill={BRAND.textDim} letterSpacing=".18em"
          opacity={ringT}>LOOP</text>

        {/* connector arc from active to next (faint trail) */}
        {cycleTime > 0 && active < 3 && (() => {
          const from = positions[active];
          const to   = positions[Math.min(3, active + 1)];
          const trailT = Easing.easeInOutCubic(sub);
          const tx = from.x + (to.x - from.x) * trailT;
          const ty = from.y + (to.y - from.y) * trailT;
          return (
            <g opacity="0.6">
              <line x1={from.x} y1={from.y} x2={tx} y2={ty}
                stroke={BRAND.accent} strokeWidth="1.5"
                opacity={1 - trailT * 0.7} />
              <circle cx={tx} cy={ty} r={3}
                fill={BRAND.accent2}
                style={{ filter:'drop-shadow(0 0 6px rgba(79,140,255,.9))' }} />
            </g>
          );
        })()}

        {/* nodes */}
        {positions.map((p, i) => {
          // pop in staggered
          const nodeDelay = 0.2 + i * 0.12;
          const nodeT = Easing.easeOutBack(clamp((localTime - nodeDelay) / 0.4, 0, 1));
          if (nodeT <= 0) return null;

          const isActive = i === active;
          const activeFade = isActive
            ? Math.min(1, sub > 0.85 ? (1 - (sub - 0.85) / 0.15) : 1)
            : 0;
          const r = 10 + activeFade * 4;

          return (
            <g key={i} transform={`scale(${nodeT}) translate(${p.x * (1/nodeT) - p.x}, ${p.y * (1/nodeT) - p.y})`}
               style={{ transformOrigin:`${p.x}px ${p.y}px` }}>
              {isActive && (
                <circle cx={p.x} cy={p.y} r={r + 8}
                  fill="rgba(79,140,255,.18)"
                  filter="url(#nodeGlow)" />
              )}
              <circle cx={p.x} cy={p.y} r={r}
                fill={isActive ? BRAND.accent : BRAND.bg1}
                stroke={isActive ? 'none' : BRAND.line2}
                strokeWidth="1"
              />
              {/* node label — positioned outward */}
              {(() => {
                const dx = p.x - cx, dy = p.y - cy;
                const len = Math.hypot(dx, dy) || 1;
                const lx = p.x + (dx / len) * 36;
                const ly = p.y + (dy / len) * 36;
                return (
                  <text x={lx} y={ly + 4} textAnchor="middle"
                    fontFamily={BRAND.fontMono} fontSize="15"
                    fontWeight={isActive ? 600 : 500}
                    fill={isActive ? BRAND.text : BRAND.textDim}
                    letterSpacing=".12em">
                    {labels[i].toUpperCase()}
                  </text>
                );
              })()}
            </g>
          );
        })}
      </svg>
    </div>
  );
}

/* ---------- Tool calling visual ---------- */
function ToolBus({ x, y, width=900, height=520 }){
  const { localTime } = useSprite();
  const tools = [
    { label:'search()', glyph:'⌕' },
    { label:'db.query()', glyph:'≣' },
    { label:'fetch()',  glyph:'↯' },
  ];

  // Beats:
  //   0.0–0.6 LLM appears
  //   0.6–1.0 tool tiles appear
  //   1.2–2.6 outgoing arrows + tools pulse (staggered)
  //   2.6–4.0 return arrows
  //   4.0–end answer chip materializes

  const llmT  = Easing.easeOutBack(clamp((localTime - 0.0) / 0.5, 0, 1));
  const toolsT= clamp((localTime - 0.6) / 0.5, 0, 1);
  const answerT = Easing.easeOutBack(clamp((localTime - 3.6) / 0.7, 0, 1));

  const cx = width / 2;
  const cy = 110;
  const toolsY = 320;
  const toolW = 220, toolH = 96;
  const toolGap = 40;
  const totalW = tools.length * toolW + (tools.length - 1) * toolGap;
  const startX = (width - totalW) / 2;

  const toolPositions = tools.map((_, i) => ({
    x: startX + i * (toolW + toolGap),
    y: toolsY,
    cx: startX + i * (toolW + toolGap) + toolW / 2,
    cy: toolsY,
  }));

  return (
    <div style={{ position:'absolute', left:x, top:y, width, height }}>
      <svg viewBox={`0 0 ${width} ${height}`}
        style={{ position:'absolute', inset:0, width:'100%', height:'100%', overflow:'visible' }}>
        <defs>
          <linearGradient id="busLine" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%"   stopColor="rgba(79,140,255,.0)" />
            <stop offset="50%"  stopColor="rgba(155,108,255,.9)" />
            <stop offset="100%" stopColor="rgba(79,140,255,.0)" />
          </linearGradient>
        </defs>

        {toolPositions.map((p, i) => {
          // outgoing arrow stagger
          const outStart = 1.1 + i * 0.18;
          const outT = Easing.easeInOutCubic(clamp((localTime - outStart) / 0.55, 0, 1));
          const returnStart = 2.5 + i * 0.18;
          const retT = Easing.easeInOutCubic(clamp((localTime - returnStart) / 0.55, 0, 1));

          // outgoing line from LLM down to tool
          const x1 = cx, y1 = cy + 60;
          const x2 = p.cx, y2 = p.y;
          const mx1 = x1 + (x2 - x1) * outT;
          const my1 = y1 + (y2 - y1) * outT;

          // return line from tool up
          const x3 = p.cx, y3 = p.y;
          const x4 = cx, y4 = cy + 60;
          const mx2 = x3 + (x4 - x3) * retT;
          const my2 = y3 + (y4 - y3) * retT;

          return (
            <g key={i}>
              {/* outgoing */}
              <line x1={x1} y1={y1} x2={mx1} y2={my1}
                stroke="rgba(155,108,255,.85)" strokeWidth="1.5"
                opacity={outT > 0 ? 1 : 0} />
              {outT > 0.05 && outT < 1 && (
                <circle cx={mx1} cy={my1} r="4"
                  fill={BRAND.accent2}
                  style={{ filter:'drop-shadow(0 0 8px rgba(155,108,255,.9))' }} />
              )}
              {/* return */}
              <line x1={x3} y1={y3} x2={mx2} y2={my2}
                stroke="rgba(79,140,255,.7)" strokeWidth="1.5"
                strokeDasharray="3 4"
                opacity={retT > 0 ? 1 : 0} />
              {retT > 0.05 && retT < 1 && (
                <circle cx={mx2} cy={my2} r="4"
                  fill={BRAND.accent}
                  style={{ filter:'drop-shadow(0 0 8px rgba(79,140,255,.9))' }} />
              )}
            </g>
          );
        })}
      </svg>

      {/* LLM node (center top) */}
      <div style={{
        position:'absolute',
        left: cx - 90, top: cy - 50,
        width: 180, height: 100,
        borderRadius: 14,
        background:`linear-gradient(180deg, ${BRAND.bg2}, ${BRAND.bg1})`,
        border:`1px solid ${BRAND.line2}`,
        boxShadow:`0 0 40px rgba(${BRAND.accentRgb}, .14)`,
        display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center',
        gap:6,
        transform:`scale(${llmT})`, transformOrigin:'center',
        opacity: llmT,
      }}>
        <div style={{
          fontFamily: BRAND.fontMono, fontSize:13, color: BRAND.textMute,
          letterSpacing:'.2em', textTransform:'uppercase',
        }}>llm</div>
        <div style={{
          fontFamily: BRAND.fontSans, fontSize:28, fontWeight:600,
          background: BRAND.grad, WebkitBackgroundClip:'text', backgroundClip:'text',
          color:'transparent', letterSpacing:'-.01em',
        }}>gpt-4o</div>
      </div>

      {/* Tool tiles */}
      {toolPositions.map((p, i) => {
        const localT = Easing.easeOutBack(clamp((localTime - (0.8 + i * 0.1)) / 0.5, 0, 1));
        // pulse when ping arrives (~outStart + 0.55)
        const pingTime = 1.65 + i * 0.18;
        const pulse = Math.max(0, 1 - Math.abs(localTime - pingTime) / 0.4);
        return (
          <div key={i} style={{
            position:'absolute',
            left: p.x, top: p.y,
            width: toolW, height: toolH,
            borderRadius: 14,
            background: BRAND.bg1,
            border:`1px solid ${BRAND.line2}`,
            boxShadow: `0 0 ${20 + pulse * 40}px rgba(${BRAND.accentRgb}, ${0.05 + pulse * 0.35})`,
            transform:`scale(${localT})`, transformOrigin:'center top',
            opacity: localT,
            display:'flex', alignItems:'center', justifyContent:'flex-start',
            gap: 16, padding:'0 20px',
          }}>
            <div style={{
              width: 44, height: 44, borderRadius: 10,
              background:`linear-gradient(135deg, ${BRAND.bg2}, ${BRAND.bg})`,
              border:`1px solid ${BRAND.line2}`,
              display:'flex', alignItems:'center', justifyContent:'center',
              fontFamily: BRAND.fontMono, fontSize:22,
              color: pulse > 0.3 ? BRAND.accent2 : BRAND.textDim,
              transition:'color .2s',
            }}>{tools[i].glyph}</div>
            <div>
              <div style={{
                fontFamily: BRAND.fontMono, fontSize:11, color: BRAND.textMute,
                letterSpacing:'.2em', textTransform:'uppercase', marginBottom:4,
              }}>tool</div>
              <div style={{
                fontFamily: BRAND.fontMono, fontSize:18, fontWeight:500, color: BRAND.text,
              }}>{tools[i].label}</div>
            </div>
          </div>
        );
      })}

      {/* Answer chip */}
      <div style={{
        position:'absolute',
        left: cx - 160, top: 450,
        width: 320, height: 56, borderRadius: 999,
        background:`linear-gradient(90deg, rgba(79,140,255,.18), rgba(155,108,255,.18))`,
        border:`1px solid ${BRAND.line2}`,
        display:'flex', alignItems:'center', justifyContent:'center', gap:14,
        fontFamily: BRAND.fontMono, fontSize:18, color: BRAND.text,
        transform:`scale(${answerT})`, transformOrigin:'center',
        opacity: answerT,
        boxShadow:`0 12px 40px rgba(155,108,255,.18)`,
      }}>
        <span style={{
          width:8, height:8, borderRadius:'50%',
          background: BRAND.accent2, boxShadow:`0 0 10px ${BRAND.accent2}`,
        }} />
        answer ready
      </div>
    </div>
  );
}

/* ---------- Featured post card ---------- */
function PostCard({ x, y, width=820, height=520 }){
  const { localTime } = useSprite();
  const t = Easing.easeOutCubic(clamp(localTime / 0.8, 0, 1));
  // gentle parallax on the inner content
  const drift = Math.sin(localTime * 0.6) * 6;
  return (
    <div style={{
      position:'absolute', left:x, top:y, width, height,
      borderRadius: 24,
      background:`linear-gradient(180deg, ${BRAND.bg2}, ${BRAND.bg1})`,
      border:`1px solid ${BRAND.line2}`,
      boxShadow:'0 30px 80px rgba(0,0,0,.5), 0 0 60px rgba(79,140,255,.08)',
      transform:`translateY(${(1 - t) * 30}px) scale(${0.96 + 0.04 * t})`,
      opacity: t,
      overflow:'hidden',
      padding: 56,
      display:'flex', flexDirection:'column', justifyContent:'space-between',
    }}>
      {/* corner glow */}
      <div style={{
        position:'absolute', top:-100, right:-80,
        width:380, height:380, borderRadius:'50%',
        background:'radial-gradient(circle, rgba(155,108,255,.25), transparent 60%)',
        pointerEvents:'none',
      }} />

      <div style={{ display:'flex', alignItems:'center', gap:14 }}>
        <span style={{
          display:'inline-flex', alignItems:'center', gap:10,
          fontFamily: BRAND.fontMono, fontSize:13, fontWeight:500,
          letterSpacing:'.18em', textTransform:'uppercase',
          color: BRAND.textDim,
          padding:'8px 14px', borderRadius:999,
          border:`1px solid ${BRAND.line2}`, background:'rgba(255,255,255,.02)',
        }}>
          <span style={{
            width:6, height:6, borderRadius:'50%',
            background: BRAND.accent, boxShadow:`0 0 8px ${BRAND.accent}`,
          }} />
          Featured
          <span style={{ color: BRAND.textMute }}>/</span>
          <span style={{
            background: BRAND.grad, WebkitBackgroundClip:'text', backgroundClip:'text',
            color:'transparent', fontWeight:600,
          }}>AI</span>
        </span>
        <span style={{
          fontFamily: BRAND.fontMono, fontSize:13, color: BRAND.textMute,
          letterSpacing:'.04em',
        }}>08 Jun 2026 · 12 min read</span>
      </div>

      <div style={{ transform:`translateY(${drift}px)` }}>
        <h2 style={{
          fontFamily: BRAND.fontSans, fontSize:64, fontWeight:600,
          letterSpacing:'-.02em', lineHeight:1.04, color: BRAND.text,
          margin:0, marginBottom:24, maxWidth:680, textWrap:'balance',
        }}>
          How to build an{' '}
          <span style={{
            background: BRAND.grad, backgroundSize:'200% auto',
            backgroundPosition: `${((useTime() * 30) % 200).toFixed(0)}% 50%`,
            WebkitBackgroundClip:'text', backgroundClip:'text', color:'transparent',
          }}>AI agent</span>{' '}
          with LangChain &amp; OpenAI.
        </h2>
        <p style={{
          fontFamily: BRAND.fontMono, fontSize:18, lineHeight:1.6,
          color: BRAND.textDim, margin:0, maxWidth:620,
        }}>
          An LLM in a loop with tools, working toward a goal — built end‑to‑end in TypeScript.
        </p>
      </div>

      <div style={{
        display:'flex', alignItems:'center', justifyContent:'space-between',
        paddingTop: 20, borderTop:`1px solid ${BRAND.line}`,
      }}>
        <span style={{
          fontFamily: BRAND.fontMono, fontSize:14, color: BRAND.textMute,
          letterSpacing:'.04em',
        }}>mahanaeem.com / writing</span>
        <span style={{
          fontFamily: BRAND.fontMono, fontSize:15, color: BRAND.text,
          display:'inline-flex', alignItems:'center', gap:10,
        }}>
          Read the guide <span style={{ color: BRAND.accent2 }}>→</span>
        </span>
      </div>
    </div>
  );
}

/* ---------- Services list ---------- */
function ServicesList({ x, y, width=800 }){
  const { localTime } = useSprite();
  const rows = [
    { idx:'01', label:'Build with me',  hint:'AI features, agents, RAG' },
    { idx:'02', label:'Talk to me',     hint:'Architecture & code review' },
    { idx:'03', label:'Read my writing', hint:'Field notes on shipping AI' },
  ];
  return (
    <div style={{ position:'absolute', left:x, top:y, width }}>
      {rows.map((r, i) => {
        const delay = i * 0.35;
        const t = Easing.easeOutCubic(clamp((localTime - delay) / 0.6, 0, 1));
        return (
          <div key={i} style={{
            display:'grid',
            gridTemplateColumns:'80px 1fr auto',
            alignItems:'center',
            gap: 36,
            padding:'30px 8px',
            borderBottom:`1px solid ${BRAND.line}`,
            transform:`translateX(${(1 - t) * 40}px)`,
            opacity: t,
          }}>
            <div style={{
              fontFamily: BRAND.fontMono, fontSize:22, fontWeight:500,
              color: BRAND.accent2, letterSpacing:'.04em',
            }}>{r.idx}</div>
            <div>
              <div style={{
                fontFamily: BRAND.fontSans, fontSize:44, fontWeight:600,
                letterSpacing:'-.01em', color: BRAND.text, lineHeight:1.1,
              }}>{r.label}</div>
              <div style={{
                fontFamily: BRAND.fontMono, fontSize:16, color: BRAND.textMute,
                marginTop:8, letterSpacing:'.04em',
              }}>{r.hint}</div>
            </div>
            <div style={{
              width:56, height:56, borderRadius:14,
              border:`1px solid ${BRAND.line2}`,
              display:'flex', alignItems:'center', justifyContent:'center',
              fontFamily: BRAND.fontMono, fontSize:22, color: BRAND.text,
            }}>→</div>
          </div>
        );
      })}
    </div>
  );
}

/* ---------- Final CTA ---------- */
function FinalCTA({ x, y, width=1400 }){
  const { localTime } = useSprite();
  const titleT = Easing.easeOutCubic(clamp(localTime / 0.8, 0, 1));
  const urlT   = Easing.easeOutCubic(clamp((localTime - 0.6) / 0.7, 0, 1));
  return (
    <div style={{
      position:'absolute', left:x, top:y, width,
      textAlign:'center',
    }}>
      <div style={{
        fontFamily: BRAND.fontMono, fontSize:18, fontWeight:500,
        letterSpacing:'.3em', textTransform:'uppercase',
        color: BRAND.textMute,
        opacity: titleT,
        transform:`translateY(${(1 - titleT) * 12}px)`,
        marginBottom: 36,
      }}>
        <span style={{ color: BRAND.accent }}>●</span>&nbsp;&nbsp;Web engineering with an AI brain
      </div>
      <h1 style={{
        fontFamily: BRAND.fontSans, fontSize:160, fontWeight:600,
        letterSpacing:'-.03em', lineHeight:1, margin:0,
        opacity: titleT,
        transform:`translateY(${(1 - titleT) * 20}px) scale(${0.97 + 0.03 * titleT})`,
      }}>
        <span style={{
          background: BRAND.grad, backgroundSize:'200% auto',
          backgroundPosition:`${((useTime() * 24) % 200).toFixed(0)}% 50%`,
          WebkitBackgroundClip:'text', backgroundClip:'text', color:'transparent',
        }}>mahanaeem.com</span>
      </h1>
      <div style={{
        marginTop: 56,
        display:'inline-flex', alignItems:'center', gap:24,
        padding:'22px 36px',
        border:`1px solid ${BRAND.line2}`,
        borderRadius: 18,
        background:'rgba(255,255,255,.03)',
        backdropFilter:'blur(8px)',
        opacity: urlT,
        transform:`translateY(${(1 - urlT) * 14}px)`,
        fontFamily: BRAND.fontMono, fontSize:22, color: BRAND.text,
        letterSpacing:'.04em',
      }}>
        <span>Build, ship, write</span>
        <span style={{ width:1, height:22, background: BRAND.line2 }} />
        <span style={{ color: BRAND.accent2 }}>hire me →</span>
      </div>
    </div>
  );
}

/* ---------- Scene wrapper with crossfade ---------- */
function Scene({ start, end, fadeIn=0.5, fadeOut=0.5, children }){
  const t = useTime();
  if (t < start - fadeIn || t > end + fadeOut) return null;
  let opacity = 1;
  if (t < start) opacity = clamp((t - (start - fadeIn)) / fadeIn, 0, 1);
  else if (t > end) opacity = 1 - clamp((t - end) / fadeOut, 0, 1);

  // slow parallax on the scene
  const localT = clamp((t - start) / Math.max(0.01, end - start), 0, 1);
  const scale = 1 + localT * 0.02;

  return (
    <Sprite start={start - fadeIn} end={end + fadeOut} keepMounted={false}>
      <div style={{
        position:'absolute', inset:0,
        opacity,
        transform:`scale(${scale})`,
        transformOrigin:'center',
        willChange:'opacity, transform',
      }}>
        {children}
      </div>
    </Sprite>
  );
}

/* ---------- Audio cue board ---------- */
function AudioCues({ enabled, cues }){
  const { time, playing } = useTimeline();
  const ctxRef = React.useRef(null);
  const prevRef = React.useRef(0);

  React.useEffect(() => {
    if (!enabled) return;
    if (!ctxRef.current) {
      try {
        ctxRef.current = new (window.AudioContext || window.webkitAudioContext)();
      } catch (e) {}
    }
    if (ctxRef.current && ctxRef.current.state === 'suspended') {
      ctxRef.current.resume().catch(() => {});
    }
  }, [enabled, playing]);

  React.useEffect(() => {
    if (!enabled || !ctxRef.current || !playing) {
      prevRef.current = time;
      return;
    }
    const ctx = ctxRef.current;
    const prev = prevRef.current;
    if (time < prev) { prevRef.current = time; return; } // scrub backwards
    cues.forEach(c => {
      if (c.time > prev && c.time <= time) {
        playCue(ctx, c.type);
      }
    });
    prevRef.current = time;
  }, [time, enabled, playing, cues]);

  return null;
}

function playCue(ctx, type){
  const now = ctx.currentTime;
  if (type === 'click') {
    // short keystroke: filtered noise burst
    const buf = ctx.createBuffer(1, ctx.sampleRate * 0.04, ctx.sampleRate);
    const data = buf.getChannelData(0);
    for (let i = 0; i < data.length; i++) data[i] = (Math.random() * 2 - 1) * 0.6;
    const src = ctx.createBufferSource();
    src.buffer = buf;
    const bp = ctx.createBiquadFilter();
    bp.type = 'bandpass'; bp.frequency.value = 3200; bp.Q.value = 1.5;
    const g = ctx.createGain();
    g.gain.setValueAtTime(0.0001, now);
    g.gain.exponentialRampToValueAtTime(0.18, now + 0.005);
    g.gain.exponentialRampToValueAtTime(0.0001, now + 0.05);
    src.connect(bp).connect(g).connect(ctx.destination);
    src.start(now); src.stop(now + 0.06);
  } else if (type === 'pop') {
    // soft sine pop
    const osc = ctx.createOscillator();
    osc.type = 'sine';
    osc.frequency.setValueAtTime(880, now);
    osc.frequency.exponentialRampToValueAtTime(520, now + 0.12);
    const g = ctx.createGain();
    g.gain.setValueAtTime(0.0001, now);
    g.gain.exponentialRampToValueAtTime(0.12, now + 0.012);
    g.gain.exponentialRampToValueAtTime(0.0001, now + 0.18);
    osc.connect(g).connect(ctx.destination);
    osc.start(now); osc.stop(now + 0.2);
  } else if (type === 'tick') {
    // higher tick
    const osc = ctx.createOscillator();
    osc.type = 'triangle';
    osc.frequency.setValueAtTime(1400, now);
    const g = ctx.createGain();
    g.gain.setValueAtTime(0.0001, now);
    g.gain.exponentialRampToValueAtTime(0.06, now + 0.005);
    g.gain.exponentialRampToValueAtTime(0.0001, now + 0.06);
    osc.connect(g).connect(ctx.destination);
    osc.start(now); osc.stop(now + 0.08);
  } else if (type === 'whoosh') {
    // soft swell
    const osc = ctx.createOscillator();
    osc.type = 'sine';
    osc.frequency.setValueAtTime(220, now);
    osc.frequency.linearRampToValueAtTime(440, now + 0.45);
    const g = ctx.createGain();
    g.gain.setValueAtTime(0.0001, now);
    g.gain.linearRampToValueAtTime(0.12, now + 0.12);
    g.gain.exponentialRampToValueAtTime(0.0001, now + 0.55);
    osc.connect(g).connect(ctx.destination);
    osc.start(now); osc.stop(now + 0.6);
  }
}

/* ============================================================
   Music engine — ambient procedural pad
   Sub bass drone + 3-voice detuned pad with slow LFO filter,
   chord progression (Am → F → C → Am over 28s) + sparse bell
   accents on scene starts. All routed through a synthetic
   convolution reverb. Royalty-free by construction.
   ============================================================ */
function buildReverbIR(ctx, durationSec = 3.2, decay = 2.6){
  const length = ctx.sampleRate * durationSec;
  const buf = ctx.createBuffer(2, length, ctx.sampleRate);
  for (let ch = 0; ch < 2; ch++){
    const data = buf.getChannelData(ch);
    for (let i = 0; i < length; i++){
      data[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / length, decay);
    }
  }
  return buf;
}

const MIDI = (m) => 440 * Math.pow(2, (m - 69) / 12);

function startMusic(ctx, state, startOffsetSec){
  state.active = true;
  const now = ctx.currentTime;
  const FULL = 36;
  // remaining seconds of composition to play
  const rem = Math.max(2, FULL - startOffsetSec);

  // Master + reverb bus
  const master = ctx.createGain();
  master.gain.value = 0;
  master.gain.linearRampToValueAtTime(0.55, now + 1.6); // gentle fade-in

  const convolver = ctx.createConvolver();
  convolver.buffer = buildReverbIR(ctx, 3.2, 2.6);
  const wet = ctx.createGain(); wet.gain.value = 0.42;
  const dry = ctx.createGain(); dry.gain.value = 0.78;

  master.connect(dry).connect(ctx.destination);
  master.connect(convolver).connect(wet).connect(ctx.destination);

  state.master = master;
  state.voices = [];

  // Chord progression — 6 chords × 6s = 36s
  // Am  F  C  G  F  Am — loose, drifting
  const chordDur = 6.0;
  const bassMidi = [45, 41, 48, 43, 41, 45];  // A2, F2, C3, G2, F2, A2
  const padMidi  = [
    [57, 60, 64],   // Am: A3 C4 E4
    [53, 57, 60],   // F:  F3 A3 C4
    [48, 52, 55],   // C:  C3 E3 G3
    [55, 59, 62],   // G:  G3 B3 D4
    [53, 57, 60],   // F:  F3 A3 C4
    [57, 60, 64],   // Am
  ];

  // helper to convert composition-time to ctx-time (skipping past whatever already elapsed)
  const at = (compT) => now + Math.max(0, compT - startOffsetSec);

  /* --- Sub bass drone --- */
  const bass = ctx.createOscillator();
  bass.type = 'sine';
  bass.frequency.value = MIDI(bassMidi[Math.min(bassMidi.length - 1, Math.floor(startOffsetSec / chordDur))]);
  bassMidi.forEach((m, i) => {
    const t = at(i * chordDur);
    if (t >= now) bass.frequency.setValueAtTime(MIDI(m), t);
  });
  const bassFilter = ctx.createBiquadFilter();
  bassFilter.type = 'lowpass'; bassFilter.frequency.value = 220; bassFilter.Q.value = 0.7;
  const bassGain = ctx.createGain(); bassGain.gain.value = 0.22;
  bass.connect(bassFilter).connect(bassGain).connect(master);
  bass.start(now);
  bass.stop(now + rem + 1.5);
  state.voices.push(bass);

  /* --- Pad: 3 detuned voices --- */
  for (let v = 0; v < 3; v++){
    const osc = ctx.createOscillator();
    osc.type = v === 0 ? 'sine' : 'triangle';
    osc.detune.value = (v - 1) * 8;

    const filter = ctx.createBiquadFilter();
    filter.type = 'lowpass';
    filter.frequency.value = 850;
    filter.Q.value = 1.6;

    // slow LFO on filter cutoff
    const lfo = ctx.createOscillator();
    lfo.type = 'sine';
    lfo.frequency.value = 0.07 + v * 0.04;
    const lfoAmp = ctx.createGain();
    lfoAmp.gain.value = 380;
    lfo.connect(lfoAmp).connect(filter.frequency);
    lfo.start(now);
    lfo.stop(now + rem + 1.5);

    const voiceGain = ctx.createGain();
    voiceGain.gain.value = 0.085;

    // initial frequency based on current chord
    const startChord = Math.min(padMidi.length - 1, Math.floor(startOffsetSec / chordDur));
    osc.frequency.value = MIDI(padMidi[startChord][v]);

    padMidi.forEach((chord, i) => {
      const t = at(i * chordDur);
      if (t >= now) {
        // soft glide between chords
        osc.frequency.setValueAtTime(osc.frequency.value, Math.max(now, t - 0.4));
        osc.frequency.linearRampToValueAtTime(MIDI(chord[v]), t + 0.3);
      }
    });

    osc.connect(filter).connect(voiceGain).connect(master);
    osc.start(now);
    osc.stop(now + rem + 1.5);

    state.voices.push(osc, lfo);
  }

  /* --- Bell accents on scene starts --- */
  const bells = [
    { t:  2.7, m: 76 }, // pitch reveal
    { t:  5.5, m: 79 }, // 01 BUILD MERN
    { t: 11.0, m: 81 }, // 02 PYTHON AI
    { t: 16.0, m: 83 }, // 03 AGENTS
    { t: 20.5, m: 78 }, // 04 INTEGRATE
    { t: 25.0, m: 72 }, // 05 DEPLOY
    { t: 29.6, m: 79 }, // LIVE!
    { t: 30.0, m: 81 }, // 06 WRITING
    { t: 34.0, m: 76 }, // CTA
  ];
  bells.forEach(b => {
    if (b.t < startOffsetSec) return;
    const tStart = at(b.t);
    const bell = ctx.createOscillator();
    bell.type = 'sine';
    bell.frequency.value = MIDI(b.m);
    const bg = ctx.createGain();
    bg.gain.setValueAtTime(0.0001, tStart);
    bg.gain.exponentialRampToValueAtTime(0.10, tStart + 0.02);
    bg.gain.exponentialRampToValueAtTime(0.0001, tStart + 2.6);
    bell.connect(bg).connect(master);
    bell.start(tStart);
    bell.stop(tStart + 2.8);
    state.voices.push(bell);
  });

  /* --- Outro fade --- */
  const fadeStart = at(36.0);
  const fadeEnd   = at(38.2);
  if (fadeEnd > now) {
    master.gain.setValueAtTime(0.55, Math.max(now, fadeStart));
    master.gain.linearRampToValueAtTime(0.0, fadeEnd);
  }
}

function stopMusic(state){
  if (!state.active || !state.master) return;
  state.active = false;
  try {
    const ctx = state.master.context;
    const now = ctx.currentTime;
    state.master.gain.cancelScheduledValues(now);
    state.master.gain.setValueAtTime(state.master.gain.value, now);
    state.master.gain.linearRampToValueAtTime(0, now + 0.35);
  } catch (e) {}
  const voices = state.voices;
  state.voices = [];
  state.master = null;
  setTimeout(() => {
    voices.forEach(v => { try { v.stop(); } catch (e) {} });
  }, 400);
}

function MusicEngine({ enabled }){
  const { time, playing } = useTimeline();
  const ctxRef   = React.useRef(null);
  const stateRef = React.useRef({ active: false, voices: [], master: null });
  const prevTimeRef = React.useRef(time);
  const [restartKey, setRestartKey] = React.useState(0);

  // Lazy AudioContext (created on first enable)
  React.useEffect(() => {
    if (!enabled) return;
    if (!ctxRef.current) {
      try {
        ctxRef.current = new (window.AudioContext || window.webkitAudioContext)();
      } catch (e) { return; }
    }
    if (ctxRef.current.state === 'suspended') {
      ctxRef.current.resume().catch(() => {});
    }
  }, [enabled, playing]);

  // Detect significant time jumps -> stop + bump restartKey to re-arm
  React.useEffect(() => {
    const prev = prevTimeRef.current;
    if (Math.abs(time - prev) > 1.2 && stateRef.current.active) {
      stopMusic(stateRef.current);
      setRestartKey(k => k + 1);
    }
    prevTimeRef.current = time;
  }, [time]);

  // Start / stop music based on enabled + playing + restartKey
  React.useEffect(() => {
    const state = stateRef.current;
    if (!enabled || !playing) {
      stopMusic(state);
      return;
    }
    const ctx = ctxRef.current;
    if (!ctx) return;
    if (state.active) return;
    // Start from current playhead
    startMusic(ctx, state, prevTimeRef.current);
    return () => stopMusic(state);
  }, [enabled, playing, restartKey]);

  return null;
}

/* ---------- Timecode badge (for commenting) ---------- */
function TimecodeBadge(){
  const { time: t, playing } = useTimeline();
  const [, setTick] = React.useState(0);
  React.useEffect(() => {
    if (!playing) return undefined;
    const id = setInterval(() => setTick(x => x + 1), 1000);
    return () => clearInterval(id);
  }, [playing]);
  // update data-screen-label on root each second
  React.useEffect(() => {
    const root = document.querySelector('[data-video-root]');
    if (root) root.setAttribute('data-screen-label', `t=${t.toFixed(1)}s`);
  }, [t]);
  return null;
}

/* expose */
Object.assign(window, {
  BRAND, BgChrome, BrandBadge, TypeOnText, GradientText,
  AgentOrbit, ToolBus, PostCard, ServicesList, FinalCTA,
  Scene, AudioCues, MusicEngine, TimecodeBadge,
});
