// Shared effects used across all three variants.
// Each effect is scoped to a `root` element (the artboard scroll container),
// so multiple variants can coexist on the same canvas without interfering.

// ─── Grain overlay (SVG noise, css-only, performant) ────────────
function Grain({ opacity = 0.08 }) {
  const svg = `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.6 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>")`;
  return (
    <div style={{
      position: 'absolute', inset: 0, pointerEvents: 'none',
      backgroundImage: svg, backgroundSize: '220px 220px',
      opacity, mixBlendMode: 'multiply', zIndex: 50,
    }} />
  );
}

// ─── Aurora gradient blobs, slow drift, light variant ───────────
function Aurora({ palette = ['#e8e3d8', '#dfe4e0', '#e6dedd', '#eee9dc'] }) {
  const blobs = React.useMemo(() => (
    palette.map((c, i) => ({
      c,
      size: 60 + i * 12,
      x: [15, 75, 35, 85][i % 4],
      y: [20, 30, 70, 60][i % 4],
      delay: i * 2.5,
      dur: 24 + i * 4,
    }))
  ), [palette]);
  return (
    <div style={{
      position: 'absolute', inset: 0, overflow: 'hidden',
      pointerEvents: 'none', zIndex: 0,
    }}>
      <style>{`
        @keyframes aurora-drift-a { 0%,100%{transform:translate(0,0) scale(1);} 50%{transform:translate(8%, -6%) scale(1.15);} }
        @keyframes aurora-drift-b { 0%,100%{transform:translate(0,0) scale(1);} 50%{transform:translate(-10%, 8%) scale(1.1);} }
        @keyframes aurora-drift-c { 0%,100%{transform:translate(0,0) scale(1);} 50%{transform:translate(6%, 10%) scale(0.9);} }
        @keyframes aurora-drift-d { 0%,100%{transform:translate(0,0) scale(1);} 50%{transform:translate(-7%, -9%) scale(1.2);} }
      `}</style>
      {blobs.map((b, i) => (
        <div key={i} style={{
          position: 'absolute',
          left: `${b.x}%`, top: `${b.y}%`,
          width: `${b.size}vmin`, height: `${b.size}vmin`,
          background: `radial-gradient(circle at 50% 50%, ${b.c} 0%, transparent 65%)`,
          filter: 'blur(40px)',
          animation: `aurora-drift-${['a','b','c','d'][i%4]} ${b.dur}s ease-in-out ${b.delay}s infinite`,
          transform: 'translate(-50%, -50%)',
          mixBlendMode: 'multiply',
        }} />
      ))}
    </div>
  );
}

// ─── Floating orbs (fine, small dots drifting up) ───────────────
function Orbs({ count = 18, color = 'rgba(20,20,20,0.4)' }) {
  const orbs = React.useMemo(() => (
    Array.from({ length: count }, (_, i) => ({
      x: Math.random() * 100,
      size: 2 + Math.random() * 4,
      dur: 18 + Math.random() * 22,
      delay: -Math.random() * 30,
    }))
  ), [count]);
  return (
    <div style={{
      position: 'absolute', inset: 0, overflow: 'hidden',
      pointerEvents: 'none', zIndex: 1,
    }}>
      <style>{`
        @keyframes orb-rise {
          0%   { transform: translate(0, 110%);   opacity: 0; }
          10%  { opacity: 0.9; }
          90%  { opacity: 0.7; }
          100% { transform: translate(20px, -20%); opacity: 0; }
        }
      `}</style>
      {orbs.map((o, i) => (
        <div key={i} style={{
          position: 'absolute',
          left: `${o.x}%`, bottom: 0,
          width: o.size, height: o.size, borderRadius: '50%',
          background: color,
          animation: `orb-rise ${o.dur}s linear ${o.delay}s infinite`,
        }} />
      ))}
    </div>
  );
}

// ─── Animated diagonal/grid lines that draw in on scroll ────────
function GridLines({ density = 8, color = 'rgba(10,10,10,0.08)' }) {
  const cols = Array.from({ length: density }, (_, i) => i);
  return (
    <div style={{
      position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 1,
    }}>
      {cols.map((i) => (
        <div key={`v${i}`} style={{
          position: 'absolute', top: 0, bottom: 0,
          left: `${(100 / density) * i}%`,
          width: 1, background: color,
        }} />
      ))}
    </div>
  );
}

// ─── Reveal on scroll: IntersectionObserver within a root ───────
// Usage: <Reveal root={scrollRef.current}>...</Reveal>
function useReveal(root, options = {}) {
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(false);
  React.useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    // root can be null → viewport, fine for standalone pages too
    const io = new IntersectionObserver((entries) => {
      for (const e of entries) {
        if (e.isIntersecting) { setShown(true); io.unobserve(e.target); }
      }
    }, { root: root || null, threshold: 0.15, rootMargin: '0px 0px -10% 0px', ...options });
    io.observe(el);
    return () => io.disconnect();
  }, [root]);
  return [ref, shown];
}

function Reveal({ children, root, delay = 0, y = 24, as: As = 'div', style = {}, ...rest }) {
  const [ref, shown] = useReveal(root);
  return (
    <As ref={ref} style={{
      transform: shown ? 'translate3d(0,0,0)' : `translate3d(0,${y}px,0)`,
      opacity: shown ? 1 : 0,
      transition: `transform 1s cubic-bezier(.2,.7,.1,1) ${delay}s, opacity 1s ease ${delay}s`,
      ...style,
    }} {...rest}>{children}</As>
  );
}

// ─── Parallax on scroll — y offset = depth * scrollTop ──────────
function useParallax(root, depth = 0.2) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const scroller = root || window;
    const getTop = () => (root ? root.scrollTop : window.scrollY);
    let raf = 0;
    const tick = () => {
      raf = 0;
      if (ref.current) {
        const y = getTop() * depth;
        ref.current.style.transform = `translate3d(0, ${-y}px, 0)`;
      }
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(tick); };
    scroller.addEventListener('scroll', onScroll, { passive: true });
    tick();
    return () => { scroller.removeEventListener('scroll', onScroll); cancelAnimationFrame(raf); };
  }, [root, depth]);
  return ref;
}

// ─── 3D tilt on hover ───────────────────────────────────────────
function Tilt({ children, max = 8, style = {}, ...rest }) {
  const ref = React.useRef(null);
  const onMove = (e) => {
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const x = (e.clientX - r.left) / r.width - 0.5;
    const y = (e.clientY - r.top) / r.height - 0.5;
    el.style.transform = `perspective(900px) rotateX(${-y * max}deg) rotateY(${x * max}deg) translate3d(0,0,0)`;
  };
  const onLeave = () => { if (ref.current) ref.current.style.transform = ''; };
  return (
    <div ref={ref} onMouseMove={onMove} onMouseLeave={onLeave}
      style={{ transition: 'transform .4s cubic-bezier(.2,.7,.1,1)', transformStyle: 'preserve-3d', ...style }}
      {...rest}
    >{children}</div>
  );
}

// ─── Magnet cursor — one per artboard, constrained to its rect ──
function MagnetCursor({ root }) {
  const dotRef = React.useRef(null);
  const ringRef = React.useRef(null);
  React.useEffect(() => {
    if (!root) return;
    const dot = dotRef.current, ring = ringRef.current;
    let rafId = 0;
    let tx = 0, ty = 0, rx = 0, ry = 0;
    let dx = 0, dy = 0; // dot target
    let hover = false;
    const tick = () => {
      rafId = 0;
      rx += (tx - rx) * 0.18;
      ry += (ty - ry) * 0.18;
      dx += (tx - dx) * 0.55;
      dy += (ty - dy) * 0.55;
      if (ring) ring.style.transform = `translate3d(${rx}px, ${ry}px, 0) translate(-50%,-50%) scale(${hover ? 1.6 : 1})`;
      if (dot) dot.style.transform = `translate3d(${dx}px, ${dy}px, 0) translate(-50%,-50%)`;
    };
    const schedule = () => { if (!rafId) rafId = requestAnimationFrame(tick); };
    const onMove = (e) => {
      const r = root.getBoundingClientRect();
      tx = e.clientX - r.left;
      ty = e.clientY - r.top;
      hover = !!e.target.closest('[data-magnet]');
      schedule();
    };
    const onEnter = () => { if (dot) dot.style.opacity = '1'; if (ring) ring.style.opacity = '1'; };
    const onLeave = () => { if (dot) dot.style.opacity = '0'; if (ring) ring.style.opacity = '0'; };
    root.addEventListener('mousemove', onMove);
    root.addEventListener('mouseenter', onEnter);
    root.addEventListener('mouseleave', onLeave);
    return () => {
      root.removeEventListener('mousemove', onMove);
      root.removeEventListener('mouseenter', onEnter);
      root.removeEventListener('mouseleave', onLeave);
      cancelAnimationFrame(rafId);
    };
  }, [root]);
  return (
    <>
      <div ref={ringRef} style={{
        position: 'absolute', left: 0, top: 0, width: 38, height: 38,
        border: '1px solid #0a0a0a', borderRadius: '50%',
        pointerEvents: 'none', zIndex: 999, opacity: 0,
        transition: 'opacity .3s, border-color .3s',
        mixBlendMode: 'difference', borderColor: '#fff',
      }} />
      <div ref={dotRef} style={{
        position: 'absolute', left: 0, top: 0, width: 6, height: 6,
        background: '#0a0a0a', borderRadius: '50%',
        pointerEvents: 'none', zIndex: 999, opacity: 0,
        transition: 'opacity .3s',
        mixBlendMode: 'difference', background: '#fff',
      }} />
    </>
  );
}

// ─── Animated underline link (magnet target) ────────────────────
function NavLink({ children, href = '#' }) {
  return (
    <a href={href} data-magnet style={{
      color: 'inherit', textDecoration: 'none', position: 'relative',
      padding: '4px 0', fontSize: 13, letterSpacing: 0.2,
    }}
    onMouseEnter={(e) => { const u = e.currentTarget.querySelector('i'); if (u) u.style.transform = 'scaleX(1)'; }}
    onMouseLeave={(e) => { const u = e.currentTarget.querySelector('i'); if (u) u.style.transform = 'scaleX(0)'; }}
    >
      {children}
      <i style={{
        position: 'absolute', left: 0, right: 0, bottom: 0, height: 1,
        background: 'currentColor', transform: 'scaleX(0)', transformOrigin: 'left',
        transition: 'transform .5s cubic-bezier(.2,.7,.1,1)',
      }} />
    </a>
  );
}

// ─── Placeholder image (striped SVG w/ monospace caption) ───────
function Placeholder({ label = 'product shot', ratio = 16/9, style = {}, dark = false }) {
  const bg = dark ? '#0a0a0a' : '#eae5dc';
  const fg = dark ? '#fff' : '#0a0a0a';
  return (
    <div style={{
      position: 'relative', width: '100%', aspectRatio: ratio, overflow: 'hidden',
      background: bg, color: fg, ...style,
    }}>
      <svg width="100%" height="100%" preserveAspectRatio="none" viewBox="0 0 100 100" style={{ position: 'absolute', inset: 0 }}>
        <defs>
          <pattern id={`stripes-${label.replace(/\s/g,'')}`} patternUnits="userSpaceOnUse" width="4" height="4" patternTransform="rotate(45)">
            <line x1="0" y1="0" x2="0" y2="4" stroke={fg} strokeOpacity="0.12" strokeWidth="1" />
          </pattern>
        </defs>
        <rect width="100" height="100" fill={`url(#stripes-${label.replace(/\s/g,'')})`} />
      </svg>
      <div style={{
        position: 'absolute', left: 14, bottom: 12,
        fontFamily: 'JetBrains Mono, monospace', fontSize: 11,
        letterSpacing: 0.5, opacity: 0.7, textTransform: 'uppercase',
      }}>
        <span style={{ opacity: 0.5 }}>◇</span>&nbsp;{label}
      </div>
    </div>
  );
}

// ─── useIsMobile — single-source-of-truth viewport check (≤768px) ────
function useIsMobile(breakpoint = 768) {
  const [isMobile, setIsMobile] = React.useState(
    typeof window !== 'undefined' ? window.innerWidth <= breakpoint : false
  );
  React.useEffect(() => {
    const mq = window.matchMedia(`(max-width: ${breakpoint}px)`);
    const handler = (e) => setIsMobile(e.matches);
    mq.addEventListener ? mq.addEventListener('change', handler) : mq.addListener(handler);
    return () => mq.removeEventListener ? mq.removeEventListener('change', handler) : mq.removeListener(handler);
  }, [breakpoint]);
  return isMobile;
}

Object.assign(window, {
  Grain, Aurora, Orbs, GridLines,
  Reveal, useReveal, useParallax, Tilt, MagnetCursor, NavLink, Placeholder,
  useIsMobile,
});
