// three-hero.jsx — Three.js BIM + PM hero scene

function BIMHero() {
  const mountRef = React.useRef(null);
  const mouseRef = React.useRef({ x: 0, y: 0 });
  const frameRef = React.useRef(null);

  React.useEffect(() => {
    if (!mountRef.current || typeof THREE === 'undefined') return;

    const container = mountRef.current;
    const w = container.clientWidth;
    const h = container.clientHeight;

    // --- Renderer ---
    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setSize(w, h);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setClearColor(0x000000, 0);
    container.appendChild(renderer.domElement);

    // --- Scene & Camera ---
    const scene = new THREE.Scene();
    scene.fog = new THREE.Fog(0xf8f7f4, 38, 85);
    const camera = new THREE.PerspectiveCamera(35, w / h, 0.1, 200);
    camera.position.set(28, 26, 28);
    camera.lookAt(0, 5.5, 0);

    // --- Colors (matching site palette) ---
    const STEEL    = 0x345d8a;
    const STEEL_HI = 0x5682B3;
    const AMBER    = 0xa8612a;
    const LINE     = 0xdcd8cd;
    const FOG      = 0x76705f;
    const PAPER    = 0x0e0c08;

    // --- Ground grid ---
    const gridSize = 40;
    const gridDiv  = 40;
    const grid = new THREE.GridHelper(gridSize, gridDiv, LINE, LINE);
    grid.material.opacity = 0.25;
    grid.material.transparent = true;
    grid.position.y = -0.01;
    scene.add(grid);

    // fine grid
    const fineGrid = new THREE.GridHelper(gridSize, gridDiv * 4, LINE, LINE);
    fineGrid.material.opacity = 0.08;
    fineGrid.material.transparent = true;
    fineGrid.position.y = -0.02;
    scene.add(fineGrid);

    // --- Soft ground shadow to seat the building ---
    const shCanvas = document.createElement('canvas');
    shCanvas.width = shCanvas.height = 256;
    const shCtx = shCanvas.getContext('2d');
    const shGrad = shCtx.createRadialGradient(128, 128, 10, 128, 128, 128);
    shGrad.addColorStop(0, 'rgba(14,12,8,0.20)');
    shGrad.addColorStop(1, 'rgba(14,12,8,0)');
    shCtx.fillStyle = shGrad;
    shCtx.fillRect(0, 0, 256, 256);
    const shadow = new THREE.Mesh(
      new THREE.PlaneGeometry(26, 20),
      new THREE.MeshBasicMaterial({
        map: new THREE.CanvasTexture(shCanvas),
        transparent: true,
        depthWrite: false,
      })
    );
    shadow.rotation.x = -Math.PI / 2;
    shadow.position.set(0.5, -0.005, 0);
    scene.add(shadow);

    // --- Building parameters ---
    const floors = 8;
    const floorH = 1.6;
    const buildW = 10;
    const buildD = 7;
    const colRows = 4;
    const colCols = 3;

    const buildingGroup = new THREE.Group();

    // --- Helper: create wireframe box edges ---
    function wireBox(w, h, d, color, opacity) {
      const geo = new THREE.BoxGeometry(w, h, d);
      const edges = new THREE.EdgesGeometry(geo);
      const mat = new THREE.LineBasicMaterial({ color, transparent: true, opacity });
      return new THREE.LineSegments(edges, mat);
    }

    // --- Floor slabs ---
    const slabGroup = new THREE.Group();
    for (let i = 0; i <= floors; i++) {
      const y = i * floorH;
      // construction gradient — upper floors read lighter / in progress
      const slab = wireBox(buildW, 0.08, buildD, STEEL, 0.38 - (i / floors) * 0.15);
      slab.position.y = y;
      slabGroup.add(slab);

      const planeGeo = new THREE.PlaneGeometry(buildW, buildD);
      const planeMat = new THREE.MeshBasicMaterial({
        color: STEEL,
        transparent: true,
        opacity: i === 0 ? 0.06 : 0.03,
        side: THREE.DoubleSide,
      });
      const plane = new THREE.Mesh(planeGeo, planeMat);
      plane.rotation.x = -Math.PI / 2;
      plane.position.y = y;
      slabGroup.add(plane);
    }
    buildingGroup.add(slabGroup);

    // --- Structural beams (girders between columns) ---
    const beamGroup = new THREE.Group();
    const beamMat = new THREE.LineBasicMaterial({ color: STEEL, transparent: true, opacity: 0.22 });
    for (let f = 1; f <= floors; f++) {
      const y = f * floorH - 0.12;
      for (let c = 0; c <= colCols; c++) {
        const z = -buildD / 2 + (buildD / colCols) * c;
        const geo = new THREE.BufferGeometry().setFromPoints([
          new THREE.Vector3(-buildW / 2, y, z),
          new THREE.Vector3(buildW / 2, y, z),
        ]);
        beamGroup.add(new THREE.Line(geo, beamMat));
      }
      for (let r = 0; r <= colRows; r++) {
        const x = -buildW / 2 + (buildW / colRows) * r;
        const geo = new THREE.BufferGeometry().setFromPoints([
          new THREE.Vector3(x, y, -buildD / 2),
          new THREE.Vector3(x, y, buildD / 2),
        ]);
        beamGroup.add(new THREE.Line(geo, beamMat));
      }
    }
    buildingGroup.add(beamGroup);

    // --- Columns (structural) ---
    const colGroup = new THREE.Group();
    const colGeo = new THREE.BoxGeometry(0.3, floorH * floors, 0.3);
    const colEdges = new THREE.EdgesGeometry(colGeo);
    for (let r = 0; r <= colRows; r++) {
      for (let c = 0; c <= colCols; c++) {
        const mat = new THREE.LineBasicMaterial({ color: STEEL, transparent: true, opacity: 0.4 });
        const col = new THREE.LineSegments(colEdges, mat);
        col.position.set(
          -buildW / 2 + (buildW / colRows) * r,
          (floorH * floors) / 2,
          -buildD / 2 + (buildD / colCols) * c
        );
        colGroup.add(col);
      }
    }
    buildingGroup.add(colGroup);

    // --- Core (elevator/stairs) ---
    const coreGroup = new THREE.Group();
    const core = wireBox(2.4, floorH * floors, 2.0, STEEL_HI, 0.5);
    core.position.set(1.5, (floorH * floors) / 2, 0);
    coreGroup.add(core);
    const coreWall = wireBox(0.05, floorH * floors, 2.0, STEEL_HI, 0.3);
    coreWall.position.set(1.5, (floorH * floors) / 2, 0);
    coreGroup.add(coreWall);

    const stairMat = new THREE.LineBasicMaterial({ color: STEEL_HI, transparent: true, opacity: 0.25 });
    for (let f = 0; f < floors; f++) {
      const baseY = f * floorH;
      const midY = baseY + floorH * 0.5;
      const topY = baseY + floorH;
      const stairGeo = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(0.5, baseY, -0.5),
        new THREE.Vector3(0.5, midY, 0.5),
        new THREE.Vector3(2.5, midY, 0.5),
        new THREE.Vector3(2.5, topY, -0.5),
      ]);
      coreGroup.add(new THREE.Line(stairGeo, stairMat));
    }
    buildingGroup.add(coreGroup);

    // --- MEP installation colors ---
    const MEP_HVAC = 0xa8612a;
    const MEP_PIPE = 0x5a8a6a;
    const MEP_ELEC = 0x8a6a5a;

    // --- HVAC ductwork (rectangular ducts per floor) ---
    const hvacGroup = new THREE.Group();
    for (let f = 1; f <= floors; f++) {
      const y = f * floorH - 0.25;
      const trunk = wireBox(buildW - 2, 0.28, 0.45, MEP_HVAC, 0.35);
      trunk.position.set(0, y, 0);
      hvacGroup.add(trunk);

      if (f % 2 === 1) {
        const br1 = wireBox(0.3, 0.2, buildD * 0.35, MEP_HVAC, 0.25);
        br1.position.set(-2.5, y, buildD * 0.18);
        hvacGroup.add(br1);
        const br2 = wireBox(0.3, 0.2, buildD * 0.35, MEP_HVAC, 0.25);
        br2.position.set(3.5, y, -buildD * 0.18);
        hvacGroup.add(br2);
      } else {
        const br3 = wireBox(0.25, 0.18, buildD * 0.3, MEP_HVAC, 0.25);
        br3.position.set(-1, y, -buildD * 0.2);
        hvacGroup.add(br3);
        const br4 = wireBox(0.25, 0.18, buildD * 0.3, MEP_HVAC, 0.25);
        br4.position.set(2.5, y, buildD * 0.15);
        hvacGroup.add(br4);
      }
    }
    buildingGroup.add(hvacGroup);

    // --- Plumbing risers + horizontal runs ---
    const plumbGroup = new THREE.Group();
    const plumbMat = new THREE.LineBasicMaterial({ color: MEP_PIPE, transparent: true, opacity: 0.4 });
    [
      { x: -buildW / 2 + 1.2, z: -buildD / 2 + 1 },
      { x: -buildW / 2 + 1.2, z: buildD / 2 - 1 },
      { x: buildW / 2 - 1.5, z: 0 },
    ].forEach(pos => {
      const geo = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(pos.x, 0, pos.z),
        new THREE.Vector3(pos.x, floorH * floors, pos.z),
      ]);
      plumbGroup.add(new THREE.Line(geo, plumbMat));
    });
    for (let f = 1; f <= floors; f++) {
      const y = f * floorH - 0.45;
      const geo = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(-buildW / 2 + 1.2, y, -buildD / 2 + 1),
        new THREE.Vector3(-buildW / 2 + 1.2, y, buildD / 2 - 1),
      ]);
      plumbGroup.add(new THREE.Line(geo, plumbMat));
    }
    buildingGroup.add(plumbGroup);

    // --- Electrical cable trays ---
    const elecGroup = new THREE.Group();
    const elecMat = new THREE.LineBasicMaterial({ color: MEP_ELEC, transparent: true, opacity: 0.3 });
    for (let f = 1; f <= floors; f++) {
      const y = f * floorH - 0.5;
      const geo = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(-buildW / 2 + 0.4, y, -buildD / 2 + 0.4),
        new THREE.Vector3(buildW / 2 - 0.4, y, -buildD / 2 + 0.4),
        new THREE.Vector3(buildW / 2 - 0.4, y, buildD / 2 - 0.4),
        new THREE.Vector3(-buildW / 2 + 0.4, y, buildD / 2 - 0.4),
      ]);
      elecGroup.add(new THREE.Line(geo, elecMat));
    }
    const elecRiserGeo = new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(buildW / 2 - 0.4, 0, buildD / 2 - 0.4),
      new THREE.Vector3(buildW / 2 - 0.4, floorH * floors, buildD / 2 - 0.4),
    ]);
    elecGroup.add(new THREE.Line(elecRiserGeo, elecMat));
    buildingGroup.add(elecGroup);

    // --- Interior partition walls ---
    const partGroup = new THREE.Group();
    const partMat = new THREE.LineBasicMaterial({ color: FOG, transparent: true, opacity: 0.12 });
    for (let f = 1; f <= floors; f++) {
      const yBot = (f - 1) * floorH + 0.05;
      const yTop = f * floorH - 0.05;
      const corrGeo = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(-buildW / 2 + 0.5, yBot, -1),
        new THREE.Vector3(-buildW / 2 + 0.5, yTop, -1),
        new THREE.Vector3(0.2, yTop, -1),
        new THREE.Vector3(0.2, yBot, -1),
        new THREE.Vector3(-buildW / 2 + 0.5, yBot, -1),
      ]);
      partGroup.add(new THREE.Line(corrGeo, partMat));

      if (f % 2 === 0) {
        const crossGeo = new THREE.BufferGeometry().setFromPoints([
          new THREE.Vector3(-2, yBot, -buildD / 2 + 0.5),
          new THREE.Vector3(-2, yTop, -buildD / 2 + 0.5),
          new THREE.Vector3(-2, yTop, -1),
          new THREE.Vector3(-2, yBot, -1),
        ]);
        partGroup.add(new THREE.Line(crossGeo, partMat));
      }
    }
    buildingGroup.add(partGroup);

    // --- Roof mechanical equipment ---
    const roofY = floors * floorH;
    const roofGroup = new THREE.Group();
    const ahu1 = wireBox(2.0, 1.0, 1.5, AMBER, 0.35);
    ahu1.position.set(-2, roofY + 0.5, -1.5);
    roofGroup.add(ahu1);
    const ahu2 = wireBox(1.5, 0.8, 1.2, AMBER, 0.35);
    ahu2.position.set(3, roofY + 0.4, 1.5);
    roofGroup.add(ahu2);
    const coolingTower = wireBox(1.0, 1.4, 1.0, FOG, 0.3);
    coolingTower.position.set(-3.5, roofY + 0.7, 1.8);
    roofGroup.add(coolingTower);
    const roofPipeMat = new THREE.LineBasicMaterial({ color: MEP_PIPE, transparent: true, opacity: 0.35 });
    const roofPipe = new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(-2, roofY, -1.5),
      new THREE.Vector3(-2, roofY + 0.2, -1.5),
      new THREE.Vector3(-buildW / 2 + 1.2, roofY + 0.2, -1),
    ]);
    roofGroup.add(new THREE.Line(roofPipe, roofPipeMat));
    buildingGroup.add(roofGroup);

    // --- Tower crane ---
    const craneGroup = new THREE.Group();
    const craneMat = new THREE.LineBasicMaterial({ color: AMBER, transparent: true, opacity: 0.5 });
    const craneX = -9, craneZ = 5.5, mastH = 14.5;

    const mast = wireBox(0.7, mastH, 0.7, AMBER, 0.45);
    mast.position.set(craneX, mastH / 2, craneZ);
    craneGroup.add(mast);

    // mast lattice diagonals
    for (let s = 0; s < 7; s++) {
      const y0 = s * 2, y1 = y0 + 2;
      const dir = s % 2 === 0 ? 1 : -1;
      const latGeo1 = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(craneX - 0.35 * dir, y0, craneZ + 0.35),
        new THREE.Vector3(craneX + 0.35 * dir, y1, craneZ + 0.35),
      ]);
      craneGroup.add(new THREE.Line(latGeo1, craneMat));
      const latGeo2 = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(craneX + 0.35, y0, craneZ - 0.35 * dir),
        new THREE.Vector3(craneX + 0.35, y1, craneZ + 0.35 * dir),
      ]);
      craneGroup.add(new THREE.Line(latGeo2, craneMat));
    }

    // slewing assembly — rotates around the mast top
    const jibGroup = new THREE.Group();
    jibGroup.position.set(craneX, mastH, craneZ);

    // main jib (extends +x in local space)
    const jib = wireBox(11, 0.45, 0.5, AMBER, 0.5);
    jib.position.set(4.5, 0.25, 0);
    jibGroup.add(jib);
    // counter-jib
    const counterJib = wireBox(3.4, 0.45, 0.5, AMBER, 0.5);
    counterJib.position.set(-2.2, 0.25, 0);
    jibGroup.add(counterJib);
    // counterweight
    const counterweight = wireBox(1.0, 1.0, 0.9, AMBER, 0.45);
    counterweight.position.set(-3.4, -0.4, 0);
    jibGroup.add(counterweight);
    // apex + tie bars
    const apexGeo = new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(0, 0.5, 0),
      new THREE.Vector3(0, 2.2, 0),
    ]);
    jibGroup.add(new THREE.Line(apexGeo, craneMat));
    const tie1 = new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(0, 2.2, 0),
      new THREE.Vector3(9.2, 0.5, 0),
    ]);
    jibGroup.add(new THREE.Line(tie1, craneMat));
    const tie2 = new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(0, 2.2, 0),
      new THREE.Vector3(-3.4, 0.5, 0),
    ]);
    jibGroup.add(new THREE.Line(tie2, craneMat));
    // hoist cable + hook
    const cableGeo = new THREE.BufferGeometry().setFromPoints([
      new THREE.Vector3(7, 0, 0),
      new THREE.Vector3(7, -2.2, 0),
    ]);
    jibGroup.add(new THREE.Line(cableGeo, craneMat));
    const hook = wireBox(0.35, 0.35, 0.35, AMBER, 0.5);
    hook.position.set(7, -2.4, 0);
    jibGroup.add(hook);

    craneGroup.add(jibGroup);
    scene.add(craneGroup);

    // --- Exterior skin (curtain wall) ---
    const skinGroup = new THREE.Group();
    const skinMat = new THREE.MeshBasicMaterial({
      color: STEEL_HI,
      transparent: true,
      opacity: 0.04,
      side: THREE.DoubleSide,
    });
    const skinFront = new THREE.Mesh(
      new THREE.PlaneGeometry(buildW, floorH * floors),
      skinMat
    );
    skinFront.position.set(0, (floorH * floors) / 2, buildD / 2);
    skinGroup.add(skinFront);
    const skinRight = new THREE.Mesh(
      new THREE.PlaneGeometry(buildD, floorH * floors),
      skinMat
    );
    skinRight.rotation.y = Math.PI / 2;
    skinRight.position.set(buildW / 2, (floorH * floors) / 2, 0);
    skinGroup.add(skinRight);

    const cwMat = new THREE.LineBasicMaterial({ color: STEEL, transparent: true, opacity: 0.12 });
    for (let i = 0; i <= 10; i++) {
      const x = -buildW / 2 + (buildW / 10) * i;
      const geo = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(x, 0, buildD / 2),
        new THREE.Vector3(x, floorH * floors, buildD / 2),
      ]);
      skinGroup.add(new THREE.Line(geo, cwMat));
    }
    for (let i = 0; i <= 7; i++) {
      const z = -buildD / 2 + (buildD / 7) * i;
      const geo = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(buildW / 2, 0, z),
        new THREE.Vector3(buildW / 2, floorH * floors, z),
      ]);
      skinGroup.add(new THREE.Line(geo, cwMat));
    }
    for (let f = 0; f <= floors; f++) {
      const y = f * floorH;
      const frontGeo = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(-buildW / 2, y, buildD / 2),
        new THREE.Vector3(buildW / 2, y, buildD / 2),
      ]);
      skinGroup.add(new THREE.Line(frontGeo, cwMat));
      const rightGeo = new THREE.BufferGeometry().setFromPoints([
        new THREE.Vector3(buildW / 2, y, -buildD / 2),
        new THREE.Vector3(buildW / 2, y, buildD / 2),
      ]);
      skinGroup.add(new THREE.Line(rightGeo, cwMat));
    }
    buildingGroup.add(skinGroup);

    scene.add(buildingGroup);


    // --- Data flow particles streaming into the building ---
    const flowGroup = new THREE.Group();
    const NUM_STREAMS = 6;
    const PARTICLES_PER_STREAM = 2;

    // Stream origins — spread around building at ground level, coming from far out
    const streamDefs = [
      { from: new THREE.Vector3(-22, 0.5, -12), to: new THREE.Vector3(-buildW/2, floorH * 2, 0),    color: STEEL },
      { from: new THREE.Vector3(-18, 0.5,  14), to: new THREE.Vector3(-buildW/2, floorH * 4, 1),    color: STEEL_HI },
      { from: new THREE.Vector3( 20, 0.5, -10), to: new THREE.Vector3( buildW/2, floorH * 3, -1),   color: STEEL },
      { from: new THREE.Vector3( 16, 0.5,  16), to: new THREE.Vector3( buildW/2, floorH * 5, 1.5),  color: AMBER },
      { from: new THREE.Vector3(  0, 0.5, -18), to: new THREE.Vector3( 0, floorH * 1, -buildD/2),   color: STEEL_HI },
      { from: new THREE.Vector3( -8, 0.5,  18), to: new THREE.Vector3(-2, floorH * 6, buildD/2),    color: AMBER },
    ];

    // Each particle tracks its own progress (0→1) along a curved path
    const flowParticles = [];

    streamDefs.forEach((stream, si) => {
      // midpoint lifted up to create an arc
      const mid = new THREE.Vector3().lerpVectors(stream.from, stream.to, 0.5);
      mid.y += 4 + Math.random() * 3;

      for (let p = 0; p < PARTICLES_PER_STREAM; p++) {
        const dotGeo = new THREE.SphereGeometry(0.08, 6, 6);
        const dotMat = new THREE.MeshBasicMaterial({
          color: stream.color,
          transparent: true,
          opacity: 0,
        });
        const dot = new THREE.Mesh(dotGeo, dotMat);
        flowGroup.add(dot);

        // stagger start times so they don't all launch at once
        flowParticles.push({
          mesh: dot,
          from: stream.from.clone(),
          mid: mid.clone(),
          to: stream.to.clone(),
          progress: p / PARTICLES_PER_STREAM, // spread evenly along path
          speed: 0.12 + Math.random() * 0.06,
        });
      }

      // draw the stream path as a faint dashed curve
      const curve = new THREE.QuadraticBezierCurve3(stream.from, mid, stream.to);
      const curvePoints = curve.getPoints(40);
      const curveGeo = new THREE.BufferGeometry().setFromPoints(curvePoints);
      const curveMat = new THREE.LineDashedMaterial({
        color: stream.color,
        transparent: true,
        opacity: 0.12,
        dashSize: 0.6,
        gapSize: 0.4,
      });
      const curveLine = new THREE.Line(curveGeo, curveMat);
      curveLine.computeLineDistances();
      flowGroup.add(curveLine);

      // small "source" marker at origin
      const srcGeo = new THREE.RingGeometry(0.3, 0.5, 6);
      const srcMat = new THREE.MeshBasicMaterial({
        color: stream.color,
        transparent: true,
        opacity: 0.3,
        side: THREE.DoubleSide,
      });
      const srcRing = new THREE.Mesh(srcGeo, srcMat);
      srcRing.position.copy(stream.from);
      srcRing.rotation.x = -Math.PI / 2;
      flowGroup.add(srcRing);
    });

    scene.add(flowGroup);

    // --- AI scan plane — sweeps up through the building on a loop ---
    const scanGroup = new THREE.Group();
    const buildingH = floorH * floors;

    // main scan plane
    const scanGeo = new THREE.PlaneGeometry(buildW + 4, buildD + 4);
    const scanMat = new THREE.MeshBasicMaterial({
      color: STEEL_HI,
      transparent: true,
      opacity: 0,
      side: THREE.DoubleSide,
    });
    const scanPlane = new THREE.Mesh(scanGeo, scanMat);
    scanPlane.rotation.x = -Math.PI / 2;
    scanPlane.position.y = 0;
    scanGroup.add(scanPlane);

    // scan edge glow — a ring outline at the scan plane level
    const scanRingGeo = new THREE.RingGeometry(
      Math.max(buildW, buildD) * 0.55,
      Math.max(buildW, buildD) * 0.58,
      4
    );
    const scanRingMat = new THREE.MeshBasicMaterial({
      color: STEEL_HI,
      transparent: true,
      opacity: 0,
      side: THREE.DoubleSide,
    });
    const scanRing = new THREE.Mesh(scanRingGeo, scanRingMat);
    scanRing.rotation.x = -Math.PI / 2;
    scanRing.rotation.z = Math.PI / 4;
    scanGroup.add(scanRing);

    // trailing afterglow planes — faint echoes below the scan
    const trailPlanes = [];
    for (let i = 1; i <= 3; i++) {
      const tGeo = new THREE.PlaneGeometry(buildW + 2, buildD + 2);
      const tMat = new THREE.MeshBasicMaterial({
        color: STEEL_HI,
        transparent: true,
        opacity: 0,
        side: THREE.DoubleSide,
      });
      const tPlane = new THREE.Mesh(tGeo, tMat);
      tPlane.rotation.x = -Math.PI / 2;
      scanGroup.add(tPlane);
      trailPlanes.push({ mesh: tPlane, offset: i * 0.8 });
    }

    scene.add(scanGroup);

    // --- Neural network connections between building nodes ---
    const neuralGroup = new THREE.Group();

    // define nodes at key structural intersections
    const neuralNodes = [];
    for (let f = 2; f <= floors; f += 4) {
      const y = f * floorH;
      // nodes at column intersections on this floor
      neuralNodes.push(new THREE.Vector3(-buildW/2, y, -buildD/2));
      neuralNodes.push(new THREE.Vector3( buildW/2, y, -buildD/2));
      neuralNodes.push(new THREE.Vector3(-buildW/2, y,  buildD/2));
      neuralNodes.push(new THREE.Vector3( buildW/2, y,  buildD/2));
      neuralNodes.push(new THREE.Vector3(0, y, 0));
      neuralNodes.push(new THREE.Vector3(1.5, y, 0)); // core
    }

    // node spheres
    const neuralDots = [];
    neuralNodes.forEach(pos => {
      const geo = new THREE.SphereGeometry(0.12, 8, 8);
      const mat = new THREE.MeshBasicMaterial({
        color: STEEL_HI,
        transparent: true,
        opacity: 0.15,
      });
      const dot = new THREE.Mesh(geo, mat);
      dot.position.copy(pos);
      neuralGroup.add(dot);
      neuralDots.push({ mesh: dot, baseY: pos.y });
    });

    // connections — link nearby nodes (within a threshold)
    const neuralLines = [];
    const connectionThreshold = 9;
    for (let i = 0; i < neuralNodes.length; i++) {
      for (let j = i + 1; j < neuralNodes.length; j++) {
        const dist = neuralNodes[i].distanceTo(neuralNodes[j]);
        if (dist < connectionThreshold && dist > 1) {
          const pts = [neuralNodes[i], neuralNodes[j]];
          const geo = new THREE.BufferGeometry().setFromPoints(pts);
          const mat = new THREE.LineBasicMaterial({
            color: STEEL_HI,
            transparent: true,
            opacity: 0.04,
          });
          const line = new THREE.Line(geo, mat);
          neuralGroup.add(line);
          neuralLines.push({ line, mat, dist, i, j });
        }
      }
    }

    // neural pulse particles — travel along connections
    const neuralPulses = [];
    const NUM_PULSES = 3;
    for (let p = 0; p < NUM_PULSES; p++) {
      const lineData = neuralLines[Math.floor(Math.random() * neuralLines.length)];
      if (!lineData) continue;
      const geo = new THREE.SphereGeometry(0.06, 6, 6);
      const mat = new THREE.MeshBasicMaterial({
        color: STEEL_HI,
        transparent: true,
        opacity: 0,
      });
      const dot = new THREE.Mesh(geo, mat);
      neuralGroup.add(dot);
      neuralPulses.push({
        mesh: dot,
        from: neuralNodes[lineData.i],
        to: neuralNodes[lineData.j],
        progress: Math.random(),
        speed: 0.3 + Math.random() * 0.4,
        lineRef: lineData,
      });
    }

    scene.add(neuralGroup);

    // --- Ambient light (for any future mesh materials) ---
    scene.add(new THREE.AmbientLight(0xffffff, 0.6));

    // --- Mouse tracking ---
    function onMouseMove(e) {
      mouseRef.current.x = (e.clientX / window.innerWidth) * 2 - 1;
      mouseRef.current.y = -(e.clientY / window.innerHeight) * 2 + 1;
    }
    window.addEventListener('mousemove', onMouseMove);

    // --- Animation ---
    const clock = new THREE.Clock();

    function animate() {
      frameRef.current = requestAnimationFrame(animate);
      const t = clock.getElapsedTime();

      // slow auto-rotation
      const autoAngle = t * 0.08;
      // mouse parallax offset
      const mx = mouseRef.current.x * 0.3;
      const my = mouseRef.current.y * 0.15;

      const radius = 34;
      camera.position.x = Math.cos(autoAngle + mx) * radius;
      camera.position.z = Math.sin(autoAngle + mx) * radius;
      camera.position.y = 26 + my * 4;
      camera.lookAt(0, 5.5, 0);

      // tower crane — slow jib slew around the mast
      jibGroup.rotation.y = 0.55 + Math.sin(t * 0.12) * 0.5;

      // --- AI scan plane sweep ---
      const scanCycle = 6; // seconds per full sweep
      const scanPause = 2; // seconds pause at top before restarting
      const totalCycle = scanCycle + scanPause;
      const cycleT = t % totalCycle;
      if (cycleT < scanCycle) {
        const scanProgress = cycleT / scanCycle;
        // ease-in-out
        const eased = scanProgress < 0.5
          ? 2 * scanProgress * scanProgress
          : 1 - Math.pow(-2 * scanProgress + 2, 2) / 2;
        const scanY = eased * (buildingH + 2) - 0.5;
        scanPlane.position.y = scanY;
        scanRing.position.y = scanY;
        // opacity: fade in, bright in middle, fade out at top
        const scanFade = Math.sin(scanProgress * Math.PI);
        scanMat.opacity = scanFade * 0.08;
        scanRingMat.opacity = scanFade * 0.25;
        // trails
        trailPlanes.forEach(tp => {
          tp.mesh.position.y = scanY - tp.offset;
          tp.mesh.material.opacity = Math.max(0, scanFade * 0.04 * (1 - tp.offset / 3));
        });
      } else {
        // pause phase — fade everything out
        scanMat.opacity *= 0.92;
        scanRingMat.opacity *= 0.92;
        trailPlanes.forEach(tp => { tp.mesh.material.opacity *= 0.92; });
      }

      // --- Neural network animation ---
      // pulse nodes near the scan plane
      neuralDots.forEach(nd => {
        const distToScan = Math.abs(nd.baseY - scanPlane.position.y);
        const activation = Math.max(0, 1 - distToScan / 2.5);
        nd.mesh.material.opacity = 0.12 + activation * 0.55;
        const s = 1 + activation * 1.5;
        nd.mesh.scale.setScalar(s);
      });

      // pulse connection lines near scan
      neuralLines.forEach(nl => {
        const midY = (neuralNodes[nl.i].y + neuralNodes[nl.j].y) / 2;
        const distToScan = Math.abs(midY - scanPlane.position.y);
        const activation = Math.max(0, 1 - distToScan / 3);
        nl.mat.opacity = 0.03 + activation * 0.18;
      });

      // move neural pulses along connections
      neuralPulses.forEach(np => {
        np.progress += np.speed * 0.016;
        if (np.progress > 1) {
          np.progress = 0;
          // pick a new random connection
          const newLine = neuralLines[Math.floor(Math.random() * neuralLines.length)];
          if (newLine) {
            np.from = neuralNodes[newLine.i];
            np.to = neuralNodes[newLine.j];
            np.lineRef = newLine;
          }
        }
        const p = np.progress;
        np.mesh.position.lerpVectors(np.from, np.to, p);
        const pulseFade = Math.sin(p * Math.PI);
        np.mesh.material.opacity = pulseFade * 0.6;
        np.mesh.scale.setScalar(0.8 + pulseFade * 1.2);
      });

      // animate data flow particles along bezier curves
      const dt = clock.getDelta() || 0.016;
      flowParticles.forEach(fp => {
        fp.progress += fp.speed * dt;
        if (fp.progress > 1) fp.progress -= 1;

        const p = fp.progress;
        // quadratic bezier: B(t) = (1-t)²·P0 + 2(1-t)t·P1 + t²·P2
        const inv = 1 - p;
        fp.mesh.position.set(
          inv * inv * fp.from.x + 2 * inv * p * fp.mid.x + p * p * fp.to.x,
          inv * inv * fp.from.y + 2 * inv * p * fp.mid.y + p * p * fp.to.y,
          inv * inv * fp.from.z + 2 * inv * p * fp.mid.z + p * p * fp.to.z
        );

        // fade in at start, bright in middle, fade at end (arrival)
        const fade = Math.sin(p * Math.PI);
        fp.mesh.material.opacity = fade * 0.7;
        // scale up slightly in the middle of the arc
        const s = 0.06 + fade * 0.06;
        fp.mesh.scale.setScalar(s / 0.08);
      });

      renderer.render(scene, camera);
    }
    animate();

    // --- Resize ---
    function onResize() {
      const w = container.clientWidth;
      const h = container.clientHeight;
      camera.aspect = w / h;
      camera.updateProjectionMatrix();
      renderer.setSize(w, h);
    }
    window.addEventListener('resize', onResize);

    // --- Cleanup ---
    return () => {
      cancelAnimationFrame(frameRef.current);
      window.removeEventListener('mousemove', onMouseMove);
      window.removeEventListener('resize', onResize);
      renderer.dispose();
      if (container.contains(renderer.domElement)) {
        container.removeChild(renderer.domElement);
      }
    };
  }, []);

  return (
    <div
      ref={mountRef}
      style={{
        position: 'absolute',
        inset: 0,
        zIndex: 0,
        pointerEvents: 'none',
      }}
    />
  );
}
