// waiv-particles.jsx — <ParticleField/>: a cursor-reactive canvas of drifting
// card-shards. Mounts absolutely into its parent (which must be position:relative).
// Shards parallax by depth, drift slowly, and scatter away from the cursor.

(function () {
  const { useRef, useEffect } = React;

  function ParticleField({ count = 34, tint = ['#ffd527', '#3f8bff', '#cfe0ff'], opacity = 0.5 }) {
    const canvasRef = useRef(null);

    useEffect(() => {
      const canvas = canvasRef.current;
      const parent = canvas.parentElement;
      const ctx = canvas.getContext('2d');
      const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      let W = 0, H = 0, dpr = Math.min(2, window.devicePixelRatio || 1);
      const parts = [];
      const rand = (a, b) => a + Math.random() * (b - a);

      const resize = () => {
        const r = parent.getBoundingClientRect();
        W = r.width; H = r.height;
        canvas.width = W * dpr; canvas.height = H * dpr;
        canvas.style.width = W + 'px'; canvas.style.height = H + 'px';
        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      };
      resize();

      for (let i = 0; i < count; i++) {
        const z = rand(0.32, 1);                 // depth
        parts.push({
          x: rand(0, W), y: rand(0, H), z,
          w: rand(16, 30) * z,
          vx: rand(-0.12, 0.12), vy: rand(-0.16, -0.04),
          rot: rand(0, Math.PI * 2), vrot: rand(-0.004, 0.004),
          col: tint[(Math.random() * tint.length) | 0],
        });
      }

      // pointer in canvas-local coords (-1 when outside)
      const ptr = { x: -1, y: -1 };
      const onMove = (e) => {
        const r = parent.getBoundingClientRect();
        if (e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom) { ptr.x = -1; ptr.y = -1; return; }
        ptr.x = e.clientX - r.left; ptr.y = e.clientY - r.top;
      };
      window.addEventListener('pointermove', onMove, { passive: true });

      const drawShard = (p) => {
        const w = p.w, h = w / 1.585, r = h * 0.22;
        ctx.save();
        ctx.translate(p.x, p.y); ctx.rotate(p.rot);
        ctx.globalAlpha = opacity * (0.18 + 0.55 * p.z);
        ctx.beginPath();
        ctx.moveTo(-w / 2 + r, -h / 2);
        ctx.arcTo(w / 2, -h / 2, w / 2, h / 2, r);
        ctx.arcTo(w / 2, h / 2, -w / 2, h / 2, r);
        ctx.arcTo(-w / 2, h / 2, -w / 2, -h / 2, r);
        ctx.arcTo(-w / 2, -h / 2, w / 2, -h / 2, r);
        ctx.closePath();
        const g = ctx.createLinearGradient(-w / 2, -h / 2, w / 2, h / 2);
        g.addColorStop(0, p.col + '55'); g.addColorStop(1, p.col + '08');
        ctx.fillStyle = g; ctx.fill();
        ctx.lineWidth = 1; ctx.strokeStyle = p.col + '66'; ctx.stroke();
        ctx.restore();
      };

      let raf;
      const tick = () => {
        ctx.clearRect(0, 0, W, H);
        for (const p of parts) {
          // cursor scatter
          if (ptr.x >= 0) {
            const dx = p.x - ptr.x, dy = p.y - ptr.y, d2 = dx * dx + dy * dy, R = 150;
            if (d2 < R * R) {
              const d = Math.sqrt(d2) || 1, f = (1 - d / R) * 1.8 * p.z;
              p.vx += (dx / d) * f; p.vy += (dy / d) * f;
            }
          }
          p.x += p.vx; p.y += p.vy; p.rot += p.vrot;
          p.vx *= 0.94; p.vy *= 0.94;
          p.vy -= 0.02 * p.z;                     // gentle upward drift
          // wrap
          const m = 40;
          if (p.y < -m) { p.y = H + m; p.x = rand(0, W); }
          if (p.x < -m) p.x = W + m; if (p.x > W + m) p.x = -m;
          if (p.y > H + m) p.y = -m;
          drawShard(p);
        }
        raf = requestAnimationFrame(tick);
      };
      if (!reduce) raf = requestAnimationFrame(tick); else { for (const p of parts) drawShard(p); }

      const ro = new ResizeObserver(resize); ro.observe(parent);
      return () => { cancelAnimationFrame(raf); window.removeEventListener('pointermove', onMove); ro.disconnect(); };
    }, []);

    return <canvas ref={canvasRef} className="wv-particles" aria-hidden="true" />;
  }

  window.ParticleField = ParticleField;
})();
