// neural-hero.jsx — "Neural" hero, 3D edition (v3: anatomical brain)
// Strategy: Build a proper brain SHAPE that reads as a brain at first glance:
//   • Two distinct hemispheres separated by a deep longitudinal fissure
//   • Egg-shaped silhouette (longer front-to-back than side-to-side)
//   • Heavy gyri/sulci surface displacement (the iconic "folds")
//   • Cerebellum bulge tucked under the rear
//   • Brainstem stub
// Then sparse glowing surface neurons that fire region-by-region, with
// arcing synapse lightning between cross-region pairs.

const { motion: nm, useScroll: nuScroll, useTransform: nuTransform } = window.framerMotion || window.Motion || {};
const { Reveal: NReveal, SplitText: NSplitText, MagneticLink: NMagneticLink, IconArrow: NIconArrow } = window;

function BrainCanvas3D() {
  const mountRef = React.useRef(null);
  const [ready, setReady] = React.useState(false);
  const [error, setError] = React.useState(false);

  React.useEffect(() => {
    if (!mountRef.current) return;
    let cleanup; let cancelled = false;
    const ensureThree = () => new Promise((resolve, reject) => {
      if (window.THREE) return resolve(window.THREE);
      const s = document.createElement("script");
      s.src = "https://unpkg.com/three@0.160.0/build/three.min.js";
      s.onload = () => resolve(window.THREE);
      s.onerror = reject;
      document.head.appendChild(s);
    });
    ensureThree().then((THREE) => {
      if (cancelled || !mountRef.current) return;
      cleanup = setupScene(THREE, mountRef.current, () => setReady(true));
    }).catch(() => setError(true));
    return () => { cancelled = true; if (cleanup && cleanup.dispose) cleanup.dispose(); };
  }, []);

  return (
    <div className="nh3-stage" ref={mountRef}>
      {!ready && !error && <div className="nh3-loading">initializing neural mesh…</div>}
      {error && <div className="nh3-loading nh3-error">webgl unavailable</div>}
    </div>
  );
}

/* ── Hemisphere displacement ──────────────────────────────────────
   For a unit-direction vector (in hemisphere-local space), returns
   the surface radius. Heavy multi-octave noise gives the gyri/sulci
   look. The hemisphere is squashed flat on its medial side (x=0
   plane) so two of these meeting form the longitudinal fissure.

   Hemisphere-local axes:
     +x = lateral (outward, away from midline)
     +y = up (parietal/crown)
     +z = anterior (frontal)
     -z = posterior (occipital)
*/
function hemiRadius(d) {
  const x = d.x, y = d.y, z = d.z;

  // Base ellipsoid: longer front-to-back (z), narrower medially (x),
  // and slightly taller than wide.
  // r_base = 1 / sqrt((x/a)^2 + (y/b)^2 + (z/c)^2)
  // a controls lateral width, b controls height, c controls front-back length
  const a = 0.78;   // lateral half-width (small => narrow per-hemisphere)
  const b = 0.95;   // vertical half-height
  const c = 1.20;   // anteroposterior half-length
  const baseR = 1.0 / Math.sqrt(
    (x / a) * (x / a) +
    (y / b) * (y / b) +
    (z / c) * (z / c)
  );

  // Frontal pole bulge — the front (positive z) sticks forward and rounds up
  let front = 0;
  if (z > 0.1) front = 0.10 * Math.pow(Math.max(0, z), 1.5);

  // Occipital pole — back is rounded but slightly tapered
  let back = 0;
  if (z < -0.3) back = 0.04 * (-z - 0.3);

  // Temporal lobe bulge — sticks down and slightly forward on the lateral side
  let temporal = 0;
  if (y < -0.1 && x > 0.2) {
    const t = Math.max(0, -y - 0.1) * Math.max(0, x - 0.2) * 4.0;
    const fwd = (z + 0.3) * 0.3 + 0.7; // slightly favors forward-down
    temporal = 0.18 * t * fwd;
  }

  // GYRI/SULCI displacement — multi-octave noise tuned for brain-like
  // wavy folds. Lower-frequency for major lobes, high-frequency on top
  // for the characteristic small ridges.
  const gyri =
    0.085 * Math.sin(x * 4.5 + z * 3.0) * Math.cos(y * 4.0 + z * 1.8) +
    0.065 * Math.sin(z * 5.5 + 1.7) * Math.cos(y * 5.0 - x * 1.8) +
    0.050 * Math.sin(y * 6.5 + x * 3.5 + z * 1.2) +
    // High-frequency ridges — these create the characteristic wavy gyri
    0.045 * Math.sin(x * 12.0 + y * 7.0 + z * 3.0) * Math.cos(z * 11.0 + y * 4.0) +
    0.035 * Math.sin(y * 14.0 - z * 10.0 + x * 5.0) * Math.cos(x * 9.0) +
    0.025 * Math.sin(z * 16.0 + x * 8.0) * Math.cos(y * 12.0);

  // Medial flatten — push the medial side (x close to 0) inward a tiny
  // bit so the fissure looks deeper.
  let medial = 0;
  if (x < 0.15) medial = -0.04 * (0.15 - x);

  return baseR + front + back + temporal + gyri + medial;
}

/* ── Cerebellum displacement ──────────────────────────────────────
   Smaller bumpy mass with characteristic horizontal folia. */
function cerebRadius(d) {
  const x = d.x, y = d.y, z = d.z;
  const a = 0.85, b = 0.65, c = 0.85;
  const baseR = 1.0 / Math.sqrt(
    (x / a) * (x / a) +
    (y / b) * (y / b) +
    (z / c) * (z / c)
  );
  // Horizontal ridges (folia) — thin parallel grooves
  const folia =
    0.025 * Math.sin(y * 26.0) +
    0.018 * Math.sin(y * 18.0 + z * 4.0) * Math.cos(x * 3.0);
  return baseR + folia;
}

function setupScene(THREE, mount, onReady) {
  const w = mount.clientWidth || 600;
  const h = mount.clientHeight || 600;

  const scene = new THREE.Scene();

  const camera = new THREE.PerspectiveCamera(34, w / h, 0.1, 100);
  // Pull camera up and back so we see a 3/4 top-down view (showing the
  // longitudinal fissure between the hemispheres clearly, like a real brain).
  camera.position.set(0, 2.2, 8.5);
  camera.lookAt(0, 0, 0);

  let renderer;
  try {
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, powerPreference: "high-performance" });
  } catch (e) { return { dispose: () => {} }; }
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
  renderer.setSize(w, h);
  renderer.setClearColor(0x000000, 0);
  mount.appendChild(renderer.domElement);

  // Brain group — wraps everything so we can rotate as one
  const brainGroup = new THREE.Group();
  brainGroup.scale.setScalar(1.45);
  scene.add(brainGroup);

  /* ── 1. Cerebrum: two hemispheres ── */
  const FISSURE_GAP = 0.025;     // half-gap between hemispheres (subtle)
  const cerebrumGroup = new THREE.Group();
  brainGroup.add(cerebrumGroup);

  function buildHemisphere(side) {
    // side: -1 (left) or +1 (right). In hemisphere-local space we always
    // build the "right half" (x >= 0); for the left we mirror in x.
    const geo = new THREE.IcosahedronGeometry(1, 6); // ~10242 verts, denser folds
    const pos = geo.attributes.position;
    const norm = new THREE.Vector3();
    for (let i = 0; i < pos.count; i++) {
      norm.fromBufferAttribute(pos, i).normalize();
      // Force x >= 0 in local space (mirror left half to right)
      const localX = Math.abs(norm.x);
      const local = new THREE.Vector3(localX, norm.y, norm.z);
      const r = hemiRadius(local);
      pos.setXYZ(i, localX * r, norm.y * r, norm.z * r);
    }
    geo.computeVertexNormals();

    // World scale + offset
    const group = new THREE.Group();

    // Edges geometry — only edges between faces with significant angle
    // change (real surface creases). This gives a proper "fold" look
    // instead of dense random triangulation.
    // Edges geometry — picks up real surface creases for the gyri/sulci look
    const edges = new THREE.EdgesGeometry(geo, 14);
    const wire = new THREE.LineSegments(
      edges,
      new THREE.LineBasicMaterial({
        color: 0xb8e6ff,
        transparent: true,
        opacity: 0.78,
        depthWrite: false,
      })
    );
    group.add(wire);

    // Filled opaque shell behind edges so far-side edges don't show through.
    const fillMat = new THREE.MeshBasicMaterial({
      color: 0x0c1828,
      transparent: false,
      opacity: 1.0,
      depthWrite: true,
      side: THREE.FrontSide,
    });
    const fill = new THREE.Mesh(geo, fillMat);
    group.add(fill);

    // Mirror to left side and offset by fissure gap
    group.scale.x = side; // flips x for left hemisphere
    group.position.x = side * FISSURE_GAP;

    cerebrumGroup.add(group);

    return { geo, wire, fill, fillMat };
  }

  const left = buildHemisphere(-1);
  const right = buildHemisphere(1);

  /* ── 2. Cerebellum — bumpy mass tucked under the back ── */
  const cereGeo = new THREE.IcosahedronGeometry(1, 4);
  {
    const pos = cereGeo.attributes.position;
    const norm = new THREE.Vector3();
    for (let i = 0; i < pos.count; i++) {
      norm.fromBufferAttribute(pos, i).normalize();
      const r = cerebRadius(norm);
      pos.setXYZ(i, norm.x * r, norm.y * r, norm.z * r);
    }
    cereGeo.computeVertexNormals();
  }
  const cereGroup = new THREE.Group();
  // Position under-and-behind the cerebrum
  cereGroup.position.set(0, -0.55, -0.95);
  cereGroup.scale.set(0.55, 0.42, 0.55);
  brainGroup.add(cereGroup);

  const cereWire = new THREE.LineSegments(
    new THREE.EdgesGeometry(cereGeo, 12),
    new THREE.LineBasicMaterial({ color: 0x9fdcff, transparent: true, opacity: 0.55, depthWrite: false })
  );
  const cereFillMat = new THREE.MeshBasicMaterial({
    color: 0x0a1422, transparent: false, opacity: 1.0, depthWrite: true,
  });
  const cereFill = new THREE.Mesh(cereGeo, cereFillMat);
  cereGroup.add(cereWire);
  cereGroup.add(cereFill);

  /* ── 3. Brainstem — small descending cylinder (subtle, mostly hidden) ── */
  const stemGeo = new THREE.CylinderGeometry(0.10, 0.08, 0.40, 12, 1);
  const stemMat = new THREE.MeshBasicMaterial({ color: 0x1a2a3c, transparent: true, opacity: 0.5 });
  const stem = new THREE.Mesh(stemGeo, stemMat);
  stem.position.set(0, -1.05, -0.7);
  stem.rotation.x = 0.25;
  brainGroup.add(stem);

  /* ── 4. Surface neurons: glowing points stuck to the cerebrum surface ── */
  const NEURON_COUNT = 280;
  const neuronPositions = new Float32Array(NEURON_COUNT * 3);
  const neuronRegions = new Float32Array(NEURON_COUNT);
  const neuronPhases = new Float32Array(NEURON_COUNT);

  // Region: 0=frontal, 1=parietal, 2=occipital, 3=temporal, 4=cerebellum, 5=core
  for (let i = 0; i < NEURON_COUNT; i++) {
    let region;
    let p = new THREE.Vector3();
    const r = Math.random();
    if (r < 0.85) {
      // Cerebrum surface
      const side = Math.random() < 0.5 ? -1 : 1;
      // Random direction (avoiding the medial fissure)
      let dir;
      let tries = 0;
      do {
        dir = new THREE.Vector3(
          Math.random() * 0.95 + 0.05, // bias outward (positive in hemisphere-local)
          Math.random() * 2 - 1,
          Math.random() * 2 - 1
        ).normalize();
        tries++;
      } while (dir.x < 0.18 && tries < 8);
      const rad = hemiRadius(dir);
      p.copy(dir).multiplyScalar(rad);
      // Apply hemisphere world transform (mirror + offset)
      p.x = side * (p.x + FISSURE_GAP);
      // Region from y/z
      if (p.y > 0.5) region = 1;            // parietal (top)
      else if (p.z > 0.4) region = 0;        // frontal
      else if (p.z < -0.6) region = 2;       // occipital
      else region = 3;                       // temporal
    } else if (r < 0.95) {
      // Cerebellum surface
      const dir = new THREE.Vector3(
        Math.random() * 2 - 1,
        Math.random() * 2 - 1,
        Math.random() * 2 - 1
      ).normalize();
      const rad = cerebRadius(dir);
      p.copy(dir).multiplyScalar(rad);
      // Apply cereGroup transform manually
      p.x *= 0.55; p.y *= 0.42; p.z *= 0.55;
      p.x += 0; p.y += -0.55; p.z += -0.95;
      region = 4;
    } else {
      // Core (thalamus) — bright nodes deep
      p.set((Math.random() - 0.5) * 0.5, (Math.random() - 0.5) * 0.5, (Math.random() - 0.5) * 0.5);
      region = 5;
    }
    neuronPositions[i * 3] = p.x;
    neuronPositions[i * 3 + 1] = p.y;
    neuronPositions[i * 3 + 2] = p.z;
    neuronRegions[i] = region;
    neuronPhases[i] = Math.random() * Math.PI * 2;
  }

  const neuronGeo = new THREE.BufferGeometry();
  neuronGeo.setAttribute("position", new THREE.BufferAttribute(neuronPositions, 3));
  neuronGeo.setAttribute("aRegion", new THREE.BufferAttribute(neuronRegions, 1));
  neuronGeo.setAttribute("aPhase", new THREE.BufferAttribute(neuronPhases, 1));

  const neuronMat = new THREE.ShaderMaterial({
    transparent: true,
    depthWrite: false,
    uniforms: {
      uTime: { value: 0 },
      uPx: { value: renderer.getPixelRatio() },
      uAccent: { value: new THREE.Color(0x7cc4e8) },
      uAccent2: { value: new THREE.Color(0xb79dff) },
      uFireRegion: { value: -1 },
      uFireT: { value: 0 },
    },
    vertexShader: `
      attribute float aRegion;
      attribute float aPhase;
      varying float vFire;
      varying float vRegion;
      uniform float uTime;
      uniform float uPx;
      uniform float uFireRegion;
      uniform float uFireT;
      void main() {
        vRegion = aRegion;
        float fire = 0.0;
        if (abs(aRegion - uFireRegion) < 0.5) {
          fire = uFireT * (0.7 + 0.3 * sin(aPhase + uTime * 8.0));
        }
        vFire = fire;
        float breath = 0.6 + 0.4 * sin(uTime * 1.4 + aPhase);
        vec4 mv = modelViewMatrix * vec4(position, 1.0);
        gl_Position = projectionMatrix * mv;
        float baseSize = 4.0 + 2.0 * breath;
        float fireBoost = 1.0 + 5.0 * fire;
        gl_PointSize = baseSize * fireBoost * uPx * (12.0 / -mv.z);
      }
    `,
    fragmentShader: `
      varying float vFire;
      varying float vRegion;
      uniform vec3 uAccent;
      uniform vec3 uAccent2;
      void main() {
        vec2 uv = gl_PointCoord - 0.5;
        float d = length(uv);
        if (d > 0.5) discard;
        float a = smoothstep(0.5, 0.0, d);
        a = pow(a, 1.6);
        vec3 col = mix(uAccent, vec3(1.0), 0.4);
        if (abs(vRegion - 5.0) < 0.5) col = mix(col, vec3(1.0), 0.5);
        col = mix(col, vec3(1.0), vFire * 0.95);
        gl_FragColor = vec4(col, a * (0.55 + vFire * 0.45));
      }
    `,
  });
  const neurons = new THREE.Points(neuronGeo, neuronMat);
  brainGroup.add(neurons);

  /* ── 5. Synapse lightning ── */
  const ARC_COUNT = 10;
  const arcs = [];
  const candidatePairs = [];
  for (let i = 0; i < 200; i++) {
    const a = Math.floor(Math.random() * NEURON_COUNT);
    const b = Math.floor(Math.random() * NEURON_COUNT);
    if (a === b) continue;
    if (neuronRegions[a] === neuronRegions[b]) continue;
    candidatePairs.push([a, b]);
  }

  const arcGroup = new THREE.Group();
  brainGroup.add(arcGroup);

  for (let i = 0; i < ARC_COUNT; i++) {
    const SEG = 32;
    const arr = new Float32Array(SEG * 3);
    const g = new THREE.BufferGeometry();
    g.setAttribute("position", new THREE.BufferAttribute(arr, 3));
    const m = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0 });
    const line = new THREE.Line(g, m);
    arcGroup.add(line);
    arcs.push({ line, mat: m, geom: g, t: 1, dur: 0, seg: SEG });
  }

  function fireArc(elapsed) {
    const free = arcs.find((a) => a.t >= 1);
    if (!free) return;
    const pair = candidatePairs[Math.floor(Math.random() * candidatePairs.length)];
    if (!pair) return;
    const ax = neuronPositions[pair[0] * 3], ay = neuronPositions[pair[0] * 3 + 1], az = neuronPositions[pair[0] * 3 + 2];
    const bx = neuronPositions[pair[1] * 3], by = neuronPositions[pair[1] * 3 + 1], bz = neuronPositions[pair[1] * 3 + 2];
    const mx = (ax + bx) / 2, my = (ay + by) / 2, mz = (az + bz) / 2;
    const out = Math.sqrt(mx * mx + my * my + mz * mz) || 1;
    const k = 0.6;
    const cx = mx * (1 + k / out);
    const cy = my * (1 + k / out);
    const cz = mz * (1 + k / out);
    const arr = free.geom.attributes.position.array;
    for (let i = 0; i < free.seg; i++) {
      const u = i / (free.seg - 1);
      const omu = 1 - u;
      arr[i * 3] = omu * omu * ax + 2 * omu * u * cx + u * u * bx;
      arr[i * 3 + 1] = omu * omu * ay + 2 * omu * u * cy + u * u * by;
      arr[i * 3 + 2] = omu * omu * az + 2 * omu * u * cz + u * u * bz;
    }
    free.geom.attributes.position.needsUpdate = true;
    free.t = 0;
    free.dur = 0.7 + Math.random() * 0.4;
  }

  /* ── 6. Soft halo plane behind brain ── */
  const haloGeo = new THREE.PlaneGeometry(20, 20);
  const haloMat = new THREE.ShaderMaterial({
    transparent: true,
    depthWrite: false,
    uniforms: { uAccent: { value: new THREE.Color(0x7cc4e8) }, uTime: { value: 0 } },
    vertexShader: `varying vec2 vUv; void main(){ vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);} `,
    fragmentShader: `
      varying vec2 vUv;
      uniform vec3 uAccent;
      uniform float uTime;
      void main(){
        vec2 p = vUv - 0.5;
        float d = length(p);
        float pulse = 0.5 + 0.5 * sin(uTime * 0.6);
        float a = smoothstep(0.42, 0.0, d) * (0.18 + 0.06 * pulse);
        gl_FragColor = vec4(uAccent, a);
      }
    `,
  });
  const halo = new THREE.Mesh(haloGeo, haloMat);
  halo.position.z = -3;
  scene.add(halo);

  /* ── Mouse parallax ── */
  const target = { x: 0, y: 0 };
  const onMove = (e) => {
    target.x = (e.clientX / window.innerWidth) * 2 - 1;
    target.y = (e.clientY / window.innerHeight) * 2 - 1;
  };
  window.addEventListener("pointermove", onMove);

  /* ── Track CSS accent ── */
  const updateAccents = () => {
    const cs = getComputedStyle(document.documentElement);
    const a1 = cs.getPropertyValue("--accent").trim();
    const a2 = cs.getPropertyValue("--accent-2").trim();
    if (a1) {
      neuronMat.uniforms.uAccent.value.set(a1);
      haloMat.uniforms.uAccent.value.set(a1);
      left.wire.material.color.set(a1);
      right.wire.material.color.set(a1);
      cereWire.material.color.set(a1);
    }
    if (a2) neuronMat.uniforms.uAccent2.value.set(a2);
  };
  updateAccents();
  const accentInt = setInterval(updateAccents, 1500);

  /* ── Loop ── */
  let smoothX = 0, smoothY = 0;
  let nextFire = 0.5;
  let firingRegion = -1;
  let fireStart = 0;
  let arcTimer = 0;

  const start = performance.now();
  let rafId;
  let visible = true;
  const onVis = () => { visible = !document.hidden; };
  document.addEventListener("visibilitychange", onVis);

  const tick = () => {
    rafId = requestAnimationFrame(tick);
    if (!visible) return;
    const elapsed = (performance.now() - start) / 1000;

    smoothX += (target.x - smoothX) * 0.05;
    smoothY += (target.y - smoothY) * 0.05;
    // Slow rotation around vertical axis + slight tilt towards top-down view
    brainGroup.rotation.y = elapsed * 0.10 + smoothX * 0.5;
    brainGroup.rotation.x = -0.30 + smoothY * 0.18;

    neuronMat.uniforms.uTime.value = elapsed;
    haloMat.uniforms.uTime.value = elapsed;

    if (elapsed > nextFire) {
      firingRegion = Math.floor(Math.random() * 6);
      fireStart = elapsed;
      nextFire = elapsed + 0.7 + Math.random() * 0.5;
      neuronMat.uniforms.uFireRegion.value = firingRegion;
    }
    const fireAge = elapsed - fireStart;
    let fireT = 0;
    if (fireAge < 0.85) {
      const u = fireAge / 0.85;
      fireT = u < 0.12 ? u / 0.12 : Math.pow(1 - (u - 0.12) / 0.88, 1.5);
    } else {
      neuronMat.uniforms.uFireRegion.value = -1;
    }
    neuronMat.uniforms.uFireT.value = fireT;

    arcTimer += 1 / 60;
    if (arcTimer > 0.32) {
      arcTimer = 0;
      fireArc(elapsed);
    }

    for (const a of arcs) {
      if (a.t >= 1) { a.mat.opacity = 0; continue; }
      a.t = Math.min(1, a.t + (1 / a.dur) * (1 / 60));
      const env = a.t < 0.4 ? a.t / 0.4 : (1 - a.t) / 0.6;
      a.mat.opacity = 0.95 * Math.max(0, env);
    }

    renderer.render(scene, camera);
  };
  tick();
  onReady && onReady();

  /* ── Resize ── */
  const onResize = () => {
    const W = mount.clientWidth, H = mount.clientHeight;
    if (!W || !H) return;
    camera.aspect = W / H;
    camera.updateProjectionMatrix();
    renderer.setSize(W, H);
  };
  const ro = new ResizeObserver(onResize);
  ro.observe(mount);

  return {
    dispose() {
      cancelAnimationFrame(rafId);
      clearInterval(accentInt);
      ro.disconnect();
      document.removeEventListener("visibilitychange", onVis);
      window.removeEventListener("pointermove", onMove);
      try { mount.removeChild(renderer.domElement); } catch (e) {}
      [left, right].forEach(({ geo, wire, fill, fillMat }) => {
        geo.dispose(); wire.geometry.dispose(); wire.material.dispose();
        fill.geometry.dispose(); fillMat.dispose();
      });
      cereGeo.dispose(); cereWire.geometry.dispose(); cereWire.material.dispose(); cereFillMat.dispose();
      stemGeo.dispose(); stemMat.dispose();
      neuronGeo.dispose(); neuronMat.dispose();
      haloGeo.dispose(); haloMat.dispose();
      arcs.forEach((a) => { a.geom.dispose(); a.mat.dispose(); });
      renderer.dispose();
    },
  };
}

/* ── HUD ─────────────────────────────────────────────────────────── */

function BrainwaveLine() {
  const pts = React.useMemo(() => {
    const arr = [];
    for (let i = 0; i <= 40; i++) {
      const x = i * 2;
      let y = 6;
      if (i % 11 === 5) y -= 5;
      else if (i % 11 === 6) y += 4;
      else if (i % 11 === 7) y -= 2;
      else y += Math.sin(i * 0.6) * 1.2;
      arr.push(`${i === 0 ? "M" : "L"} ${x} ${y.toFixed(2)}`);
    }
    return arr.join(" ");
  }, []);
  return (
    <svg viewBox="0 0 80 12" width="60" height="12" className="nh-wave">
      <nm.path d={pts} fill="none" stroke="var(--accent)" strokeWidth="0.6"
        initial={{ pathLength: 0, opacity: 0 }}
        animate={{ pathLength: [0, 1], opacity: [0, 1, 1, 0] }}
        transition={{ duration: 2.4, repeat: Infinity, ease: "easeInOut" }}/>
    </svg>
  );
}

function BrainHUD() {
  const [tps, setTps] = React.useState(2847);
  React.useEffect(() => {
    const t = setInterval(() => setTps(2700 + Math.floor(Math.random() * 400)), 900);
    return () => clearInterval(t);
  }, []);
  return (
    <>
      <div className="nh-hud nh-hud--tl">
        <span className="nh-hud-dot"/>
        <span>NEURAL · SYS / ACTIVE</span>
      </div>
      <div className="nh-hud nh-hud--tr">
        <span>280 NODES</span>
        <span className="nh-hud-sep">·</span>
        <span>6 REGIONS</span>
      </div>
      <div className="nh-hud nh-hud--bl">
        <span>fire/sec</span>
        <span className="nh-hud-v">{tps}</span>
      </div>
      <div className="nh-hud nh-hud--br">
        <span>signal</span>
        <BrainwaveLine/>
      </div>
    </>
  );
}

/* ── Hero ────────────────────────────────────────────────────────── */

function HeroNeural() {
  const { scrollY } = nuScroll();
  const y1 = nuTransform(scrollY, [0, 600], [0, 100]);
  const o1 = nuTransform(scrollY, [0, 500], [1, 0]);
  const yBrain = nuTransform(scrollY, [0, 800], [0, 120]);
  const oBrain = nuTransform(scrollY, [0, 600], [1, 0.1]);

  const [variant, setVariant] = React.useState(window.__brainVariant || "anatomical");
  React.useEffect(() => {
    const id = setInterval(() => {
      const v = window.__brainVariant || "anatomical";
      setVariant((cur) => (cur === v ? cur : v));
    }, 200);
    return () => clearInterval(id);
  }, []);

  return (
    <section id="home" className="hero hero--neural hero--neural-bg">
      <div className="nh-bg">
        <div className="nh-bg-grid"/>
        <div className="nh-bg-vignette"/>
      </div>

      <nm.div className="hero-neural-bg-canvas" style={{ y: yBrain, opacity: oBrain }}>
        {window.DendriteNetwork ? <window.DendriteNetwork/> : <BrainCanvas3D/>}
      </nm.div>

      <div className="hero-neural-fade"/>

      <nm.div className="hero-neural-center" style={{ y: y1, opacity: o1 }}>
        <NReveal y={8}>
          <div className="eyebrow eyebrow--centered">
            <span className="eyebrow-dot"/>
            Software Development Consulting · Victoria, BC
          </div>
        </NReveal>

        <h1 className="display display--neural">
          <span className="display-row">
            <NSplitText text="Griffin" delay={0.2}/>
            <span className="brand-accent"><NSplitText text="Mind" delay={0.2 + 7 * 0.03}/></span>
          </span>
          <span className="display-row display-row--lab">
            <NSplitText text="Labs" delay={0.7} stagger={0.05}/>
          </span>
        </h1>

        <NReveal delay={1.2}>
          <p className="hero-lede prose hero-lede--center">
            Transforming ambitious ideas into powerful digital realities. We architect
            cutting-edge solutions at the edge of blockchain, mobile, and web.
          </p>
        </NReveal>

        <NReveal delay={1.35}>
          <div className="cta-row cta-row--center">
            <NMagneticLink href="#contact" primary>
              Start your project <NIconArrow/>
            </NMagneticLink>
            <NMagneticLink href="#services">Explore services</NMagneticLink>
          </div>
        </NReveal>
      </nm.div>

      <nm.div className="hero-scroll"
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ delay: 2, duration: 1 }}>
        <span>Scroll</span>
        <nm.i animate={{ y: [0, 8, 0] }} transition={{ duration: 1.8, repeat: Infinity }}/>
      </nm.div>
    </section>
  );
}

Object.assign(window, { HeroNeural });
