/* === VISUAL EFFECTS — 3D particles, tilt, magnetic, animated counters === */

const { useState: fxState, useEffect: fxEffect, useRef: fxRef } = React;

/* =================================================================
   ParticleField — canvas with z-depth particle network
   Used as hero background. Honors prefers-reduced-motion.
   ================================================================= */
function ParticleField({ density = 60, height = "100%", opacity = 0.9, color1 = "#0a6ef5", color2 = "#05b89c", color3 = "#e85d00", interactive = true }) {
  const canvasRef = fxRef(null);
  const rafRef = fxRef(null);
  const mouseRef = fxRef({ x: 0.5, y: 0.5 });
  const reducedMotion = fxRef(false);

  fxEffect(() => {
    reducedMotion.current = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    const DPR = Math.min(window.devicePixelRatio || 1, 2);
    let W = 0, H = 0;
    const resize = () => {
      const r = canvas.getBoundingClientRect();
      W = Math.max(320, r.width);
      H = Math.max(320, 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();

    const onMouse = (e) => {
      if (!interactive) return;
      const r = canvas.getBoundingClientRect();
      mouseRef.current = {
        x: (e.clientX - r.left) / r.width,
        y: (e.clientY - r.top) / r.height,
      };
    };

    window.addEventListener("resize", resize);
    if (interactive) window.addEventListener("mousemove", onMouse);

    const colors = [color1, color2, color3, color1];
    const particles = [];
    for (let i = 0; i < density; i++) {
      particles.push({
        x: Math.random() * W,
        y: Math.random() * H,
        z: Math.random(),
        vx: (Math.random() - 0.5) * 1.2,
        vy: (Math.random() - 0.5) * 1.2,
        vz: (Math.random() - 0.5) * 0.004,
        r: Math.random() * 2 + 0.6,
        c: colors[Math.floor(Math.random() * colors.length)],
        op: Math.random() * 0.5 + 0.2,
      });
    }

    const shapes = [];
    for (let i = 0; i < 9; i++) {
      shapes.push({
        x: Math.random() * W,
        y: Math.random() * H,
        rot: Math.random() * Math.PI * 2,
        rotSpd: (Math.random() - 0.5) * 0.006,
        size: Math.random() * 50 + 22,
        op: Math.random() * 0.06 + 0.015,
        type: ["diamond", "triangle", "circle"][Math.floor(Math.random() * 3)],
        vx: (Math.random() - 0.5) * 0.25,
        vy: (Math.random() - 0.5) * 0.25,
        c: colors[Math.floor(Math.random() * colors.length)],
      });
    }

    let t = 0;
    const drawDiamond = (x, y, s) => {
      ctx.beginPath();
      ctx.moveTo(x, y - s);
      ctx.lineTo(x + s * 0.6, y);
      ctx.lineTo(x, y + s);
      ctx.lineTo(x - s * 0.6, y);
      ctx.closePath();
    };
    const drawTriangle = (x, y, s) => {
      ctx.beginPath();
      ctx.moveTo(x, y - s);
      ctx.lineTo(x + s * 0.866, y + s * 0.5);
      ctx.lineTo(x - s * 0.866, y + s * 0.5);
      ctx.closePath();
    };

    const tick = () => {
      t += 0.005;
      ctx.clearRect(0, 0, W, H);
      const mx = mouseRef.current.x * W;
      const my = mouseRef.current.y * H;

      // Shapes — slow floating geometry
      shapes.forEach((s) => {
        s.x += s.vx + Math.sin(t * 0.6 + s.rot) * 0.08;
        s.y += s.vy + Math.cos(t * 0.5 + s.rot) * 0.08;
        s.rot += s.rotSpd;
        if (s.x < -120) s.x = W + 120;
        if (s.x > W + 120) s.x = -120;
        if (s.y < -120) s.y = H + 120;
        if (s.y > H + 120) s.y = -120;
        ctx.save();
        ctx.translate(s.x, s.y);
        ctx.rotate(s.rot);
        ctx.strokeStyle = s.c;
        ctx.globalAlpha = s.op;
        ctx.lineWidth = 1;
        if (s.type === "diamond") { drawDiamond(0, 0, s.size); ctx.stroke(); }
        else if (s.type === "triangle") { drawTriangle(0, 0, s.size * 0.7); ctx.stroke(); }
        else {
          ctx.beginPath();
          ctx.arc(0, 0, s.size * 0.7, 0, Math.PI * 2);
          ctx.stroke();
          ctx.beginPath();
          ctx.arc(0, 0, s.size * 0.42, 0, Math.PI * 2);
          ctx.stroke();
        }
        ctx.restore();
      });

      // Particle-to-particle interaction — short-range attraction + close-range repulsion
      // gives a "neuron cluster" feel without exploding the sim
      const FLOCK_RANGE = 120;
      const REPEL_RANGE = 22;
      for (let i = 0; i < particles.length; i++) {
        const a = particles[i];
        for (let j = i + 1; j < particles.length; j++) {
          const b = particles[j];
          const dx = b.x - a.x, dy = b.y - a.y;
          const d2 = dx * dx + dy * dy;
          if (d2 > FLOCK_RANGE * FLOCK_RANGE || d2 < 0.0001) continue;
          const d = Math.sqrt(d2);
          if (d < REPEL_RANGE) {
            // repel close pairs
            const f = (REPEL_RANGE - d) / REPEL_RANGE * 0.06;
            a.vx -= (dx / d) * f; a.vy -= (dy / d) * f;
            b.vx += (dx / d) * f; b.vy += (dy / d) * f;
          } else {
            // gentle attraction to form clusters
            const f = (1 - d / FLOCK_RANGE) * 0.004;
            a.vx += (dx / d) * f; a.vy += (dy / d) * f;
            b.vx -= (dx / d) * f; b.vy -= (dy / d) * f;
          }
        }
      }

      // Particles
      particles.forEach((p) => {
        if (interactive) {
          const dx = mx - p.x;
          const dy = my - p.y;
          const d = Math.sqrt(dx * dx + dy * dy);
          if (d < 280) {
            p.vx += (dx / d) * 0.018;
            p.vy += (dy / d) * 0.018;
          }
        }
        // soft speed cap so clusters don't blow up
        const sp2 = p.vx * p.vx + p.vy * p.vy;
        const MAX_SP = 2.2;
        if (sp2 > MAX_SP * MAX_SP) {
          const sp = Math.sqrt(sp2);
          p.vx = (p.vx / sp) * MAX_SP;
          p.vy = (p.vy / sp) * MAX_SP;
        }
        p.vx *= 0.992; p.vy *= 0.992;
        p.x += p.vx; p.y += p.vy; p.z += p.vz;
        if (p.z < 0) p.z = 1;
        if (p.z > 1) p.z = 0;
        if (p.x < 0) p.x = W;
        if (p.x > W) p.x = 0;
        if (p.y < 0) p.y = H;
        if (p.y > H) p.y = 0;

        const scale = 0.4 + p.z * 0.7;
        ctx.beginPath();
        ctx.arc(p.x, p.y, p.r * scale, 0, Math.PI * 2);
        ctx.fillStyle = p.c;
        ctx.globalAlpha = p.op * scale;
        ctx.fill();
      });

      // Connections
      ctx.globalAlpha = 1;
      const LINK_RANGE = 150;
      for (let i = 0; i < particles.length; i++) {
        const a = particles[i];
        for (let j = i + 1; j < particles.length; j++) {
          const b = particles[j];
          const dx = a.x - b.x, dy = a.y - b.y;
          const d = Math.sqrt(dx * dx + dy * dy);
          if (d < LINK_RANGE) {
            ctx.beginPath();
            ctx.moveTo(a.x, a.y);
            ctx.lineTo(b.x, b.y);
            const al = (1 - d / LINK_RANGE) * 0.18;
            ctx.strokeStyle = `rgba(10,110,245,${al})`;
            ctx.lineWidth = 0.6;
            ctx.stroke();
          }
        }
      }

      if (!reducedMotion.current) rafRef.current = requestAnimationFrame(tick);
    };
    tick();

    return () => {
      cancelAnimationFrame(rafRef.current);
      window.removeEventListener("resize", resize);
      if (interactive) window.removeEventListener("mousemove", onMouse);
    };
  }, [density, color1, color2, color3, interactive]);

  return (
    <canvas
      ref={canvasRef}
      style={{
        position: "absolute",
        inset: 0,
        width: "100%",
        height,
        pointerEvents: "none",
        opacity,
        zIndex: 1,
      }}
    />
  );
}
window.ParticleField = ParticleField;

/* =================================================================
   Tilt — 3D mouse-follow wrapper
   ================================================================= */
function Tilt({ children, max = 8, scale = 1.02, glare = false, style = {}, className = "" }) {
  const ref = fxRef(null);
  const [t, setT] = fxState({ x: 0, y: 0, mx: 50, my: 50, active: false });

  const onMove = (e) => {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    const px = (e.clientX - r.left) / r.width - 0.5;
    const py = (e.clientY - r.top) / r.height - 0.5;
    setT({ x: -py * max, y: px * max, mx: (px + 0.5) * 100, my: (py + 0.5) * 100, active: true });
  };
  const onLeave = () => setT({ x: 0, y: 0, mx: 50, my: 50, active: false });

  return (
    <div
      ref={ref}
      onMouseMove={onMove}
      onMouseLeave={onLeave}
      className={className}
      style={{
        ...style,
        transform: `perspective(1200px) rotateX(${t.x}deg) rotateY(${t.y}deg) scale(${t.active ? scale : 1})`,
        transition: t.active ? "transform 0.08s ease-out" : "transform 0.5s cubic-bezier(0.16, 1, 0.3, 1)",
        transformStyle: "preserve-3d",
        position: "relative",
      }}
    >
      {children}
      {glare && (
        <span
          aria-hidden
          style={{
            position: "absolute",
            inset: 0,
            borderRadius: "inherit",
            pointerEvents: "none",
            background: `radial-gradient(circle at ${t.mx}% ${t.my}%, rgba(255,255,255,${t.active ? 0.25 : 0}) 0%, rgba(255,255,255,0) 50%)`,
            transition: "opacity 0.4s ease",
            opacity: t.active ? 1 : 0,
            mixBlendMode: "overlay",
          }}
        />
      )}
    </div>
  );
}
window.Tilt = Tilt;

/* =================================================================
   MagneticBtn — children subtly follow cursor inside the trigger
   ================================================================= */
function MagneticBtn({ children, strength = 0.35, style = {}, ...rest }) {
  const ref = fxRef(null);
  const [d, setD] = fxState({ x: 0, y: 0 });

  const onMove = (e) => {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    const cx = r.left + r.width / 2;
    const cy = r.top + r.height / 2;
    const dx = (e.clientX - cx) * strength;
    const dy = (e.clientY - cy) * strength;
    setD({ x: dx, y: dy });
  };
  const onLeave = () => setD({ x: 0, y: 0 });

  return (
    <span
      ref={ref}
      onMouseMove={onMove}
      onMouseLeave={onLeave}
      className="magnetic"
      style={{
        ...style,
        transform: `translate3d(${d.x}px, ${d.y}px, 0)`,
      }}
      {...rest}
    >
      {children}
    </span>
  );
}
window.MagneticBtn = MagneticBtn;

/* =================================================================
   AnimatedNum — count up integer when in view
   For "12M+" / "$80K" / mixed strings — falls back to instant render.
   ================================================================= */
function parseTarget(str) {
  if (typeof str !== "string") return { n: Number(str) || 0, prefix: "", suffix: "" };
  // try simple numeric extraction
  const m = str.match(/^([^\d-]*)(-?\d+(?:[.,]\d+)?)(.*)$/);
  if (!m) return { n: null, prefix: "", suffix: "" };
  return {
    prefix: m[1] || "",
    n: parseFloat(m[2].replace(",", ".")),
    suffix: m[3] || "",
    integer: !m[2].includes(".") && !m[2].includes(","),
  };
}

function AnimatedNum({ value, duration = 1500 }) {
  const [ref, inView] = window.useInView(0.3);
  const [out, setOut] = fxState(typeof value === "string" ? value : String(value));

  const parsed = useMemo(() => parseTarget(value), [value]);

  fxEffect(() => {
    if (!inView) return;
    if (parsed.n === null) { setOut(String(value)); return; }
    const start = performance.now();
    const from = 0;
    const to = parsed.n;
    let raf;
    const animate = (now) => {
      const t = Math.min((now - start) / duration, 1);
      const ease = 1 - Math.pow(1 - t, 3);
      const v = from + (to - from) * ease;
      const display = parsed.integer ? Math.round(v) : (Math.round(v * 10) / 10);
      setOut(parsed.prefix + display + parsed.suffix);
      if (t < 1) raf = requestAnimationFrame(animate);
    };
    raf = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(raf);
  }, [inView, value]);

  return <span ref={ref}>{out}</span>;
}
window.AnimatedNum = AnimatedNum;

/* =================================================================
   Orb — soft radial gradient blob, decorative
   ================================================================= */
function Orb({ size = 480, color = "rgba(10,110,245,0.08)", style = {}, drift = false }) {
  return (
    <div
      aria-hidden
      style={{
        position: "absolute",
        width: size,
        height: size,
        borderRadius: "50%",
        background: `radial-gradient(circle, ${color} 0%, transparent 70%)`,
        pointerEvents: "none",
        animation: drift ? "drift 12s ease-in-out infinite" : "none",
        ...style,
      }}
    />
  );
}
window.Orb = Orb;

/* =================================================================
   FloatingShape — small floating geometric SVG that rotates
   ================================================================= */
function FloatingShape({ kind = "diamond", size = 40, color = "#0a6ef5", style = {}, speed = 14 }) {
  const path = useMemo(() => {
    if (kind === "diamond") return <path d={`M${size/2} 2 L${size-2} ${size/2} L${size/2} ${size-2} L2 ${size/2} Z`} stroke={color} strokeWidth="1" fill="none" />;
    if (kind === "triangle") return <path d={`M${size/2} 2 L${size-2} ${size-2} L2 ${size-2} Z`} stroke={color} strokeWidth="1" fill="none" />;
    if (kind === "circle") return (<>
      <circle cx={size/2} cy={size/2} r={size/2 - 2} stroke={color} strokeWidth="1" fill="none" />
      <circle cx={size/2} cy={size/2} r={size/3 - 2} stroke={color} strokeWidth="1" fill="none" />
    </>);
    if (kind === "ring") return <circle cx={size/2} cy={size/2} r={size/2 - 2} stroke={color} strokeWidth="1.5" fill="none" strokeDasharray="3 4" />;
    return null;
  }, [kind, size, color]);
  return (
    <div
      aria-hidden
      style={{
        position: "absolute",
        width: size, height: size,
        animation: `float-slow ${speed}s ease-in-out infinite`,
        opacity: 0.55,
        pointerEvents: "none",
        ...style,
      }}
    >
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ animation: `spin-slow ${speed * 2}s linear infinite` }}>
        {path}
      </svg>
    </div>
  );
}
window.FloatingShape = FloatingShape;

/* =================================================================
   AnimatedBlob — morphing organic blob for hero background
   ================================================================= */
function AnimatedBlob({ size = 480, gradient = "linear-gradient(135deg, #0a6ef5 0%, #05b89c 100%)", style = {}, opacity = 0.15 }) {
  return (
    <div
      aria-hidden
      style={{
        position: "absolute",
        width: size, height: size,
        background: gradient,
        animation: "blob-morph 18s ease-in-out infinite",
        filter: "blur(50px)",
        opacity,
        pointerEvents: "none",
        ...style,
      }}
    />
  );
}
window.AnimatedBlob = AnimatedBlob;

/* =================================================================
   GradientBtn — solid blue primary button with magnetic + tilt micro
   ================================================================= */
function GradientBtn({ children, to, onClick, tone = "blue", size = "md", style = {}, withArrow = true }) {
  const accent = tone === "warm" ? "#e85d00" : tone === "teal" ? "#05b89c" : "#0a6ef5";
  const sizes = {
    sm: { padding: "10px 20px", fontSize: 12 },
    md: { padding: "14px 28px", fontSize: 14 },
    lg: { padding: "16px 32px", fontSize: 14 },
  }[size];
  const inner = (
    <span style={{
      padding: sizes.padding,
      borderRadius: 100,
      background: accent,
      color: "#fff",
      fontFamily: "Inter, sans-serif",
      fontSize: sizes.fontSize,
      fontWeight: 600,
      letterSpacing: "0.02em",
      boxShadow: `0 8px 24px ${accent}40`,
      transition: "all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)",
      display: "inline-flex",
      alignItems: "center",
      gap: 10,
      ...style,
    }}>
      {children}
      {withArrow && <window.I.ArrowRight size={14} color="#fff" />}
    </span>
  );
  const wrapped = (
    <MagneticBtn strength={0.25}>{inner}</MagneticBtn>
  );
  if (to) return <window.Link to={to}>{wrapped}</window.Link>;
  return <button onClick={onClick} style={{ background: "none", border: "none", padding: 0 }}>{wrapped}</button>;
}
window.GradientBtn = GradientBtn;

/* =================================================================
   OutlineBtn — secondary button
   ================================================================= */
function OutlineBtn({ children, to, onClick, size = "md", style = {} }) {
  const sizes = {
    sm: { padding: "9px 18px", fontSize: 12 },
    md: { padding: "13px 26px", fontSize: 14 },
    lg: { padding: "15px 30px", fontSize: 14 },
  }[size];
  const inner = (
    <span
      style={{
        padding: sizes.padding,
        borderRadius: 100,
        background: "transparent",
        color: "#0d0d0d",
        border: "1.5px solid rgba(0,0,0,0.15)",
        fontFamily: "Inter, sans-serif",
        fontSize: sizes.fontSize,
        fontWeight: 500,
        letterSpacing: "0.02em",
        transition: "all 0.25s ease",
        display: "inline-flex",
        alignItems: "center",
        gap: 8,
        cursor: "pointer",
        ...style,
      }}
      onMouseEnter={(e) => { e.currentTarget.style.borderColor = "#0a6ef5"; e.currentTarget.style.color = "#0a6ef5"; }}
      onMouseLeave={(e) => { e.currentTarget.style.borderColor = "rgba(0,0,0,0.15)"; e.currentTarget.style.color = "#0d0d0d"; }}
    >
      {children}
    </span>
  );
  if (to) return <window.Link to={to}>{inner}</window.Link>;
  return <button onClick={onClick} style={{ background: "none", border: "none", padding: 0 }}>{inner}</button>;
}
window.OutlineBtn = OutlineBtn;
