// Narration rail — shows what the transformer is "thinking" at each stage,
// with the real-life analogy that matches the current step.
function Narration({ stage, tokens, activeIdx, nextToken, speed }) {
  const cur = tokens[activeIdx]?.text?.replace(/ /g, '␣') ?? '—';

  const steps = [
    {
      title: 'Reading the prompt',
      body: `Splitting your text into sub-word pieces. "${cur}" gets a numeric id like a library call number — the model only speaks in numbers.`,
      hint: 'text → ids',
    },
    {
      title: 'Looking up the fingerprint',
      body: `Each id grabs one row from a giant lookup table — a "personality sheet" of ~4096 numbers that encodes what this token means.`,
      hint: 'id → vector',
    },
    {
      title: 'Asking every past token for context',
      body: `The current token sends a question (Q) to every previous token's advertisement (K), then mixes their contents (V) weighted by how well they match. That's attention.`,
      hint: 'Q · Kᵀ → softmax → · V',
    },
    {
      title: 'Thinking on it',
      body: `The vector is expanded to 4× its width, passed through a nonlinearity, then squeezed back. Stack this 32+ times — that's most of the model's parameters.`,
      hint: 'MLP × N',
    },
    {
      title: 'Guessing what comes next',
      body: `A final projection produces one score per vocab entry. Temperature warps the distribution — lower = safer, higher = weirder. A random draw picks the winner.`,
      hint: 'logits → softmax → sample',
    },
  ];

  const s = steps[clamp(stage, 0, steps.length - 1)];

  return (
    <div style={{
      border: '1px solid var(--line-strong)', borderRadius: 8, padding: '16px 18px',
      background: 'linear-gradient(180deg, rgba(140,255,160,0.03), rgba(140,255,160,0.005))',
      display: 'flex', flexDirection: 'column', gap: 10,
      minHeight: 0,
    }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
        <div style={{ fontFamily: 'var(--mono)', fontSize: 10, color: 'var(--ink)', letterSpacing: '0.14em', textShadow: '0 0 8px var(--ink)' }}>
          /// NARRATION
        </div>
        <div style={{ fontFamily: 'var(--mono)', fontSize: 10, color: 'var(--text-faint)' }}>{s.hint}</div>
      </div>
      <div style={{ fontFamily: 'var(--sans)', fontSize: 22, fontWeight: 500, lineHeight: 1.15, color: 'var(--text)', textWrap: 'pretty' }}>
        {s.title}
      </div>
      <div style={{ fontFamily: 'var(--sans)', fontSize: 13, lineHeight: 1.55, color: 'var(--text-dim)', textWrap: 'pretty' }}>
        {s.body}
      </div>
      <div style={{ flex: 1 }} />
      <div style={{
        borderTop: '1px dashed var(--line-strong)', paddingTop: 10,
        display: 'flex', flexDirection: 'column', gap: 6,
      }}>
        <div style={{ fontFamily: 'var(--mono)', fontSize: 9, color: 'var(--text-faint)', letterSpacing: '0.1em' }}>GENERATED SO FAR</div>
        <div style={{
          fontFamily: 'var(--mono)', fontSize: 13, color: 'var(--ink)',
          textShadow: '0 0 6px var(--ink)', lineHeight: 1.5,
          wordBreak: 'break-word', minHeight: 40,
        }}>
          {tokens.length === 0 && !nextToken && (
            <span style={{ color: 'var(--text-faint)', textShadow: 'none', fontStyle: 'italic' }}>
              waiting for the model to have an opinion…
            </span>
          )}
          {tokens.map(t => t.text).join('')}
          <span style={{ color: 'var(--magenta)', textShadow: '0 0 8px var(--magenta)' }}>{nextToken || ''}</span>
          <span className="blink" style={{
            display: 'inline-block', width: 6, height: 14,
            background: 'var(--ink)', marginLeft: 2, verticalAlign: 'middle',
            boxShadow: '0 0 6px var(--ink)',
          }} />
        </div>
      </div>
      <style>{`
        .blink { animation: blink 1s steps(2, start) infinite; }
        @keyframes blink { to { visibility: hidden; } }
      `}</style>
    </div>
  );
}
window.Narration = Narration;
