// Norton-Gauss · Agent simulation hero
// Live multi-agent network on the hero — agents spawn, route through a
// stylised operations topology, do tasks, and complete. Cursor interaction:
// hover the canvas and your pointer becomes a node, joined to the nearest
// real nodes by lime hairlines.
//
// Implementation: SVG with requestAnimationFrame. State is held in refs,
// React re-render happens on a low cadence to keep CPU light.

const { useEffect: aE, useRef: aR, useState: aS, useMemo: aM, useCallback: aC } = React;

// ─── Seeded RNG (so the topology is stable across reloads) ─────────
function rngFactory(seed) {
  let s = seed | 0;
  return () => {
    s = (s * 1664525 + 1013904223) | 0;
    return ((s >>> 0) % 100000) / 100000;
  };
}

// ─── Topology ─────────────────────────────────────────────────────
// Hand-tuned node positions plus generated edges. We carve the canvas
// into three lanes: intake (left), processing (middle/right cluster),
// outcomes (far right). Agents travel intake → 1..3 hops → outcome.
function buildTopology(W = 1600, H = 900) {
  const rng = rngFactory(42);
  const nodes = [];

  // Intake nodes (3) — left edge
  for (let i = 0; i < 3; i++) {
    nodes.push({
      id: `in-${i}`, kind: 'intake',
      x: 90, y: 240 + i * 220, r: 8,
      label: ['INTAKE · TICKETS', 'INTAKE · TELEMETRY', 'INTAKE · REQUESTS'][i],
      labelOffset: { dx: 18, dy: -14 },
    });
  }

  // Processing nodes — middle cluster, 14 nodes
  const procPositions = [
    [340, 180], [340, 360], [340, 540], [340, 720],
    [560, 120], [560, 280], [560, 460], [560, 640], [560, 800],
    [800, 200], [800, 400], [800, 600], [800, 780],
    [1040, 460],
  ];
  procPositions.forEach((p, i) => {
    const r = 4.5 + rng() * 3.5;
    nodes.push({
      id: `p-${i}`, kind: 'processing',
      x: p[0] + (rng() - 0.5) * 30, y: p[1] + (rng() - 0.5) * 30,
      r,
      label: i === 9 ? 'CORTEX · CORRELATE' : null,
      labelOffset: { dx: 14, dy: -10 },
    });
  });

  // Outcome nodes (3) — right edge
  const outcomes = [
    { label: 'AUTO · RESOLVED', y: 260 },
    { label: 'HUMAN · QUEUED',  y: 460 },
    { label: 'DEPLOYED',         y: 660 },
  ];
  outcomes.forEach((o, i) => {
    nodes.push({
      id: `out-${i}`, kind: 'outcome',
      x: 1500, y: o.y, r: 10,
      label: o.label, labelOffset: { dx: -18, dy: -14, anchor: 'end' },
    });
  });

  // Edges — generated by proximity from each node forward
  const edges = [];
  const byId = Object.fromEntries(nodes.map(n => [n.id, n]));
  const layers = [
    nodes.filter(n => n.kind === 'intake'),
    nodes.filter(n => n.kind === 'processing'),
    nodes.filter(n => n.kind === 'outcome'),
  ];

  // Each layer connects forward to ~2-3 nearest in next layer.
  function addLayerEdges(from, to) {
    for (const a of from) {
      const sorted = to.map(b => ({ b, d: Math.hypot(a.x - b.x, a.y - b.y) })).sort((u, v) => u.d - v.d);
      const k = 2 + Math.floor(rng() * 2);
      for (let i = 0; i < Math.min(k, sorted.length); i++) {
        edges.push({ id: `${a.id}|${sorted[i].b.id}`, a: a.id, b: sorted[i].b.id });
      }
    }
  }

  // intake → first half of processing
  addLayerEdges(layers[0], layers[1].slice(0, 8));
  // within processing: each connects forward to 1-2 others to the right
  for (let i = 0; i < layers[1].length; i++) {
    const a = layers[1][i];
    const rightOf = layers[1].filter(b => b.x > a.x + 40);
    if (!rightOf.length) continue;
    const sorted = rightOf.map(b => ({ b, d: Math.hypot(a.x - b.x, a.y - b.y) })).sort((u, v) => u.d - v.d);
    const k = 1 + Math.floor(rng() * 2);
    for (let i = 0; i < Math.min(k, sorted.length); i++) {
      edges.push({ id: `${a.id}|${sorted[i].b.id}`, a: a.id, b: sorted[i].b.id });
    }
  }
  // last column of processing → outcomes
  const lastCol = layers[1].filter(n => n.x > 980);
  addLayerEdges(lastCol, layers[2]);
  // also connect outcome[1] (cortex) back-fans to outcome layer for visibility
  const cortex = byId['p-9'];
  layers[2].forEach(o => edges.push({ id: `${cortex.id}|${o.id}|x`, a: cortex.id, b: o.id }));

  // Deduplicate
  const seen = new Set();
  const dedup = [];
  edges.forEach(e => { if (!seen.has(e.id)) { seen.add(e.id); dedup.push(e); } });

  // Adjacency for path-finding
  const adj = {};
  dedup.forEach(e => { (adj[e.a] = adj[e.a] || []).push(e.b); });

  return { nodes, edges: dedup, byId, adj, W, H };
}

// ─── Task labels ──────────────────────────────────────────────────
const TASK_POOL = [
  'incident.triage',
  'proposal.gen',
  'topology.correlate',
  'capacity.forecast',
  'cost.anomaly',
  'runbook.exec',
  'sla.compute',
  'cust.summarise',
  'trace.assemble',
  'finance.close',
  'supplier.coord',
  'edge.heartbeat',
  'pricing.optimise',
  'patch.deploy',
  'security.classify',
];

// ─── Pathfinding (random walk forward) ────────────────────────────
function randomPath(adj, start, rng) {
  const path = [start];
  let cur = start;
  let safety = 8;
  while (safety-- > 0) {
    const next = adj[cur];
    if (!next || !next.length) break;
    const pick = next[Math.floor(rng() * next.length)];
    path.push(pick);
    cur = pick;
    if (cur.startsWith('out-')) break;
  }
  return path;
}

// ─── The hero scene ───────────────────────────────────────────────
function AgentSim() {
  const wrapRef = aR(null);
  const svgRef = aR(null);
  const { nodes, edges, byId, adj, W, H } = aM(() => buildTopology(), []);

  // Cursor world-space position (in viewBox coords) and nearest neighbours
  const cursorRef = aR({ x: -9999, y: -9999, active: false });
  const [cursorNeigh, setCursorNeigh] = aS([]);
  const [cursorActive, setCursorActive] = aS(false);

  // Active agents (in a ref to avoid re-renders, mirrored to state every ~120ms)
  const agentsRef = aR([]);
  const [, force] = aS(0);

  // Active edges flash map (edgeId → expiration timestamp)
  const flashRef = aR({});
  const [flash, setFlash] = aS({}); // mirrored periodically

  // RNG for agent spawn & path
  const rngRef = aR(rngFactory(7));

  // Spawn cadence
  const lastSpawn = aR(0);

  // Animate
  aE(() => {
    let raf = 0;
    let lastFrame = performance.now();
    let lastMirror = lastFrame;
    let agentId = 0;
    const startAgents = 4;

    // Pre-seed a few agents so the page isn't empty on first paint
    function spawn() {
      const rng = rngRef.current;
      const intakes = nodes.filter(n => n.kind === 'intake');
      const start = intakes[Math.floor(rng() * intakes.length)].id;
      const path = randomPath(adj, start, rng);
      if (path.length < 2) return;
      agentsRef.current.push({
        id: ++agentId,
        path,
        seg: 0, t: 0,
        // Speed in segments-per-second along each edge
        speed: 0.32 + rng() * 0.34,
        task: TASK_POOL[Math.floor(rng() * TASK_POOL.length)],
        dwellAt: -1,
        dwellEnd: 0,
        outcome: null,
        born: lastFrame,
      });
    }
    for (let i = 0; i < startAgents; i++) spawn();

    const tick = (now) => {
      const dt = Math.min(0.05, (now - lastFrame) / 1000);
      lastFrame = now;

      // Spawn pacing — 1 agent ~every 0.9s, with jitter
      if (now - lastSpawn.current > 700 + Math.random() * 600) {
        if (agentsRef.current.length < 14) spawn();
        lastSpawn.current = now;
      }

      // Step agents
      const keep = [];
      for (const a of agentsRef.current) {
        // Dwell at processing nodes briefly to simulate "thinking"
        if (a.dwellAt >= 0) {
          if (now >= a.dwellEnd) {
            a.dwellAt = -1;
          } else {
            keep.push(a);
            continue;
          }
        }
        a.t += a.speed * dt;
        // Flash the edge we're traversing
        const fromId = a.path[a.seg];
        const toId = a.path[a.seg + 1];
        const eid = `${fromId}|${toId}`;
        flashRef.current[eid] = now + 800;

        if (a.t >= 1) {
          // arrive at next node
          a.seg += 1;
          a.t = 0;
          // dwell only at processing
          const node = byId[a.path[a.seg]];
          if (node) {
            if (node.kind === 'processing' && a.seg !== a.path.length - 1) {
              a.dwellAt = a.seg;
              a.dwellEnd = now + 280 + Math.random() * 380;
            } else if (node.kind === 'outcome') {
              a.outcome = node.id;
              a.completedAt = now;
            }
          }
        }
        // remove after a brief glow at outcome
        if (a.outcome && now - a.completedAt > 800) continue;
        keep.push(a);
      }
      agentsRef.current = keep;

      // Mirror state to React at ~15 fps (cheap-enough re-render, plenty smooth)
      if (now - lastMirror > 66) {
        // Prune expired flashes from object
        const expired = [];
        for (const k in flashRef.current) {
          if (flashRef.current[k] < now) expired.push(k);
        }
        expired.forEach(k => delete flashRef.current[k]);

        setFlash({ ...flashRef.current });
        force(now);
        lastMirror = now;
      }
      raf = requestAnimationFrame(tick);
    };
    // Pause animation when tab is hidden — saves CPU & frees the preview
    const onVis = () => {
      if (document.hidden) {
        cancelAnimationFrame(raf);
      } else {
        lastFrame = performance.now();
        raf = requestAnimationFrame(tick);
      }
    };
    document.addEventListener('visibilitychange', onVis);
    raf = requestAnimationFrame(tick);
    return () => { cancelAnimationFrame(raf); document.removeEventListener('visibilitychange', onVis); };
  }, [adj, byId, nodes]);

  // Track cursor inside the SVG (world-space). Throttled to ~30fps.
  const lastMouseRef = aR(0);
  const onMouseMove = aC((e) => {
    const now = performance.now();
    if (now - lastMouseRef.current < 33) return;
    lastMouseRef.current = now;
    const svg = svgRef.current;
    if (!svg) return;
    const pt = svg.createSVGPoint();
    pt.x = e.clientX; pt.y = e.clientY;
    const m = svg.getScreenCTM();
    if (!m) return;
    const w = pt.matrixTransform(m.inverse());
    cursorRef.current.x = w.x;
    cursorRef.current.y = w.y;
    cursorRef.current.active = true;
    if (!cursorActive) setCursorActive(true);

    // Find nearest 3 nodes
    const sorted = nodes
      .map(n => ({ id: n.id, d: Math.hypot(n.x - w.x, n.y - w.y) }))
      .sort((u, v) => u.d - v.d)
      .slice(0, 3);
    setCursorNeigh(sorted);
  }, [nodes, cursorActive]);

  const onMouseLeave = aC(() => {
    cursorRef.current.active = false;
    setCursorActive(false);
    setCursorNeigh([]);
  }, []);

  // ─── Render ─────────────────────────────────────────────────────
  // Agent positions: interpolate from byId[path[seg]] to byId[path[seg+1]]
  const agents = agentsRef.current;

  return (
    <div ref={wrapRef} className="agent-sim-wrap"
      style={{ position: 'absolute', inset: 0, pointerEvents: 'auto' }}
      onMouseMove={onMouseMove} onMouseLeave={onMouseLeave}>

      <svg ref={svgRef}
        viewBox={`0 0 ${W} ${H}`}
        preserveAspectRatio="xMidYMid slice"
        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>

        <defs>
          <radialGradient id="halo" cx="50%" cy="50%" r="50%">
            <stop offset="0%" stopColor="#D9FF35" stopOpacity="0.5" />
            <stop offset="55%" stopColor="#D9FF35" stopOpacity="0.06" />
            <stop offset="100%" stopColor="#D9FF35" stopOpacity="0" />
          </radialGradient>
          <radialGradient id="halo-soft" cx="50%" cy="50%" r="50%">
            <stop offset="0%" stopColor="#D9FF35" stopOpacity="0.18" />
            <stop offset="100%" stopColor="#D9FF35" stopOpacity="0" />
          </radialGradient>
          <filter id="glow"><feGaussianBlur stdDeviation="3" /></filter>
        </defs>

        {/* Soft halo behind the cortex node */}
        {(() => {
          const cortex = byId['p-9'];
          return <circle cx={cortex.x} cy={cortex.y} r="240" fill="url(#halo)" />;
        })()}

        {/* Grid + corner labels */}
        <g opacity="0.5">
          {/* dotted grid */}
          {Array.from({ length: 9 }).map((_, i) => (
            <line key={`gv-${i}`} x1={i * 200} y1="0" x2={i * 200} y2={H}
              stroke="#1A2E29" strokeWidth="1" strokeDasharray="2 8" />
          ))}
          {Array.from({ length: 5 }).map((_, i) => (
            <line key={`gh-${i}`} x1="0" y1={i * 200} x2={W} y2={i * 200}
              stroke="#1A2E29" strokeWidth="1" strokeDasharray="2 8" />
          ))}
        </g>

        {/* Edges */}
        {edges.map(e => {
          const a = byId[e.a], b = byId[e.b];
          const hot = flash[e.id] && flash[e.id] > performance.now();
          const isCortexFan = e.a === 'p-9' && e.id.endsWith('|x');
          return (
            <line key={e.id}
              x1={a.x} y1={a.y} x2={b.x} y2={b.y}
              stroke={hot ? '#D9FF35' : '#1A2E29'}
              strokeWidth={hot ? 1.4 : (isCortexFan ? 0.6 : 0.8)}
              opacity={hot ? 0.7 : (isCortexFan ? 0.3 : 0.8)}
            />
          );
        })}

        {/* Cursor lines + cursor node */}
        {cursorActive && cursorNeigh.map((nh, i) => {
          const n = byId[nh.id];
          return (
            <line key={`c-${nh.id}`}
              x1={cursorRef.current.x} y1={cursorRef.current.y}
              x2={n.x} y2={n.y}
              stroke="#D9FF35" strokeWidth="1" strokeDasharray="2 4"
              opacity={0.4 - i * 0.1} />
          );
        })}
        {cursorActive && (
          <g>
            <circle cx={cursorRef.current.x} cy={cursorRef.current.y} r="14"
              fill="#D9FF35" opacity="0.12" />
            <circle cx={cursorRef.current.x} cy={cursorRef.current.y} r="5"
              fill="#D9FF35" stroke="#D9FF35" />
          </g>
        )}

        {/* Nodes */}
        {nodes.map(n => {
          const isHub = n.id === 'p-9';
          const isIntake = n.kind === 'intake';
          const isOutcome = n.kind === 'outcome';
          // Pulse if an agent recently arrived
          const arriveExpire = flashRef.current[`${n.id}@arrive`];
          const arrived = arriveExpire && arriveExpire > performance.now();
          const ringR = arrived ? n.r * 3.4 : n.r * 2.2;

          return (
            <g key={n.id}>
              {(isHub || isIntake || isOutcome) && (
                <circle cx={n.x} cy={n.y} r={n.r * 2.4} fill="url(#halo-soft)" />
              )}
              <circle cx={n.x} cy={n.y} r={n.r}
                fill={isOutcome || isIntake || isHub ? '#D9FF35' : '#0C1A18'}
                stroke={isOutcome || isIntake || isHub ? '#D9FF35' : '#234234'}
                strokeWidth={isHub ? 2 : 1.2} />
              {isHub && (
                <circle cx={n.x} cy={n.y} r={n.r + 8} fill="none"
                  stroke="#D9FF35" strokeWidth="0.7" opacity="0.5" />
              )}
              {n.label && (
                <text x={n.x + (n.labelOffset?.dx || 14)}
                  y={n.y + (n.labelOffset?.dy || -10)}
                  fontFamily="JetBrains Mono"
                  fontSize="11" letterSpacing="1.8"
                  fill={isHub ? '#D9FF35' : isIntake ? '#F2F1EC' : '#F2F1EC'}
                  textAnchor={n.labelOffset?.anchor || 'start'}>
                  {n.label}
                </text>
              )}
            </g>
          );
        })}

        {/* Agents */}
        {agents.map(a => {
          const fromId = a.path[a.seg];
          const toId = a.path[a.seg + 1];
          const from = byId[fromId];
          const to = byId[toId];
          let x, y;
          if (a.dwellAt >= 0) {
            const n = byId[a.path[a.dwellAt]];
            x = n.x; y = n.y;
          } else if (a.outcome) {
            const n = byId[a.outcome];
            x = n.x; y = n.y;
          } else if (from && to) {
            x = from.x + (to.x - from.x) * a.t;
            y = from.y + (to.y - from.y) * a.t;
          } else {
            return null;
          }
          const dwelling = a.dwellAt >= 0;
          const completing = !!a.outcome;
          return (
            <g key={a.id}>
              <circle cx={x} cy={y} r={dwelling ? 8 : 5}
                fill="#D9FF35"
                opacity={completing ? 0.4 : 1}
                style={{ filter: dwelling ? 'drop-shadow(0 0 8px rgba(217,255,53,0.7))' : 'none' }} />
              {!completing && (
                <text x={x + 9} y={y + 4}
                  fontFamily="JetBrains Mono" fontSize="9"
                  letterSpacing="1.2"
                  fill="#D9FF35" opacity="0.85">
                  {a.task}
                </text>
              )}
            </g>
          );
        })}

        {/* Corner readouts */}
        <text x="40" y="44" fontFamily="JetBrains Mono" fontSize="11"
          letterSpacing="1.8" fill="#5A6E6A">NG · INTELLIGENT OPERATIONS · LIVE</text>
        <text x="40" y="64" fontFamily="JetBrains Mono" fontSize="11"
          letterSpacing="1.8" fill="#D9FF35">{agents.length} AGENTS ACTIVE</text>

        <text x={W - 40} y="44" textAnchor="end" fontFamily="JetBrains Mono"
          fontSize="11" letterSpacing="1.8" fill="#5A6E6A">{nodes.length} NODES · {edges.length} EDGES</text>
        <text x={W - 40} y="64" textAnchor="end" fontFamily="JetBrains Mono"
          fontSize="11" letterSpacing="1.8" fill="#5A6E6A">UPTIME · 99.97%</text>

        {/* Subtle ground line at the bottom */}
        <line x1="0" y1={H - 1} x2={W} y2={H - 1} stroke="#1A2E29" />
      </svg>
    </div>
  );
}

// ─── Alternative hero variants ────────────────────────────────────
function HeroLogoMorph() {
  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'flex-end', paddingRight: '7vw' }}>
      <div style={{ position: 'relative', width: 480, height: 480 }}>
        {/* Pulse rings */}
        {[100, 160, 240, 340, 460].map((r) => (
          <div key={r} style={{
            position: 'absolute', left: '50%', top: '50%',
            width: r, height: r, borderRadius: '50%',
            border: '1px solid rgba(217,255,53,0.2)',
            transform: 'translate(-50%, -50%)',
            animation: `ringPulse 4s ease-out infinite ${r * 0.004}s`,
          }} />
        ))}
        <img src={(window.__resources && window.__resources.logoMarkWhite) || "assets/logo-mark-white.png"} alt=""
          style={{ position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%, -50%)', width: 280, opacity: 0.35, filter: 'grayscale(1) brightness(0.7)', animation: 'morphBw 6s ease-in-out infinite' }} />
        <img src={(window.__resources && window.__resources.logoMarkColor) || "assets/logo-mark-color.png"} alt=""
          style={{ position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%, -50%)', width: 280, animation: 'morphColor 6s ease-in-out infinite' }} />
      </div>
      <style>{`
        @keyframes ringPulse { 0% { opacity: 0; transform: translate(-50%, -50%) scale(.6); } 30% { opacity: 0.6; } 100% { opacity: 0; transform: translate(-50%, -50%) scale(1.4); } }
        @keyframes morphBw { 0%, 100% { opacity: 0.55; } 50% { opacity: 0.05; } }
        @keyframes morphColor { 0%, 100% { opacity: 0.15; } 50% { opacity: 1; } }
      `}</style>
    </div>
  );
}

function HeroKinetic() {
  // Just a subtle background pattern for kinetic mode (the main h1 carries it)
  return (
    <div style={{ position: 'absolute', inset: 0, opacity: 0.4 }}>
      <svg viewBox="0 0 1200 900" preserveAspectRatio="xMidYMid slice"
        style={{ width: '100%', height: '100%' }}>
        {Array.from({ length: 9 }).map((_, i) => (
          <line key={i} x1={i * 150} y1="0" x2={i * 150} y2="900" stroke="#1A2E29" strokeWidth="1" />
        ))}
      </svg>
    </div>
  );
}

function HeroNoc() {
  const [tick, setTick] = aS(0);
  aE(() => {
    const t = setInterval(() => setTick(x => x + 1), 1300);
    return () => clearInterval(t);
  }, []);
  const rng = rngFactory(tick + 1);
  const spark1 = Array.from({ length: 28 }, () => 30 + rng() * 60);
  const spark2 = Array.from({ length: 28 }, () => 20 + rng() * 70);
  const bars = Array.from({ length: 22 }, () => 25 + rng() * 75);
  const sparkPath = (data, w, h) => {
    const max = Math.max(...data);
    return data.map((v, i) => `${(i / (data.length - 1)) * w},${h - (v / max) * (h - 4) - 2}`).join(' ');
  };
  return (
    <div style={{ position: 'absolute', inset: 0 }}>
      <svg viewBox="0 0 1200 900" preserveAspectRatio="xMidYMid slice" style={{ width: '100%', height: '100%' }}>
        {Array.from({ length: 13 }).map((_, i) => <line key={`gv-${i}`} x1={i * 100} y1="0" x2={i * 100} y2="900" stroke="#1A2E29" strokeDasharray="2 8" />)}
        {Array.from({ length: 9 }).map((_, i) => <line key={`gh-${i}`} x1="0" y1={i * 112} x2="1200" y2={i * 112} stroke="#1A2E29" strokeDasharray="2 8" />)}
      </svg>

      {/* Panels */}
      <div style={{ position: 'absolute', top: '8%', right: '6%', width: 280, padding: '18px 20px', border: '1px solid #234234', borderRadius: 10, background: 'rgba(8,17,15,0.7)', backdropFilter: 'blur(10px)' }}>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: '0.14em', color: '#5A6E6A', textTransform: 'uppercase', marginBottom: 8 }}>Network throughput</div>
        <div style={{ fontFamily: 'Bricolage Grotesque', fontWeight: 500, fontSize: 28, color: '#D9FF35', letterSpacing: '-0.018em', lineHeight: 1 }}>{(842 + (tick % 30)).toFixed(1)} <small style={{ fontSize: 14, color: '#F2F1EC' }}>Gbps</small></div>
        <svg viewBox="0 0 220 40" style={{ width: '100%', height: 40, marginTop: 10 }}>
          <polyline points={sparkPath(spark1, 220, 40)} fill="none" stroke="#D9FF35" strokeWidth="1.5" />
        </svg>
      </div>
      <div style={{ position: 'absolute', top: '32%', right: '24%', width: 220, padding: '18px 20px', border: '1px solid #234234', borderRadius: 10, background: 'rgba(8,17,15,0.7)', backdropFilter: 'blur(10px)' }}>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: '0.14em', color: '#5A6E6A', textTransform: 'uppercase', marginBottom: 8 }}>Sites monitored</div>
        <div style={{ fontFamily: 'Bricolage Grotesque', fontWeight: 500, fontSize: 28, color: '#D9FF35', letterSpacing: '-0.018em', lineHeight: 1 }}>2,847</div>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 10.5, color: '#D9FF35', marginTop: 6 }}>↑ 12 since 24:00</div>
      </div>
      <div style={{ position: 'absolute', bottom: '20%', right: '5%', width: 260, padding: '18px 20px', border: '1px solid #234234', borderRadius: 10, background: 'rgba(8,17,15,0.7)', backdropFilter: 'blur(10px)' }}>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: '0.14em', color: '#5A6E6A', textTransform: 'uppercase', marginBottom: 8 }}>Anomalies · last 4h</div>
        <div style={{ fontFamily: 'Bricolage Grotesque', fontWeight: 500, fontSize: 28, color: '#ff9050', letterSpacing: '-0.018em', lineHeight: 1 }}>3</div>
        <div style={{ display: 'flex', alignItems: 'end', gap: 3, height: 36, marginTop: 10 }}>
          {bars.map((v, i) => <i key={i} style={{ flex: 1, height: `${v}%`, background: v > 65 ? '#ff9050' : '#D9FF35', opacity: 0.85, borderRadius: 1 }} />)}
        </div>
      </div>
      <div style={{ position: 'absolute', bottom: '8%', right: '34%', width: 240, padding: '18px 20px', border: '1px solid #234234', borderRadius: 10, background: 'rgba(8,17,15,0.7)', backdropFilter: 'blur(10px)' }}>
        <div style={{ fontFamily: 'JetBrains Mono', fontSize: 10, letterSpacing: '0.14em', color: '#5A6E6A', textTransform: 'uppercase', marginBottom: 8 }}>SLA · 30 day rolling</div>
        <div style={{ fontFamily: 'Bricolage Grotesque', fontWeight: 500, fontSize: 28, color: '#D9FF35', letterSpacing: '-0.018em', lineHeight: 1 }}>99.97 <small style={{ fontSize: 14, color: '#F2F1EC' }}>%</small></div>
        <svg viewBox="0 0 220 40" style={{ width: '100%', height: 40, marginTop: 10 }}>
          <polyline points={sparkPath(spark2, 220, 40)} fill="none" stroke="#D9FF35" strokeWidth="1.5" />
        </svg>
      </div>
    </div>
  );
}

// ─── Three.js neural-mesh hero (drag-to-rotate, scroll-distort) ──
function HeroMesh() {
  const hostRef = aR(null);
  aE(() => {
    if (!hostRef.current) return;
    let disposed = false;
    let dispose = () => {};
    // Wait for THREE to be loaded (CDN) — poll briefly
    function tryMount(retry) {
      if (disposed) return;
      if (!window.THREE || !window.NGHeroMesh) {
        if (retry > 60) return;
        setTimeout(() => tryMount(retry + 1), 80);
        return;
      }
      dispose = window.NGHeroMesh(hostRef.current);
    }
    tryMount(0);
    return () => { disposed = true; dispose && dispose(); };
  }, []);
  return (
    <div
      ref={hostRef}
      className="hero-mesh-host"
      style={{ position: 'absolute', inset: 0, pointerEvents: 'auto' }}
      aria-hidden="true"
    />
  );
}

function HeroVisual({ variant }) {
  if (variant === 'logo') return <HeroLogoMorph />;
  if (variant === 'noc') return <HeroNoc />;
  if (variant === 'kinetic') return <HeroKinetic />;
  if (variant === 'agent') return <AgentSim />;
  return <HeroMesh />;
}

// ─── Decorative mesh for sub-pages (smaller, no-drag, no-scroll) ──
function SubpageMesh({ side = 'right', size = 'md' }) {
  const hostRef = aR(null);
  aE(() => {
    if (!hostRef.current) return;
    let disposed = false;
    let dispose = () => {};
    function tryMount(retry) {
      if (disposed) return;
      if (!window.THREE || !window.NGHeroMesh) {
        if (retry > 60) return;
        setTimeout(() => tryMount(retry + 1), 80);
        return;
      }
      const meshScale = size === 'sm' ? 0.62 : size === 'lg' ? 0.92 : 0.78;
      dispose = window.NGHeroMesh(hostRef.current, { decorative: true, meshScale });
    }
    tryMount(0);
    return () => { disposed = true; dispose && dispose(); };
  }, [side, size]);
  return (
    <div
      ref={hostRef}
      className={`subpage-mesh-host pos-${side} size-${size}`}
      aria-hidden="true"
    />
  );
}

Object.assign(window, { AgentSim, HeroLogoMorph, HeroKinetic, HeroNoc, HeroMesh, HeroVisual, SubpageMesh });
