// hero-3d.jsx — Cinematic scroll-triggered 3D hero
//
// A tall (400vh) section with a pinned canvas. As you scroll within the section,
// ~2200 particles morph through 3 shape states:
//
//   0%–35%   Stylized human silhouette (cyan)
//   35%–65%  Double-helix DNA, 3D rotation (blue → violet)
//   65%–100% Peptide chain — amino acid clusters connected by bonds (violet)
//
// Camera dollies forward, then pulls back. Color cycles. Dispersion noise
// peaks mid-transition for the "dissolve into particles" effect.
//
// Built with Three.js (loaded as global window.THREE before this file).

const { useEffect: useHeroEffect, useRef: useHeroRef, useState: useHeroState } = React;

function HeroSection3D({ navigate }) {
  const containerRef = useHeroRef(null);
  const canvasRef    = useHeroRef(null);
  const [p, setP]    = useHeroState(0);     // smoothed scroll progress 0..1

  useHeroEffect(() => {
    if (!window.THREE) {
      console.warn("Three.js not loaded yet");
      return;
    }
    const THREE = window.THREE;
    const canvas = canvasRef.current;
    const container = containerRef.current;
    if (!canvas || !container) return;

    // ── Renderer / Scene / Camera ─────────────────────────────────────────
    const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
    renderer.setClearColor(0x04060d, 1);

    const scene = new THREE.Scene();
    scene.fog = new THREE.FogExp2(0x04060d, 0.10);

    const camera = new THREE.PerspectiveCamera(55, 1, 0.1, 100);
    camera.position.set(0, 0, 4.0);

    // Horizontal offset magnitude — bigger on wide screens, none on mobile.
    let offsetMag = 0;
    function resize() {
      const w = container.clientWidth || window.innerWidth;
      const h = window.innerHeight;
      renderer.setSize(w, h, false);
      camera.aspect = w / h;
      camera.updateProjectionMatrix();
      offsetMag = w >= 1100 ? 1.45 : (w >= 768 ? 0.95 : 0);
    }
    resize();
    window.addEventListener("resize", resize);

    // ── Deterministic RNG ─────────────────────────────────────────────────
    let seedState = 123456;
    function srand() {
      seedState = (seedState * 1664525 + 1013904223) | 0;
      return ((seedState >>> 0) / 4294967296);
    }
    function rIn(a, b) { return a + (b - a) * srand(); }

    // ── Shape generators ──────────────────────────────────────────────────
    const N = window.innerWidth < 768 ? 1400 : 2200;

    function randInSphere(cx, cy, cz, r) {
      // rejection sampling
      while (true) {
        const x = (srand() * 2 - 1);
        const y = (srand() * 2 - 1);
        const z = (srand() * 2 - 1);
        if (x*x + y*y + z*z <= 1) return [cx + x*r, cy + y*r, cz + z*r];
      }
    }
    function randInCapsule(a, b, r) {
      const t = srand();
      const ax = a[0], ay = a[1], az = a[2];
      const bx = b[0], by = b[1], bz = b[2];
      const cx = ax + (bx - ax) * t;
      const cy = ay + (by - ay) * t;
      const cz = az + (bz - az) * t;
      // perpendicular disk offset
      const dx_ = bx - ax, dy_ = by - ay, dz_ = bz - az;
      const aLen = Math.hypot(dx_, dy_, dz_) || 1;
      const axn = dx_/aLen, ayn = dy_/aLen, azn = dz_/aLen;
      while (true) {
        const ox = srand()*2-1, oy = srand()*2-1, oz = srand()*2-1;
        if (ox*ox + oy*oy + oz*oz > 1) continue;
        const dot = ox*axn + oy*ayn + oz*azn;
        const px = ox - dot*axn, py = oy - dot*ayn, pz = oz - dot*azn;
        // scale to r
        return [cx + px*r, cy + py*r, cz + pz*r];
      }
    }

    // Hexagon cluster info for the line overlay — indices into the merged
    // start-state array (filled in by generateStartState).
    let hexClusterCount = 0;
    let humanIndexCount = 0;

    function generateChestMolecules(count) {
      // Hexagon molecule clusters distributed across the ENTIRE body silhouette
      // (head to feet, including arms) and biased forward in Z so they read as
      // "overlay molecules in front of the body". Each cluster:
      //   idx 0      = center atom
      //   idx 1..6   = six hexagon vertices (planar ring)
      //   idx 7      = one external substituent
      const out = [];
      const perCluster = 8;
      const clusters = Math.floor(count / perCluster);
      // Anchor positions distributed across the Vitruvian silhouette so the
      // hexagons hug the body shape (head, torso, arms, legs).
      const anchors = [
        // head + neck
        [0,    0.78, 0.10], [0,    0.82, 0.10],
        // shoulders + upper chest
        [-0.18, 0.55, 0.18], [0.18, 0.55, 0.18],
        [-0.10, 0.45, 0.20], [0.10, 0.45, 0.20], [0, 0.40, 0.22],
        // mid chest
        [-0.12, 0.30, 0.22], [0.12, 0.30, 0.22], [0, 0.32, 0.24],
        // abdomen
        [-0.10, 0.15, 0.20], [0.10, 0.15, 0.20], [0, 0.10, 0.22],
        // hips
        [-0.08, 0.00, 0.20], [0.08, 0.00, 0.20],
        // arms (Vitruvian outstretched)
        [-0.40, 0.55, 0.14], [0.40, 0.55, 0.14],
        [-0.65, 0.56, 0.14], [0.65, 0.56, 0.14],
        // thighs
        [-0.13, -0.20, 0.18], [0.13, -0.20, 0.18],
        // shins
        [-0.16, -0.55, 0.18], [0.16, -0.55, 0.18],
        // free-floating overlay points (between body parts)
        [-0.30, 0.30, 0.30], [0.30, 0.30, 0.30],
        [-0.50, 0.40, 0.28], [0.50, 0.40, 0.28],
        [0, 0.65, 0.28], [-0.25, 0.65, 0.25], [0.25, 0.65, 0.25],
      ];
      for (let c = 0; c < clusters; c++) {
        const anchor = anchors[c % anchors.length];
        // jitter the anchor a bit so clusters don't sit on identical points
        const jx = (srand() - 0.5) * 0.16;
        const jy = (srand() - 0.5) * 0.16;
        const jz = (srand() - 0.5) * 0.06;
        const cx = anchor[0] + jx;
        const cy = anchor[1] + jy;
        const cz = anchor[2] + jz;
        // Big, prominent hexagons — variation 0.13-0.20
        const size = 0.13 + srand() * 0.075;
        const rotZ = srand() * Math.PI * 2;
        // 0: center
        out.push([cx, cy, cz]);
        // 1..6: hexagon vertices
        for (let i = 0; i < 6; i++) {
          const a = rotZ + i * (Math.PI / 3);
          out.push([
            cx + Math.cos(a) * size,
            cy + Math.sin(a) * size,
            cz + (srand() - 0.5) * 0.02,
          ]);
        }
        // 7: external substituent — angled away from a vertex
        const v1Ang = rotZ;
        out.push([
          cx + Math.cos(v1Ang) * (size * 1.85),
          cy + Math.sin(v1Ang) * (size * 1.85),
          cz,
        ]);
      }
      while (out.length < count) out.push([0, 0, 0]);
      out.length = count;
      return { positions: out, perCluster, clusters };
    }

    function generateStartState(count) {
      // The opening look — human Vitruvian silhouette with a network of hexagon
      // molecule clusters floating in front of the chest. Matches the reference
      // imagery the user provided.
      const humanShare = 0.62;
      const N_human = Math.floor(count * humanShare);
      const N_mol = count - N_human;
      const humanPositions = generateHuman(N_human);
      const molData = generateChestMolecules(N_mol);
      const positions = humanPositions.concat(molData.positions);
      humanIndexCount = N_human;
      hexClusterCount = molData.clusters;
      return positions;
    }

    function generateHuman(count) {
      // Vitruvian-style pose: arms outstretched horizontally, legs slightly spread.
      // Iconic anatomy reference — reads as "human" instantly at any size.
      // Height ~1.8, width with arms ~1.7, centered at origin.
      const parts = [
        { w: 0.075, type: 'sphere', c: [0, 0.80, 0], r: 0.135 },                                 // head
        { w: 0.025, type: 'caps',   a: [0, 0.60, 0],  b: [0, 0.68, 0],  r: 0.048 },              // neck
        { w: 0.090, type: 'sphere', c: [0, 0.55, 0],  r: 0.22 },                                  // shoulders (wide)
        { w: 0.155, type: 'caps',   a: [0, 0.12, 0],  b: [0, 0.52, 0],  r: 0.18 },               // torso (tapered)
        { w: 0.035, type: 'sphere', c: [0, 0.10, 0],  r: 0.17 },                                  // hips
        // Arms — Vitruvian outstretched (horizontal)
        { w: 0.058, type: 'caps',   a: [-0.20, 0.52, 0], b: [-0.54, 0.55, 0], r: 0.062 },        // L upper arm
        { w: 0.058, type: 'caps',   a: [ 0.20, 0.52, 0], b: [ 0.54, 0.55, 0], r: 0.062 },        // R upper arm
        { w: 0.050, type: 'caps',   a: [-0.54, 0.55, 0], b: [-0.86, 0.58, 0], r: 0.052 },        // L forearm
        { w: 0.050, type: 'caps',   a: [ 0.54, 0.55, 0], b: [ 0.86, 0.58, 0], r: 0.052 },        // R forearm
        { w: 0.014, type: 'sphere', c: [-0.88, 0.58, 0], r: 0.055 },                              // L hand
        { w: 0.014, type: 'sphere', c: [ 0.88, 0.58, 0], r: 0.055 },                              // R hand
        // Legs — slightly V-shape spread
        { w: 0.105, type: 'caps',   a: [-0.10, 0.06, 0], b: [-0.16, -0.40, 0], r: 0.093 },       // L thigh
        { w: 0.105, type: 'caps',   a: [ 0.10, 0.06, 0], b: [ 0.16, -0.40, 0], r: 0.093 },       // R thigh
        { w: 0.085, type: 'caps',   a: [-0.16, -0.40, 0], b: [-0.20, -0.86, 0], r: 0.062 },      // L shin
        { w: 0.085, type: 'caps',   a: [ 0.16, -0.40, 0], b: [ 0.20, -0.86, 0], r: 0.062 },      // R shin
        { w: 0.008, type: 'sphere', c: [-0.21, -0.88, 0], r: 0.05 },                              // L foot
        { w: 0.008, type: 'sphere', c: [ 0.21, -0.88, 0], r: 0.05 },                              // R foot
      ];
      const totalW = parts.reduce((s, p) => s + p.w, 0);
      const out = [];
      for (const part of parts) {
        const n = Math.round((part.w / totalW) * count);
        for (let i = 0; i < n; i++) {
          if (part.type === 'sphere') out.push(randInSphere(part.c[0], part.c[1], part.c[2], part.r));
          else out.push(randInCapsule(part.a, part.b, part.r));
        }
      }
      while (out.length < count) out.push([0, 0, 0]);
      out.length = count;
      return out;
    }

    function generateDNA(count) {
      // Two strands; particles ordered (a0,b0,a1,b1,...)
      const radius = 0.45;
      const height = 1.8;
      const turns = 4.5;
      const pairs = Math.floor(count / 2);
      const out = [];
      for (let i = 0; i < pairs; i++) {
        const u = i / (pairs - 1);
        const y = -height/2 + u * height;
        const ang0 = u * turns * Math.PI * 2;
        const x0 = Math.cos(ang0) * radius;
        const z0 = Math.sin(ang0) * radius;
        const x1 = Math.cos(ang0 + Math.PI) * radius;
        const z1 = Math.sin(ang0 + Math.PI) * radius;
        out.push([x0, y, z0]);
        out.push([x1, y, z1]);
      }
      while (out.length < count) out.push([0, 0, 0]);
      return out;
    }

    function generatePeptide(count) {
      // Curved chain of amino acid clusters with side branches
      const aas = 26;
      const perAA = Math.floor(count / aas);
      const out = [];
      const aaCenters = [];
      for (let a = 0; a < aas; a++) {
        const t = a / (aas - 1);
        // Curved zigzag in 3D
        const x = -1.5 + t * 3.0;
        const y = Math.sin(t * Math.PI * 5.5) * 0.20 + Math.sin(t * Math.PI * 1.5) * 0.05;
        const z = Math.cos(t * Math.PI * 4.5) * 0.18;
        aaCenters.push([x, y, z]);
        // cluster around center; some forming a "side chain" stub
        for (let k = 0; k < perAA; k++) {
          if (k < perAA * 0.55) {
            // backbone-ish, tight cluster
            const p = randInSphere(x, y, z, 0.07);
            out.push(p);
          } else {
            // side chain — offset perpendicular
            const side = (a % 2 === 0 ? 1 : -1);
            const off = 0.12 + srand() * 0.10;
            const px = x + (srand() - 0.5) * 0.05;
            const py = y + side * off + (srand() - 0.5) * 0.06;
            const pz = z + (srand() - 0.5) * 0.05;
            out.push([px, py, pz]);
          }
        }
      }
      while (out.length < count) out.push(aaCenters[0] || [0,0,0]);
      out.length = count;
      return { positions: out, centers: aaCenters };
    }

    function generateChaosOffsets(count) {
      const out = [];
      for (let i = 0; i < count; i++) {
        // random spherical offset
        const r = 0.4 + srand() * 1.0;
        const phi = srand() * Math.PI * 2;
        const cosTh = srand() * 2 - 1;
        const sinTh = Math.sqrt(Math.max(0, 1 - cosTh*cosTh));
        out.push([Math.cos(phi)*sinTh*r, cosTh*r, Math.sin(phi)*sinTh*r]);
      }
      return out;
    }

    // Seed deterministic, generate all.
    // Human Vitruvian + chest hexagon molecules form the opening composite.
    seedState = 7711;
    const startPos   = generateStartState(N);
    seedState = 4242;
    const dnaPos     = generateDNA(N);
    seedState = 9988;
    const pep        = generatePeptide(N);
    const peptidePos = pep.positions;
    const peptideCenters = pep.centers;
    seedState = 5555;
    const chaosOff   = generateChaosOffsets(N);
    const N_human    = humanIndexCount;
    const HEX_CL     = hexClusterCount;
    const HEX_PER    = 8;

    // ── Particle texture (soft circular) ──────────────────────────────────
    function makeSoftSprite() {
      const c = document.createElement('canvas');
      c.width = c.height = 128;
      const ctx = c.getContext('2d');
      const g = ctx.createRadialGradient(64, 64, 0, 64, 64, 64);
      g.addColorStop(0.00, 'rgba(255,255,255,1.0)');
      g.addColorStop(0.18, 'rgba(255,255,255,0.85)');
      g.addColorStop(0.40, 'rgba(255,255,255,0.28)');
      g.addColorStop(1.00, 'rgba(255,255,255,0.0)');
      ctx.fillStyle = g;
      ctx.fillRect(0, 0, 128, 128);
      const tex = new THREE.CanvasTexture(c);
      tex.needsUpdate = true;
      return tex;
    }
    const sprite = makeSoftSprite();

    // ── Main particle system ──────────────────────────────────────────────
    const geom = new THREE.BufferGeometry();
    const positions = new Float32Array(N * 3);
    // seed initial = start state
    for (let i = 0; i < N; i++) {
      positions[3*i]   = startPos[i][0];
      positions[3*i+1] = startPos[i][1];
      positions[3*i+2] = startPos[i][2];
    }
    geom.setAttribute('position', new THREE.BufferAttribute(positions, 3));

    const mat = new THREE.PointsMaterial({
      size: 0.05,
      sizeAttenuation: true,
      color: 0x8cdfff,
      transparent: true,
      opacity: 0.95,
      map: sprite,
      alphaMap: sprite,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const points = new THREE.Points(geom, mat);
    scene.add(points);

    // ── Atmospheric particles (slow drift, background motes) ──────────────
    const atmosN = 700;
    const atmosGeom = new THREE.BufferGeometry();
    const atmosPos = new Float32Array(atmosN * 3);
    for (let i = 0; i < atmosN; i++) {
      atmosPos[3*i]   = (Math.random() - 0.5) * 14;
      atmosPos[3*i+1] = (Math.random() - 0.5) * 9;
      atmosPos[3*i+2] = (Math.random() - 0.5) * 9 - 1.5;
    }
    atmosGeom.setAttribute('position', new THREE.BufferAttribute(atmosPos, 3));
    const atmosMat = new THREE.PointsMaterial({
      size: 0.025,
      sizeAttenuation: true,
      color: 0x6080cc,
      transparent: true,
      opacity: 0.40,
      map: sprite,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const atmos = new THREE.Points(atmosGeom, atmosMat);
    scene.add(atmos);

    // ── Ambient floating molecules — hexagonal structures matching the brand
    // logo aesthetic. Each molecule is either a single hexagon (with 0-3
    // substituent stubs) or a dual fused hexagon (logo-style bicyclic).
    const ambientCount = 22;
    const ambientPerCluster = 12;      // up to 12 slots per molecule
    const ambientAnchors = [];
    {
      const candidates = [
        [-2.30,  0.65,  0.10], [-1.85,  0.10,  0.20], [-2.10, -0.45,  0.05],
        [-1.55,  0.85, -0.10], [-1.30,  0.20,  0.30], [-1.65, -0.55,  0.20],
        [-1.10, -0.10, -0.15], [-2.05, -0.95,  0.10],
        [-0.70,  1.05,  0.10], [ 0.85,  1.10, -0.05], [ 1.50,  0.85,  0.15],
        [ 1.60, -0.25, -0.30], [ 1.95,  0.35, -0.20], [ 1.20, -0.95,  0.10],
        [-0.40, -1.10,  0.15], [ 0.55, -1.10, -0.10],
        [-0.30,  1.05, -0.45], [ 0.30, -0.30, -0.50],
        [-2.40, -0.10, -0.10], [-1.05,  1.05, -0.20],
        [ 2.20, -0.60,  0.05], [ 0.20,  1.30,  0.20],
      ];
      for (let i = 0; i < ambientCount; i++) {
        const c = candidates[i % candidates.length];
        // ~35% dual hex (logo-style), rest are single hex with substituents
        const isDual = srand() < 0.35;
        const subCount = isDual ? 0 : Math.floor(srand() * 4);   // 0-3
        ambientAnchors.push({
          cx: c[0], cy: c[1], cz: c[2],
          size:  isDual ? 0.07 + srand() * 0.04 : 0.09 + srand() * 0.06,
          rotZ:  srand() * Math.PI * 2,
          phase: srand() * Math.PI * 2,
          driftAmp:   0.04 + srand() * 0.05,
          driftSpeed: 0.00035 + srand() * 0.00045,
          spinSpeed:  (srand() - 0.5) * 0.0008,
          isDual,
          subCount,
        });
      }
    }
    const AMB_MAX_EDGES = 12;
    const ambientNParticles = ambientCount * ambientPerCluster;
    const ambientPosArr = new Float32Array(ambientNParticles * 3);
    const ambientGeom   = new THREE.BufferGeometry();
    ambientGeom.setAttribute('position', new THREE.BufferAttribute(ambientPosArr, 3));
    const ambientMat = new THREE.PointsMaterial({
      size: 0.038,
      sizeAttenuation: true,
      map: sprite,
      alphaMap: sprite,
      color: 0x8cdfff,
      transparent: true,
      opacity: 0,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const ambientPoints = new THREE.Points(ambientGeom, ambientMat);
    ambientPoints.frustumCulled = false;
    scene.add(ambientPoints);

    // Bond lines per molecule (max edges padded with degenerates)
    const ambientLinesArr = new Float32Array(ambientCount * AMB_MAX_EDGES * 6);
    const ambientLinesGeom = new THREE.BufferGeometry();
    ambientLinesGeom.setAttribute('position', new THREE.BufferAttribute(ambientLinesArr, 3));
    const ambientLinesMat = new THREE.LineBasicMaterial({
      color: 0x8cdfff,
      transparent: true,
      opacity: 0,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const ambientLines = new THREE.LineSegments(ambientLinesGeom, ambientLinesMat);
    ambientLines.frustumCulled = false;
    scene.add(ambientLines);

    // ── Lines for DNA backbone + base pairs ───────────────────────────────
    // Strand A
    function buildStrandLine(stride0, count) {
      const arr = new Float32Array(count * 3);
      const g = new THREE.BufferGeometry();
      g.setAttribute('position', new THREE.BufferAttribute(arr, 3));
      const m = new THREE.LineBasicMaterial({
        color: 0x8cb8ff,
        transparent: true,
        opacity: 0,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
      });
      const l = new THREE.Line(g, m);
      scene.add(l);
      return l;
    }
    const pairs = Math.floor(N / 2);
    const strandA = buildStrandLine(0, pairs);
    const strandB = buildStrandLine(1, pairs);

    // Base pair rungs — line segments (every other pair)
    const rungCount = Math.floor(pairs / 2);
    const rungArr = new Float32Array(rungCount * 6);
    const rungGeom = new THREE.BufferGeometry();
    rungGeom.setAttribute('position', new THREE.BufferAttribute(rungArr, 3));
    const rungMat = new THREE.LineBasicMaterial({
      color: 0xa0a0ff,
      transparent: true,
      opacity: 0,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const rungs = new THREE.LineSegments(rungGeom, rungMat);
    scene.add(rungs);

    // ── Peptide bonds — lines between amino acid centers ──────────────────
    const pepBondsArr = new Float32Array((peptideCenters.length - 1) * 6);
    const pepBondsGeom = new THREE.BufferGeometry();
    pepBondsGeom.setAttribute('position', new THREE.BufferAttribute(pepBondsArr, 3));
    const pepBondsMat = new THREE.LineBasicMaterial({
      color: 0xc090ff,
      transparent: true,
      opacity: 0,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const pepBonds = new THREE.LineSegments(pepBondsGeom, pepBondsMat);
    scene.add(pepBonds);

    // ── Hexagon overlay — bonds tracing the chest molecule clusters.
    // Visible during the start state, fades as we leave it.
    // 7 segments per cluster: 6 hexagon edges + 1 substituent stub.
    const hexEdgesPerCluster = 7;
    const hexLinesArr = new Float32Array(HEX_CL * hexEdgesPerCluster * 6);
    const hexLinesGeom = new THREE.BufferGeometry();
    hexLinesGeom.setAttribute('position', new THREE.BufferAttribute(hexLinesArr, 3));
    const hexLinesMat = new THREE.LineBasicMaterial({
      color: 0x9adfff,
      transparent: true,
      opacity: 0,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const hexLines = new THREE.LineSegments(hexLinesGeom, hexLinesMat);
    hexLines.frustumCulled = false;
    scene.add(hexLines);

    // Disable frustum culling on the dynamic line objects so Three.js doesn't
    // try to compute bounding spheres from initial-zero buffers.
    strandA.frustumCulled = false;
    strandB.frustumCulled = false;
    rungs.frustumCulled = false;
    pepBonds.frustumCulled = false;
    points.frustumCulled = false;

    // ── Helpers ───────────────────────────────────────────────────────────
    const clamp = (x, a, b) => Math.max(a, Math.min(b, x));
    const smoothstep = (a, b, x) => {
      const t = clamp((x - a) / (b - a), 0, 1);
      return t * t * (3 - 2 * t);
    };
    function lerpColor(c1, c2, t) {
      return [
        c1[0] + (c2[0] - c1[0]) * t,
        c1[1] + (c2[1] - c1[1]) * t,
        c1[2] + (c2[2] - c1[2]) * t,
      ];
    }
    const colHuman   = [0x7a/255, 0xe0/255, 0xff/255]; // cyan
    const colDNA     = [0x8c/255, 0xb8/255, 0xff/255]; // blue
    const colPeptide = [0xc0/255, 0x90/255, 0xff/255]; // violet

    // ── Scroll progress ───────────────────────────────────────────────────
    let target = 0, sm = 0;
    function onScroll() {
      const rect = container.getBoundingClientRect();
      const total = container.offsetHeight - window.innerHeight;
      const scrolled = -rect.top;
      target = clamp(total > 0 ? scrolled / total : 0, 0, 1);
    }
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });

    // ── Animation loop ────────────────────────────────────────────────────
    let raf;
    let lastT = performance.now();
    let timeMs = 0;

    function animate(now) {
      const dt = Math.min(48, now - lastT);
      lastT = now;
      timeMs += dt;

      sm += (target - sm) * 0.085;
      const prog = sm;

      // ── Shape weights ────────────────────────────────────────────────────
      // 0.00–0.15  start state (body + molecules)
      // 0.15–0.35  start → DNA (shorter transition)
      // 0.35–0.65  DNA stable — long hold so the helix is the centerpiece
      // 0.65–0.85  DNA → peptide
      // 0.85+      peptide stable
      let ws = 0, wd = 0, wp = 0;
      if (prog < 0.15) {
        ws = 1;
      } else if (prog < 0.35) {
        const t = (prog - 0.15) / 0.20;
        const e = t * t * (3 - 2 * t);
        ws = 1 - e;
        wd = e;
      } else if (prog < 0.65) {
        wd = 1;
      } else if (prog < 0.85) {
        const t = (prog - 0.65) / 0.20;
        const e = t * t * (3 - 2 * t);
        wd = 1 - e;
        wp = e;
      } else {
        wp = 1;
      }

      // Dispersion (chaos offset) — Gaussian peaks at each transition midpoint.
      function gauss(x, mu, s, h) { return Math.exp(-((x - mu) ** 2) / s) * h; }
      const disp =
        Math.max(
          gauss(prog, 0.25, 0.008, 0.45),   // start → DNA midpoint
          gauss(prog, 0.75, 0.008, 0.32),   // DNA → peptide midpoint
        );

      // Write positions: weighted blend of 3 shapes + chaos
      const posArr = geom.attributes.position.array;
      for (let i = 0; i < N; i++) {
        const s_ = startPos[i], d = dnaPos[i], p_ = peptidePos[i];
        const ox = chaosOff[i][0] * disp;
        const oy = chaosOff[i][1] * disp;
        const oz = chaosOff[i][2] * disp;
        posArr[3*i    ] = s_[0]*ws + d[0]*wd + p_[0]*wp + ox;
        posArr[3*i + 1] = s_[1]*ws + d[1]*wd + p_[1]*wp + oy;
        posArr[3*i + 2] = s_[2]*ws + d[2]*wd + p_[2]*wp + oz;
      }
      geom.attributes.position.needsUpdate = true;

      // Strand & rung lines (visible when wd dominates)
      const dnaMix = wd;
      strandA.material.opacity = dnaMix * 0.55;
      strandB.material.opacity = dnaMix * 0.55;
      rungs.material.opacity   = dnaMix * 0.35;

      if (dnaMix > 0.02) {
        const sA = strandA.geometry.attributes.position.array;
        const sB = strandB.geometry.attributes.position.array;
        for (let pi = 0; pi < pairs; pi++) {
          sA[pi*3]     = posArr[(pi*2)*3];
          sA[pi*3 + 1] = posArr[(pi*2)*3 + 1];
          sA[pi*3 + 2] = posArr[(pi*2)*3 + 2];
          sB[pi*3]     = posArr[(pi*2 + 1)*3];
          sB[pi*3 + 1] = posArr[(pi*2 + 1)*3 + 1];
          sB[pi*3 + 2] = posArr[(pi*2 + 1)*3 + 2];
        }
        strandA.geometry.attributes.position.needsUpdate = true;
        strandB.geometry.attributes.position.needsUpdate = true;
        // rungs every other pair
        const rA = rungs.geometry.attributes.position.array;
        let r = 0;
        for (let pi = 0; pi < pairs; pi += 2) {
          if (r >= rungCount * 6) break;
          rA[r++] = posArr[(pi*2)*3];
          rA[r++] = posArr[(pi*2)*3 + 1];
          rA[r++] = posArr[(pi*2)*3 + 2];
          rA[r++] = posArr[(pi*2 + 1)*3];
          rA[r++] = posArr[(pi*2 + 1)*3 + 1];
          rA[r++] = posArr[(pi*2 + 1)*3 + 2];
        }
        rungs.geometry.attributes.position.needsUpdate = true;
      }

      // Peptide bonds (visible when wp dominates)
      const pepMix = wp;
      pepBonds.material.opacity = pepMix * 0.55;

      // Hexagon overlay — visible during start state, prominent
      hexLines.material.opacity = ws * 0.85;
      if (ws > 0.02) {
        const ha = hexLines.geometry.attributes.position.array;
        let li = 0;
        for (let k = 0; k < HEX_CL; k++) {
          const base = (N_human + k * HEX_PER) * 3;
          // 6 hexagon edges
          for (let e = 0; e < 6; e++) {
            const aOff = base + (1 + e) * 3;
            const bOff = base + (1 + ((e + 1) % 6)) * 3;
            ha[li++] = posArr[aOff];
            ha[li++] = posArr[aOff + 1];
            ha[li++] = posArr[aOff + 2];
            ha[li++] = posArr[bOff];
            ha[li++] = posArr[bOff + 1];
            ha[li++] = posArr[bOff + 2];
          }
          // substituent stub from vertex 0 to substituent (idx 7)
          const vOff = base + 1 * 3;
          const sOff = base + 7 * 3;
          ha[li++] = posArr[vOff];
          ha[li++] = posArr[vOff + 1];
          ha[li++] = posArr[vOff + 2];
          ha[li++] = posArr[sOff];
          ha[li++] = posArr[sOff + 1];
          ha[li++] = posArr[sOff + 2];
        }
        hexLines.geometry.attributes.position.needsUpdate = true;
      }

      // ── Ambient floating molecules — shape variety + drift, fade with ws ──
      const ambOp = ws;
      ambientPoints.material.opacity = ambOp * 0.85;
      ambientLines.material.opacity  = ambOp * 0.55;
      if (ambOp > 0.02) {
        const ap = ambientPosArr;
        const al = ambientLinesArr;
        let lineIdx = 0;
        for (let c = 0; c < ambientCount; c++) {
          const a = ambientAnchors[c];
          const tt = timeMs * a.driftSpeed + a.phase;
          const dx = Math.sin(tt) * a.driftAmp;
          const dy = Math.cos(tt * 0.7 + a.phase * 0.5) * a.driftAmp * 0.7;
          const dz = Math.sin(tt * 1.1 + a.phase * 0.3) * a.driftAmp * 0.5;
          const cx = a.cx + dx;
          const cy = a.cy + dy;
          const cz = a.cz + dz;
          const rot = a.rotZ + timeMs * a.spinSpeed;
          const r = a.size;
          const baseI = c * ambientPerCluster;

          // Per-shape: hex-based molecules matching the brand logo
          const slots = new Array(ambientPerCluster);
          const edges = [];
          if (a.isDual) {
            // Two hexagons offset on local X by ±0.85*r, then rotated by `rot`.
            // Matches the logo's overlapping bicyclic look.
            const cosR = Math.cos(rot), sinR = Math.sin(rot);
            const off = r * 0.85;
            for (let h = 0; h < 2; h++) {
              const ox = h === 0 ? -off : off;
              for (let i = 0; i < 6; i++) {
                const ang = i * (Math.PI / 3);
                const lx = ox + Math.cos(ang) * r;
                const ly = Math.sin(ang) * r;
                slots[h * 6 + i] = [
                  cx + lx * cosR - ly * sinR,
                  cy + lx * sinR + ly * cosR,
                  cz,
                ];
              }
            }
            // 6 edges per ring
            for (let i = 0; i < 6; i++) edges.push([i, (i + 1) % 6]);
            for (let i = 0; i < 6; i++) edges.push([6 + i, 6 + ((i + 1) % 6)]);
          } else {
            // Single hexagon at slots 0..5, substituents at slots 6..8
            for (let i = 0; i < 6; i++) {
              const ang = rot + i * (Math.PI / 3);
              slots[i] = [cx + Math.cos(ang) * r, cy + Math.sin(ang) * r, cz];
            }
            // up to 3 substituents — placed on alternating vertices (0, 2, 4)
            for (let i = 0; i < 3; i++) {
              if (i < a.subCount) {
                const v = i * 2;
                const ang = rot + v * (Math.PI / 3);
                slots[6 + i] = [
                  cx + Math.cos(ang) * r * 1.75,
                  cy + Math.sin(ang) * r * 1.75,
                  cz,
                ];
                edges.push([v, 6 + i]);
              } else {
                slots[6 + i] = [cx, cy, cz];
              }
            }
            // collapse the unused tail slots
            for (let i = 9; i < ambientPerCluster; i++) slots[i] = [cx, cy, cz];
            // 6 ring edges
            for (let i = 0; i < 6; i++) edges.push([i, (i + 1) % 6]);
          }

          // Write positions
          for (let i = 0; i < ambientPerCluster; i++) {
            if (!slots[i]) slots[i] = [cx, cy, cz];
            const idx = (baseI + i) * 3;
            ap[idx]     = slots[i][0];
            ap[idx + 1] = slots[i][1];
            ap[idx + 2] = slots[i][2];
          }
          // Write up to AMB_MAX_EDGES edges; pad with degenerate
          for (let e = 0; e < AMB_MAX_EDGES; e++) {
            if (e < edges.length) {
              const [i1, i2] = edges[e];
              const p1 = slots[i1], p2 = slots[i2];
              al[lineIdx++] = p1[0]; al[lineIdx++] = p1[1]; al[lineIdx++] = p1[2];
              al[lineIdx++] = p2[0]; al[lineIdx++] = p2[1]; al[lineIdx++] = p2[2];
            } else {
              al[lineIdx++] = cx; al[lineIdx++] = cy; al[lineIdx++] = cz;
              al[lineIdx++] = cx; al[lineIdx++] = cy; al[lineIdx++] = cz;
            }
          }
        }
        ambientGeom.attributes.position.needsUpdate = true;
        ambientLines.geometry.attributes.position.needsUpdate = true;
      }
      if (pepMix > 0.02) {
        const bondArr = pepBonds.geometry.attributes.position.array;
        for (let a = 0; a < peptideCenters.length - 1; a++) {
          const c1 = peptideCenters[a];
          const c2 = peptideCenters[a + 1];
          bondArr[a*6 + 0] = c1[0]; bondArr[a*6 + 1] = c1[1]; bondArr[a*6 + 2] = c1[2];
          bondArr[a*6 + 3] = c2[0]; bondArr[a*6 + 4] = c2[1]; bondArr[a*6 + 5] = c2[2];
        }
        pepBonds.geometry.attributes.position.needsUpdate = true;
      }

      // Rotation: combine slow continuous spin + scroll-tied rotation.
      // During start state (0–0.15) the figure stays still and readable.
      const rotEarly = smoothstep(0.15, 0.40, prog);
      const baseRot = timeMs * 0.00018 * rotEarly;
      const rotBoost = smoothstep(0.30, 0.85, prog) * Math.PI * 1.8;
      points.rotation.y = baseRot + rotBoost;
      points.rotation.x = Math.sin(timeMs * 0.00009) * 0.05 * rotEarly + (prog - 0.5) * 0.15;
      strandA.rotation.copy(points.rotation);
      strandB.rotation.copy(points.rotation);
      rungs.rotation.copy(points.rotation);
      pepBonds.rotation.copy(points.rotation);
      hexLines.rotation.copy(points.rotation);
      atmos.rotation.y = baseRot * 0.25;

      // Subtle breathing during start state
      const breathe = (1 - rotEarly) * Math.sin(timeMs * 0.0011) * 0.014;
      points.position.y = breathe;
      strandA.position.y = breathe;
      strandB.position.y = breathe;
      rungs.position.y = breathe;
      pepBonds.position.y = breathe;
      hexLines.position.y = breathe;

      // Horizontal offset: start state → right (clear of left text), DNA → left
      // (held during the long DNA stable phase), peptide → centered.
      let offX;
      if (prog < 0.15) {
        offX = offsetMag;
      } else if (prog < 0.35) {
        const t = (prog - 0.15) / 0.20;
        offX = offsetMag * (1 - 2 * smoothstep(0, 1, t));   // +mag → -mag
      } else if (prog < 0.65) {
        offX = -offsetMag;                                   // hold left during DNA
      } else if (prog < 0.85) {
        const t = (prog - 0.65) / 0.20;
        offX = offsetMag * (-1 + smoothstep(0, 1, t));      // -mag → 0
      } else {
        offX = 0;
      }
      points.position.x  = offX;
      strandA.position.x = offX;
      strandB.position.x = offX;
      rungs.position.x   = offX;
      pepBonds.position.x = offX;
      hexLines.position.x = offX;

      // Camera dolly — forward then pull back
      // 0   → z=4.0
      // 0.5 → z=1.9 (closest, inside DNA)
      // 1.0 → z=2.6
      let camZ;
      if (prog < 0.5) camZ = 4.0 - prog * 4.2;
      else camZ = 1.9 + (prog - 0.5) * 1.4;
      camera.position.z = camZ;
      camera.position.x = Math.sin(timeMs * 0.00025) * 0.06;
      camera.position.y = Math.cos(timeMs * 0.00018) * 0.05 - (prog - 0.5) * 0.05;
      camera.lookAt(0, 0, 0);

      // Color blend — start state cyan; DNA blue; peptide violet.
      const totalW = Math.max(0.0001, ws + wd + wp);
      const colStart = colHuman;
      const r = (colStart[0]*ws + colDNA[0]*wd + colPeptide[0]*wp) / totalW;
      const g = (colStart[1]*ws + colDNA[1]*wd + colPeptide[1]*wp) / totalW;
      const b = (colStart[2]*ws + colDNA[2]*wd + colPeptide[2]*wp) / totalW;
      mat.color.setRGB(r, g, b);

      // Size pulsing per stage (smaller mid-zoom, larger when close)
      mat.size = 0.038 + Math.sin(prog * Math.PI) * 0.020;
      // particles dim slightly during chaotic mid-transition
      mat.opacity = 0.95 - disp * 0.20;

      // Update lines colors too
      strandA.material.color.setRGB(r, g, b);
      strandB.material.color.setRGB(r, g, b);
      rungs.material.color.setRGB(Math.min(1, r * 1.05), Math.min(1, g * 1.05), Math.min(1, b * 1.1));
      pepBonds.material.color.setRGB(r, g, b);
      hexLines.material.color.setRGB(Math.min(1, r * 1.10), Math.min(1, g * 1.10), Math.min(1, b * 1.15));

      renderer.render(scene, camera);
      setP(prog);

      raf = requestAnimationFrame(animate);
    }
    raf = requestAnimationFrame(animate);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", resize);
      sprite.dispose();
      geom.dispose();
      mat.dispose();
      atmosGeom.dispose();
      atmosMat.dispose();
      strandA.geometry.dispose();
      strandA.material.dispose();
      strandB.geometry.dispose();
      strandB.material.dispose();
      rungs.geometry.dispose();
      rungs.material.dispose();
      pepBonds.geometry.dispose();
      pepBonds.material.dispose();
      hexLinesGeom.dispose();
      hexLinesMat.dispose();
      ambientGeom.dispose();
      ambientMat.dispose();
      ambientLinesGeom.dispose();
      ambientLinesMat.dispose();
      renderer.dispose();
    };
  }, []);

  return (
    <section
      ref={containerRef}
      data-screen-label="Hero · 3D scroll"
      style={{
        height: "400vh",
        position: "relative",
        background: "#04060d",
        color: "#fff",
      }}
    >
      <div style={{
        position: "sticky",
        top: 0,
        width: "100%",
        height: "100vh",
        overflow: "hidden",
      }}>
        <canvas ref={canvasRef} style={{
          position: "absolute",
          inset: 0,
          width: "100%",
          height: "100%",
          display: "block",
        }} />

        {/* Vignette */}
        <div style={{
          position: "absolute",
          inset: 0,
          background:
            "radial-gradient(ellipse at center, rgba(2,4,10,0) 35%, rgba(2,4,10,0.55) 75%, rgba(2,4,10,0.95) 100%)",
          pointerEvents: "none",
        }} />

        {/* Top fade so chrome stays readable */}
        <div style={{
          position: "absolute",
          top: 0, left: 0, right: 0, height: 160,
          background: "linear-gradient(to bottom, rgba(2,4,10,0.6) 0%, rgba(2,4,10,0) 100%)",
          pointerEvents: "none",
        }} />
        {/* Bottom fade → transition to next section */}
        <div style={{
          position: "absolute",
          bottom: 0, left: 0, right: 0, height: 200,
          background: "linear-gradient(to bottom, rgba(2,4,10,0) 0%, rgba(2,4,10,0.95) 100%)",
          pointerEvents: "none",
        }} />

        {/* Text overlays */}
        <HeroOverlays progress={p} navigate={navigate} />

        {/* Progress indicator */}
        <HeroProgressBar progress={p} />
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
function HeroOverlays({ progress, navigate }) {
  const clamp = (x, a, b) => Math.max(a, Math.min(b, x));
  // Window helper
  const win = (a, b, fadeIn = 0.06, fadeOut = 0.06) => {
    if (progress < a - fadeIn || progress > b + fadeOut) return 0;
    if (progress < a) return (progress - (a - fadeIn)) / fadeIn;
    if (progress > b) return 1 - (progress - b) / fadeOut;
    return 1;
  };

  const stage1 = win(0.00, 0.14, 0.0, 0.08);   // start (body + molecules)
  const stage2 = win(0.40, 0.62, 0.08, 0.08);  // DNA (during stable hold)
  const stage3 = win(0.85, 1.00, 0.08, 0.0);   // peptide

  return (
    <div style={{
      position: "absolute",
      inset: 0,
      display: "flex",
      alignItems: "center",
      pointerEvents: "none",
    }}>
      <div className="container" style={{ width: "100%" }}>
        {/* Stage 1 — main hero */}
        <div style={{
          position: "absolute",
          inset: 0,
          display: "flex",
          alignItems: "center",
          opacity: stage1,
          transition: "opacity .15s linear",
          pointerEvents: stage1 > 0.5 ? "auto" : "none",
        }}>
          <div className="container" style={{ width: "100%" }}>
            <div style={{ maxWidth: 720 }}>
              <div style={{
                fontFamily: "var(--mono)",
                fontSize: 12,
                letterSpacing: "0.16em",
                textTransform: "uppercase",
                color: "rgba(140,223,255,0.85)",
                marginBottom: 24,
              }}>
                Genmax Peptides · Research-grade
              </div>
              <h1 style={{
                fontSize: "clamp(40px, 6vw, 76px)",
                lineHeight: 1.02,
                letterSpacing: "-0.035em",
                fontWeight: 300,
                margin: 0,
                color: "#f4f7ff",
              }}>
                From human biology<br/>
                <span style={{ color: "rgba(244,247,255,0.55)" }}>
                  to molecular precision.
                </span>
              </h1>
              <p style={{
                fontSize: "clamp(15px, 1.4vw, 18px)",
                lineHeight: 1.6,
                color: "rgba(220,228,245,0.70)",
                marginTop: 28,
                maxWidth: 520,
              }}>
                Scroll to journey from the body to the helix, from the helix to the
                peptide — and back out into a catalog built by researchers, for researchers.
              </p>
              <div style={{ marginTop: 36, display: "flex", gap: 12, flexWrap: "wrap" }}>
                <a
                  href="#shop"
                  onClick={(e) => { e.preventDefault(); navigate && navigate("shop"); }}
                  style={{
                    display: "inline-flex",
                    alignItems: "center",
                    gap: 10,
                    padding: "16px 28px",
                    background: "rgba(140,223,255,0.08)",
                    border: "1px solid rgba(140,223,255,0.5)",
                    color: "#f4f7ff",
                    textDecoration: "none",
                    fontSize: 14,
                    letterSpacing: "0.02em",
                    backdropFilter: "blur(8px)",
                    WebkitBackdropFilter: "blur(8px)",
                  }}
                >
                  Explore the science
                  <span style={{ opacity: 0.6, fontFamily: "var(--mono)" }}>↓</span>
                </a>
                <div style={{
                  alignSelf: "center",
                  fontFamily: "var(--mono)",
                  fontSize: 11,
                  letterSpacing: "0.12em",
                  textTransform: "uppercase",
                  color: "rgba(220,228,245,0.45)",
                  marginLeft: 8,
                }}>Scroll to continue ↓</div>
              </div>
            </div>
          </div>
        </div>

        {/* Stage 2 — DNA */}
        <StageCopy
          opacity={stage2}
          eyebrow="01 · Genetic blueprint"
          title="Every peptide begins as a sequence."
          body="What was once a body becomes the strands that encode it. Each base pair a letter, each turn a sentence. We sell the language, not the body."
          accent="rgba(160,200,255,0.85)"
        />

        {/* Stage 3 — Peptide */}
        <StageCopy
          opacity={stage3}
          eyebrow="02 · Molecular precision"
          title="A peptide is a sentence made of amino acids."
          body="Short, specific, built to spec. Every Genmax peptide is HPLC-verified above 99% purity — a chain of amino acids you can actually trust."
          accent="rgba(200,160,255,0.85)"
          showCta
          navigate={navigate}
        />
      </div>
    </div>
  );
}

function StageCopy({ opacity, eyebrow, title, body, accent, showCta, navigate }) {
  return (
    <div style={{
      position: "absolute",
      inset: 0,
      display: "flex",
      alignItems: "center",
      opacity,
      transition: "opacity .15s linear",
      pointerEvents: opacity > 0.5 ? "auto" : "none",
    }}>
      <div className="container" style={{ width: "100%" }}>
        <div style={{ maxWidth: 540, marginLeft: "auto", textAlign: "right" }}>
          <div style={{
            fontFamily: "var(--mono)",
            fontSize: 11,
            letterSpacing: "0.18em",
            textTransform: "uppercase",
            color: accent,
            marginBottom: 20,
          }}>{eyebrow}</div>
          <h2 style={{
            fontSize: "clamp(28px, 3.4vw, 48px)",
            lineHeight: 1.1,
            letterSpacing: "-0.025em",
            fontWeight: 300,
            margin: 0,
            color: "#f4f7ff",
          }}>{title}</h2>
          <p style={{
            fontSize: 15,
            lineHeight: 1.7,
            color: "rgba(220,228,245,0.65)",
            marginTop: 20,
            marginLeft: "auto",
          }}>{body}</p>
          {showCta && (
            <div style={{ marginTop: 28, display: "flex", justifyContent: "flex-end" }}>
              <a
                href="#shop"
                onClick={(e) => { e.preventDefault(); navigate && navigate("shop"); }}
                style={{
                  display: "inline-flex",
                  alignItems: "center",
                  gap: 10,
                  padding: "14px 24px",
                  background: "rgba(200,160,255,0.10)",
                  border: "1px solid rgba(200,160,255,0.45)",
                  color: "#f4f7ff",
                  textDecoration: "none",
                  fontSize: 14,
                  backdropFilter: "blur(8px)",
                  WebkitBackdropFilter: "blur(8px)",
                }}
              >
                Browse the catalog
                <span style={{ opacity: 0.7, fontFamily: "var(--mono)" }}>→</span>
              </a>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

function HeroProgressBar({ progress }) {
  const stages = [
    { p: 0.05, label: "Body" },
    { p: 0.50, label: "DNA" },
    { p: 0.93, label: "Peptide" },
  ];
  return (
    <div style={{
      position: "absolute",
      bottom: 36,
      left: 0,
      right: 0,
      display: "flex",
      justifyContent: "center",
      pointerEvents: "none",
    }}>
      <div style={{
        display: "flex",
        gap: 18,
        alignItems: "center",
        padding: "10px 18px",
        border: "1px solid rgba(255,255,255,0.10)",
        background: "rgba(10,14,28,0.55)",
        backdropFilter: "blur(10px)",
        WebkitBackdropFilter: "blur(10px)",
        borderRadius: 2,
      }}>
        {stages.map((s, i) => {
          const active = Math.abs(progress - s.p) < 0.20;
          return (
            <div key={s.label} style={{ display: "flex", alignItems: "center", gap: 18 }}>
              <div style={{
                fontFamily: "var(--mono)",
                fontSize: 10,
                letterSpacing: "0.14em",
                textTransform: "uppercase",
                color: active ? "#f4f7ff" : "rgba(220,228,245,0.45)",
                transition: "color .3s",
                display: "flex",
                alignItems: "center",
                gap: 8,
              }}>
                <span style={{
                  width: 5, height: 5, borderRadius: "50%",
                  background: active ? "#8cdfff" : "rgba(220,228,245,0.25)",
                  transition: "background .3s",
                }} />
                {s.label}
              </div>
              {i < stages.length - 1 && (
                <div style={{
                  width: 32, height: 1,
                  background: progress > stages[i].p ? "rgba(140,223,255,0.5)" : "rgba(255,255,255,0.08)",
                  transition: "background .3s",
                }} />
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

window.HeroSection3D = HeroSection3D;
