// Stage 02 — embedding lookup.
// Shows a stylized vocab matrix (rows = token ids) with the active row lit up,
// then the pulled-out row as a vector of numbers streaming to the right.
function StageEmbed({ tokens, activeIdx, progress, active, onExplain }) {
  const rows = 14;       // visible slice of the 50k vocab
  const cols = 32;       // visible slice of d_model
  const activeId = tokens[activeIdx]?.id ?? 0;
  const activeRowSlot = activeId % rows;

  // Deterministic embedding matrix slice based on activeId
  const rand = React.useMemo(() => seededRand(activeId * 131 + 7), [activeId]);
  const matrix = React.useMemo(() => {
    const r = seededRand(activeId * 131 + 7);
    return Array.from({ length: rows }, () => Array.from({ length: cols }, () => (r() - 0.5) * 2));
  }, [activeId]);

  const vector = matrix[activeRowSlot] || [];

  // reveal fraction for the extracted vector (how far it has streamed out)
  const reveal = active ? clamp(progress * 1.1, 0, 1) : 0;

  // Mouse-tracked parallax on the vocab slab. Same pattern as Stage 03 heads:
  // rAF-throttled mousemove inside the slab container sets parallax (-1..1
  // per axis), which modulates the base rotateX(14deg) and adds a rotateY.
  // Magnitude kept small (~4-5 deg) so the slab feels responsive without
  // inducing motion sickness.
  const slabRef = React.useRef(null);
  const [parallax, setParallax] = React.useState({ x: 0, y: 0 });
  React.useEffect(() => {
    const el = slabRef.current; if (!el) return;
    let raf = 0;
    let pending = { x: 0, y: 0 };
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      pending = {
        x: ((e.clientX - r.left) / r.width - 0.5) * 2,
        y: ((e.clientY - r.top) / r.height - 0.5) * 2,
      };
      if (!raf) raf = requestAnimationFrame(() => { raf = 0; setParallax(pending); });
    };
    const onLeave = () => setParallax({ x: 0, y: 0 });
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
    return () => {
      el.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);
  // Base rotateX = 14deg. Parallax.y pulls the top toward the camera when
  // the cursor is near the top (negative y), pushes it back when near the
  // bottom. Parallax.x tilts left/right.
  const slabRotateX = 14 - parallax.y * 7;
  const slabRotateY = parallax.x * 9;

  return (
    <Panel stage={2} title="Embed + Position" subtitle="lookup row from 50000 × 4096 matrix, add position" active={active} progress={progress} onExplain={onExplain}>
      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) auto minmax(0, 1fr)', gap: 14, alignItems: 'center' }}>
        {/* The vocab matrix — rendered as a shallow 3D slab so the viewer
            feels the scale (50000 rows) of the real matrix, not just the
            14-row slice we're drawing. Perspective on the outer container,
            rotateX on the matrix grid, mouse-tracked tilt. */}
        <div ref={slabRef} style={{
          border: '1px solid var(--line)', borderRadius: 4, padding: '6px 8px',
          background: 'rgba(0,0,0,0.25)', position: 'relative',
          perspective: '1400px',
          isolation: 'isolate',              // prevents the active-row border from bleeding
                                              // through stacking contexts on Firefox
        }}>
          <div style={{ fontFamily: 'var(--mono)', fontSize: 9, color: 'var(--text-faint)', marginBottom: 4, display: 'flex', justifyContent: 'space-between' }}>
            <span>vocab_embedding · [50000 × 4096]</span>
            <span>↓ row #{String(activeId).padStart(5, '0')}</span>
          </div>
          <div style={{
            display: 'grid', gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`, gap: '2px 6px',
            // 3D tilt, now tracking mouse position inside the slab.
            // backface-visibility + translateZ(0) fix Safari subpixel text
            // rendering on rotated elements.
            transform: `translateZ(0) rotateX(${slabRotateX}deg) rotateY(${slabRotateY}deg)`,
            transformOrigin: 'center bottom',
            backfaceVisibility: 'hidden',
            transformStyle: 'preserve-3d',
            position: 'relative',
            // Springy return to rest when the cursor leaves.
            transition: 'transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1)',
          }}>
            {matrix.map((row, ri) => row.map((v, ci) => {
              const isActiveRow = active && ri === activeRowSlot;
              // Depth fog: top rows fade into the bg. Distance from the
              // "bottom" row (ri = rows-1). Only applies to non-active rows.
              const rowDepth = (rows - 1 - ri) / (rows - 1);
              const fogMul = isActiveRow ? 1 : (1 - rowDepth * 0.6);
              return (
                <span key={`${ri}-${ci}`} style={{
                  fontFamily: 'var(--mono)', fontSize: 9,
                  color: isActiveRow ? 'var(--amber)' : 'var(--ink)',
                  opacity: isActiveRow ? 1 : (0.18 + Math.abs(v) * 0.15) * fogMul,
                  textShadow: isActiveRow ? '0 0 6px var(--amber)' : 'none',
                  textAlign: 'right', transition: 'color 250ms, opacity 250ms',
                  fontVariantNumeric: 'tabular-nums',
                }}>{fmt(v, 1)}</span>
              );
            }))}
            {/* active row highlight bar — nested inside the transformed
                grid so it tilts with the matrix rather than floating flat */}
            {active && (
              <div style={{
                position: 'absolute', left: -4, right: -4,
                top: `calc(${activeRowSlot * 13}px - 2px)`,
                height: 13,
                border: '1px solid var(--amber)',
                borderRadius: 2,
                pointerEvents: 'none',
                boxShadow: '0 0 12px color-mix(in oklch, var(--amber) 40%, transparent), inset 0 0 12px color-mix(in oklch, var(--amber) 10%, transparent)',
                opacity: clamp(progress * 3, 0, 1),
                transform: 'translateZ(4px)',  // lift slightly so it reads as "on top" of the receding rows
              }} />
            )}
          </div>
          {/* Phosphor-green depth fog: fades the top rows into the near-bg.
              Matches var(--bg) / var(--bg-2) so rows dissolve into the void. */}
          <div style={{
            position: 'absolute', left: 8, right: 8, top: 20,
            height: '60%',
            background: 'linear-gradient(180deg, rgba(10,13,10,0.55) 0%, rgba(10,13,10,0) 75%)',
            pointerEvents: 'none',
          }} />
        </div>

        {/* arrow */}
        <div style={{ fontFamily: 'var(--mono)', color: active ? 'var(--amber)' : 'var(--text-faint)', fontSize: 18, textShadow: active ? '0 0 8px var(--amber)' : 'none' }}>→</div>

        {/* Extracted vector, streaming in */}
        <div style={{
          border: '1px solid var(--line)', borderRadius: 4, padding: '6px 8px',
          background: 'rgba(0,0,0,0.25)', minHeight: 0,
        }}>
          <div style={{ fontFamily: 'var(--mono)', fontSize: 9, color: 'var(--text-faint)', marginBottom: 4, display: 'flex', justifyContent: 'space-between' }}>
            <span>embedding vector · [4096]</span>
            <span>"{tokens[activeIdx]?.text.replace(/ /g, '␣') || '—'}"</span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: `repeat(16, minmax(0, 1fr))`, gap: '2px 4px' }}>
            {Array.from({ length: 16 * 7 }).map((_, i) => {
              const v = vector[i % vector.length] ?? 0;
              const appeared = reveal * 16 * 7 > i;
              return (
                <span key={i} style={{
                  fontFamily: 'var(--mono)', fontSize: 9,
                  color: 'var(--ink)',
                  opacity: appeared ? (0.4 + Math.abs(v) * 0.5) : 0,
                  textShadow: appeared ? '0 0 4px var(--ink)' : 'none',
                  textAlign: 'right', transition: 'opacity 120ms',
                  fontVariantNumeric: 'tabular-nums',
                }}>{fmt(v, 1)}</span>
              );
            })}
          </div>
        </div>
      </div>
    </Panel>
  );
}
window.StageEmbed = StageEmbed;
