// Constellation X — Cinematic.
// A phrase materializes word-by-word in the center of the graph; each word
// becomes a node and the constellation grows around it. Brand-statement.

const HeroConstellationX = ({ pressed, t }) => {
  const open = Math.min(1, t);
  // "Fully lit" — last word has materialized (t0 = 0.34, lit at +~0.16)
  const fullyLit = open > 0.92;

  // Broadcast page-wide state via a CSS attribute on <html>. Other components
  // (CSS, ScrollRail, etc) can react.
  React.useEffect(() => {
    document.documentElement.dataset.constellationLit = fullyLit ? '1' : '0';
  }, [fullyLit]);

  // Idle "clock" derived from scroll progress so all motion in the canvas
  // moves with the user. Multiplied by 2π so wave functions span a full cycle
  // across one section of scroll.
  const idleClock = t * Math.PI * 2;
  // 0 (idle, fully visible idle layer) → 1 (pressed, idle layer fades to 0)
  const idleAmt = 1 - open;

  // Phrase split into word-nodes positioned across the canvas
  const phrase = [
    { word: 'voice',        x: 220, y: 320, accent: false, t0: 0.00 },
    { word: 'is',           x: 360, y: 280, accent: false, t0: 0.10 },
    { word: 'the',          x: 460, y: 320, accent: false, t0: 0.18 },
    { word: 'new',          x: 580, y: 270, accent: false, t0: 0.26 },
    { word: 'interface',    x: 760, y: 320, accent: true,  t0: 0.34 },
  ];

  // Supporting (unlabeled) graph nodes that grow around the phrase
  const support = SUPPORT_NODES;

  return (
    <div className="hero-frame">
      <div className="hero-meta" style={{ top: 28, left: 36, fontSize: 11 }}>
        EXTRIAN · MANIFESTO_001
      </div>
      <div className="hero-meta" style={{ top: 28, right: 36, textAlign: 'right', fontSize: 11 }}>
        VOICE → MACHINE
      </div>

      <svg viewBox="0 0 1000 600" preserveAspectRatio="xMidYMid meet" style={{
        position: 'absolute', top: 56, bottom: 8, left: 0, right: 0,
        width: '100%', height: 'calc(100% - 64px)',
      }}>
        <defs>
          <pattern id="cgridX" width="40" height="40" patternUnits="userSpaceOnUse">
            <circle cx="20" cy="20" r="0.5" fill="var(--ex-mute)" opacity="0.22"/>
          </pattern>
          <radialGradient id="glowX">
            <stop offset="0%" stopColor="oklch(0.68 0.22 30)" stopOpacity="0.5"/>
            <stop offset="100%" stopColor="oklch(0.68 0.22 30)" stopOpacity="0"/>
          </radialGradient>
        </defs>
        <rect x="0" y="0" width="1000" height="600" fill="url(#cgridX)"/>

        {/* IDLE LAYER — gentle pulsing constellation visible at rest.
            Faint edges between nearest support pairs + breathing nodes. */}
        {IDLE_EDGES.map(([a, b], i) => {
          const na = SUPPORT_NODES[a], nb = SUPPORT_NODES[b];
          // wave of brightness sweeps across edges
          const wave = Math.sin(idleClock * 0.8 - i * 0.45) * 0.5 + 0.5;
          return (
            <line key={`ie${i}`} x1={na.x} y1={na.y} x2={nb.x} y2={nb.y}
              stroke="var(--ex-ink)"
              strokeWidth={0.5}
              opacity={(0.08 + wave * 0.14) * idleAmt}/>
          );
        })}
        {/* Idle traveling pulses on a few edges */}
        {IDLE_EDGES.filter((_, i) => i % 3 === 0).map(([a, b], i) => {
          const na = SUPPORT_NODES[a], nb = SUPPORT_NODES[b];
          const p = ((idleClock * 0.18 + i * 0.27) % 1);
          return (
            <circle key={`ip${i}`}
              cx={na.x + (nb.x - na.x) * p}
              cy={na.y + (nb.y - na.y) * p}
              r={1.6}
              fill="var(--ex-signal)"
              opacity={Math.sin(p * Math.PI) * 0.45 * idleAmt}/>
          );
        })}
        {/* Idle support nodes — breathing */}
        {SUPPORT_NODES.map((n, i) => {
          const breath = Math.sin(idleClock * 1.1 + i * 0.6) * 0.5 + 0.5;
          return (
            <g key={`is${i}`} opacity={idleAmt}>
              <circle cx={n.x} cy={n.y} r={2 + breath * 1.2}
                fill="var(--ex-ink)" opacity={0.18 + breath * 0.18}/>
              <circle cx={n.x} cy={n.y} r={6 + breath * 4}
                fill="none" stroke="var(--ex-signal)"
                strokeWidth="0.5"
                opacity={breath * 0.18}/>
            </g>
          );
        })}
        {/* Idle hint at the phrase position — faint dots where words will appear */}
        {[
          { x: 220, y: 320 }, { x: 360, y: 280 }, { x: 460, y: 320 },
          { x: 580, y: 270 }, { x: 760, y: 320 },
        ].map((w, i) => {
          const breath = Math.sin(idleClock * 0.9 - i * 0.5) * 0.5 + 0.5;
          return (
            <circle key={`ih${i}`} cx={w.x} cy={w.y} r={3 + breath * 1.5}
              fill="oklch(0.68 0.22 30)"
              opacity={(0.25 + breath * 0.35) * idleAmt}/>
          );
        })}

        {/* Support nodes appear from t=0.4 onward */}
        {support.map((n, i) => {
          const start = 0.4 + (i / support.length) * 0.5;
          const lit = open > start ? Math.min(1, (open - start) * 5) : 0;
          if (lit < 0.05) return null;
          return (
            <g key={`s${i}`}>
              <circle cx={n.x} cy={n.y} r={3} fill="var(--ex-ink)" opacity={0.4 * lit}/>
              {n.label && (
                <text x={n.x + 8} y={n.y + 4}
                  fontFamily="var(--ex-mono)" fontSize="9"
                  fill="var(--ex-mute)" opacity={0.7 * lit}>{n.label}</text>
              )}
            </g>
          );
        })}

        {/* Edges between phrase words and support, drawn as words appear */}
        {phrase.map((w, i) => {
          const wlit = open > w.t0 ? Math.min(1, (open - w.t0) * 6) : 0;
          if (wlit < 0.3) return null;
          // each phrase word connects to ~3 nearest support nodes
          const dists = support.map((n, j) => ({ j, d: Math.hypot(w.x - n.x, w.y - n.y) })).sort((a,b) => a.d - b.d);
          return (
            <g key={`pe${i}`}>
              {dists.slice(0, 3).map((dn, k) => {
                const start = 0.45 + (dn.j / support.length) * 0.5;
                const lit = open > start ? Math.min(1, (open - start) * 4) : 0;
                const a = phrase[i], b = support[dn.j];
                return (
                  <line key={k} x1={a.x} y1={a.y} x2={b.x} y2={b.y}
                    stroke="var(--ex-signal)"
                    strokeWidth={0.6 + lit * 0.4}
                    opacity={lit * 0.5}/>
                );
              })}
              {/* edge to next word */}
              {i < phrase.length - 1 && open > phrase[i+1].t0 && (
                <line x1={w.x} y1={w.y} x2={phrase[i+1].x} y2={phrase[i+1].y}
                  stroke="var(--ex-signal)" strokeWidth="1.2" opacity="0.7"/>
              )}
            </g>
          );
        })}

        {/* The phrase itself — words as nodes */}
        {phrase.map((w, i) => {
          const lit = open > w.t0 ? Math.min(1, (open - w.t0) * 6) : 0;
          if (lit < 0.05) return null;
          return (
            <g key={`p${i}`}>
              {lit > 0.4 && (
                <circle cx={w.x} cy={w.y} r={28 + lit * 12}
                  fill="url(#glowX)" opacity={lit * 0.7}/>
              )}
              <circle cx={w.x} cy={w.y} r={5}
                fill="var(--ex-signal)" opacity={lit}/>
              <text x={w.x} y={w.y - 16}
                fontFamily="Newsreader, Georgia, serif"
                fontSize={w.accent ? 44 : 36}
                fontWeight="300"
                fontStyle={w.accent ? 'italic' : 'normal'}
                textAnchor="middle"
                fill={w.accent ? 'oklch(0.55 0.22 30)' : 'var(--ex-ink)'}
                opacity={lit}
                letterSpacing="-0.02em">
                {w.word}
              </text>
            </g>
          );
        })}
      </svg>

      <div className="hero-meta" style={{ bottom: 24, left: 28 }}>
        {pressed ? '● MATERIALIZING' : '○ LISTENING'}
      </div>
      <div className="hero-meta" style={{ bottom: 24, right: 28, textAlign: 'right' }}>
        EXTRIAN · BUILDING THE LAYER BENEATH
      </div>
    </div>
  );
};

const SUPPORT_NODES = [
  { x: 110,  y: 110, label: 'PITCH' },
  { x: 240,  y: 80,  label: 'PROSODY' },
  { x: 420,  y: 100 },
  { x: 600,  y: 90,  label: 'STRESS' },
  { x: 850,  y: 130, label: 'TURN', lx: -42 },
  { x: 880,  y: 220 },
  { x: 100,  y: 260, label: 'BREATH', lx: -55 },
  { x: 130,  y: 420 },
  { x: 280,  y: 480, label: 'PAUSE' },
  { x: 480,  y: 510 },
  { x: 660,  y: 480, label: 'AFFECT' },
  { x: 880,  y: 460, label: 'REGISTER', lx: -78 },
  { x: 920,  y: 350 },
  { x: 380,  y: 200 },
  { x: 700,  y: 190 },
  { x: 540,  y: 180, label: 'INTENT' },
];

// Edges between nearest support nodes — used by the idle pulsing layer
const IDLE_EDGES = [
  [0,1],[1,2],[2,3],[3,4],[4,5],[5,11],[11,12],[12,10],[10,9],[9,8],
  [8,7],[7,6],[6,0],[1,13],[13,14],[14,3],[15,13],[15,14],[15,10],
  [15,8],[2,13],[14,5],
];

window.HeroConstellationX = HeroConstellationX;
