// shared.jsx — logo, icons, nav, footer, router, Three.js background
// Loaded after React/Babel.

const { useState, useEffect, useRef, useMemo, useCallback, createContext, useContext } = React;

// ─────── Router (hash-based, instant page swap) ───────

const RouteContext = createContext({ route: '/', go: () => {} });

function useRouter() { return useContext(RouteContext); }

function RouterProvider({ children }) {
  const [route, setRoute] = useState(() => window.location.hash.replace('#', '') || '/');

  useEffect(() => {
    const onHash = () => {
      setRoute(window.location.hash.replace('#', '') || '/');
      window.scrollTo({ top: 0, behavior: 'instant' });
    };
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);

  const go = useCallback((path) => { window.location.hash = path; }, []);

  return <RouteContext.Provider value={{ route, go }}>{children}</RouteContext.Provider>;
}

function Link({ to, children, className, style, onClick }) {
  const { go } = useRouter();
  const handle = (e) => {
    e.preventDefault();
    onClick && onClick(e);
    go(to);
  };
  return <a href={'#' + to} className={className} style={style} onClick={handle}>{children}</a>;
}

// ─────── Tweaks context ───────
const TweaksContext = createContext(null);
function useTw() { return useContext(TweaksContext); }

// ─────── Icons (line, 1.5 weight) ───────

const Icon = ({ d, size = 16, stroke = 'currentColor', fill = 'none', children, sw = 1.6, ...rest }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke={stroke}
       strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" {...rest}>
    {d ? <path d={d} /> : children}
  </svg>
);

const Icons = {
  arrow: (p) => <Icon {...p}><path d="M5 12h14M13 5l7 7-7 7"/></Icon>,
  arrowDown: (p) => <Icon {...p}><path d="M12 5v14M5 12l7 7 7-7"/></Icon>,
  check: (p) => <Icon {...p} d="M20 6L9 17l-5-5"/>,
  spark: (p) => <Icon {...p}><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1"/></Icon>,
  bolt: (p) => <Icon {...p} d="M13 2L3 14h7l-1 8 10-12h-7l1-8z"/>,
  inbox: (p) => <Icon {...p}><path d="M22 12h-6l-2 3h-4l-2-3H2"/><path d="M5.45 5.11L2 12v6a2 2 0 002 2h16a2 2 0 002-2v-6l-3.45-6.89A2 2 0 0016.76 4H7.24a2 2 0 00-1.79 1.11z"/></Icon>,
  message: (p) => <Icon {...p} d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/>,
  calendar: (p) => <Icon {...p}><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></Icon>,
  book: (p) => <Icon {...p}><path d="M4 19.5A2.5 2.5 0 016.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z"/></Icon>,
  plug: (p) => <Icon {...p}><path d="M12 22v-5M9 8V2M15 8V2M5 8h14a1 1 0 011 1v3a7 7 0 01-7 7h-2a7 7 0 01-7-7V9a1 1 0 011-1z"/></Icon>,
  chart: (p) => <Icon {...p}><path d="M3 3v18h18M7 14l4-4 4 4 5-5"/></Icon>,
  globe: (p) => <Icon {...p}><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 010 20M12 2a15 15 0 000 20"/></Icon>,
  layers: (p) => <Icon {...p}><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></Icon>,
  search: (p) => <Icon {...p}><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></Icon>,
  bell: (p) => <Icon {...p}><path d="M18 8a6 6 0 00-12 0c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 01-3.4 0"/></Icon>,
  settings: (p) => <Icon {...p}><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 11-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 11-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 11-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 110-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 112.83-2.83l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 114 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 112.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 110 4h-.09a1.65 1.65 0 00-1.51 1z"/></Icon>,
  user: (p) => <Icon {...p}><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></Icon>,
  users: (p) => <Icon {...p}><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2M9 11a4 4 0 100-8 4 4 0 000 8zM23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"/></Icon>,
  send: (p) => <Icon {...p} d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/>,
  paperclip: (p) => <Icon {...p} d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/>,
  smile: (p) => <Icon {...p}><circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01"/></Icon>,
  filter: (p) => <Icon {...p} d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/>,
  more: (p) => <Icon {...p}><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></Icon>,
  plus: (p) => <Icon {...p}><path d="M12 5v14M5 12h14"/></Icon>,
  x: (p) => <Icon {...p}><path d="M18 6L6 18M6 6l12 12"/></Icon>,
  edit: (p) => <Icon {...p}><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></Icon>,
  trash: (p) => <Icon {...p}><path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/></Icon>,
  copy: (p) => <Icon {...p}><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></Icon>,
  external: (p) => <Icon {...p}><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3"/></Icon>,
  shield: (p) => <Icon {...p} d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>,
  zap: (p) => <Icon {...p} d="M13 2L3 14h7l-1 8 10-12h-7l1-8z"/>,
  brain: (p) => <Icon {...p}><path d="M9.5 2A2.5 2.5 0 0112 4.5v15a2.5 2.5 0 01-4.96.44 2.5 2.5 0 01-2.96-3.08 3 3 0 01-.34-5.58 2.5 2.5 0 011.32-4.24 2.5 2.5 0 014.44-1.04zM14.5 2A2.5 2.5 0 0012 4.5v15a2.5 2.5 0 004.96.44 2.5 2.5 0 002.96-3.08 3 3 0 00.34-5.58 2.5 2.5 0 00-1.32-4.24 2.5 2.5 0 00-4.44-1.04z"/></Icon>,
  lock: (p) => <Icon {...p}><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></Icon>,
  qr: (p) => <Icon {...p}><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><path d="M14 14h3v3h-3zM20 14h1M14 20h1M20 20h1M17 17h4v4"/></Icon>,
  upload: (p) => <Icon {...p}><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/></Icon>,
  star: (p) => <Icon {...p} d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>,
  flow: (p) => <Icon {...p}><circle cx="6" cy="6" r="3"/><circle cx="18" cy="18" r="3"/><path d="M9 6h7a3 3 0 013 3v6"/></Icon>,
  // Channel logos
  whatsapp: (p) => <Icon {...p} fill="currentColor" stroke="none" sw={0}><path d="M17.6 6.3A7.85 7.85 0 0012 4a7.93 7.93 0 00-6.79 12L4 20l4.1-1.07A7.93 7.93 0 0012 19.93a7.93 7.93 0 005.6-13.62zM12 18.6a6.6 6.6 0 01-3.36-.92l-.24-.14-2.49.65.66-2.43-.16-.25A6.6 6.6 0 0118.6 12 6.62 6.62 0 0112 18.6zm3.62-4.95c-.2-.1-1.18-.58-1.36-.65s-.31-.1-.45.1-.51.65-.62.78-.23.15-.43.05-.84-.31-1.6-1c-.59-.52-1-1.17-1.1-1.37s-.01-.31.09-.41.2-.23.3-.35.13-.2.2-.33.03-.25-.02-.35-.45-1.07-.61-1.46c-.16-.39-.32-.34-.45-.34-.12 0-.25-.02-.39-.02s-.35.05-.53.25-.7.69-.7 1.67.72 1.94.82 2.07 1.42 2.16 3.43 3.04c1.2.52 1.67.56 2.27.47.36-.05 1.18-.48 1.34-.95s.16-.86.11-.95c-.05-.08-.18-.13-.38-.23z"/></Icon>,
  instagram: (p) => <Icon {...p} sw={1.6}><rect x="2" y="2" width="20" height="20" rx="5"/><path d="M16 11.37A4 4 0 1112.63 8 4 4 0 0116 11.37zM17.5 6.5h.01"/></Icon>,
  facebook: (p) => <Icon {...p} fill="currentColor" stroke="none" sw={0} d="M22 12a10 10 0 10-11.56 9.88v-6.99H7.9v-2.89h2.54V9.84c0-2.51 1.49-3.89 3.78-3.89 1.09 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56V12h2.78l-.45 2.89h-2.33v6.99A10 10 0 0022 12z"/>,
  telegram: (p) => <Icon {...p} fill="currentColor" stroke="none" sw={0} d="M12 2a10 10 0 100 20 10 10 0 000-20zm4.64 6.8L15 16.6c-.13.59-.47.74-.95.46l-2.62-1.93-1.27 1.22c-.14.14-.26.26-.53.26l.19-2.69 4.89-4.42c.21-.19-.05-.29-.32-.1L8.34 13.2l-2.6-.81c-.57-.18-.58-.57.12-.85l10.18-3.92c.47-.18.88.11.6 1.18z"/>,
  viber: (p) => <Icon {...p}><path d="M14 2H10a8 8 0 00-8 8v6a4 4 0 002 3.46V21a1 1 0 001.5.87L8.5 20H14a8 8 0 008-8 8 8 0 00-8-8zM8 9c0-.5.4-1 1-1s1 .5 1 1c0 .5-.4 1-1 1s-1-.5-1-1zm4 6c-2 0-3.5-1.5-3.5-3 0 0 1 .5 3.5.5s3.5-.5 3.5-.5c0 1.5-1.5 3-3.5 3zm3-6c0-.5.4-1 1-1s1 .5 1 1c0 .5-.4 1-1 1s-1-.5-1-1z"/></Icon>,
  email: (p) => <Icon {...p}><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M22 7l-10 7L2 7"/></Icon>,
  website: (p) => <Icon {...p}><circle cx="12" cy="12" r="10"/><path d="M2 12h20"/></Icon>,
  sms: (p) => <Icon {...p}><path d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z"/></Icon>,
};

window.Icons = Icons;
window.Icon = Icon;

// ─────── Logo ───────

function Logo({ size = 16, showText = true }) {
  const box = Math.round(size * 1.75);
  return (
    <Link to="/" className="logo" style={{ fontSize: size }}>
      <span className="logo-mark" style={{
        width: box, height: box,
        background: 'transparent',
        boxShadow: 'none',
        position: 'relative', overflow: 'visible',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <svg viewBox="0 0 32 32" fill="none" style={{ width: box, height: box, display: 'block', filter: 'drop-shadow(0 4px 12px var(--accent-glow))' }}>
          <defs>
            <linearGradient id="logoBubble" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0%" stopColor="var(--accent)"/>
              <stop offset="100%" stopColor="#6EE7B7"/>
            </linearGradient>
            <linearGradient id="logoSheen" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor="rgba(255,255,255,0.35)"/>
              <stop offset="60%" stopColor="rgba(255,255,255,0)"/>
            </linearGradient>
          </defs>
          {/* Speech bubble shape — rounded square with tail */}
          <path
            d="M7 4.5h18a2.5 2.5 0 0 1 2.5 2.5v14a2.5 2.5 0 0 1-2.5 2.5H14.5l-5.2 4.4a.7.7 0 0 1-1.15-.54v-3.86H7A2.5 2.5 0 0 1 4.5 21V7A2.5 2.5 0 0 1 7 4.5z"
            fill="url(#logoBubble)"
          />
          {/* Top sheen */}
          <path
            d="M7 4.5h18a2.5 2.5 0 0 1 2.5 2.5v3H4.5V7A2.5 2.5 0 0 1 7 4.5z"
            fill="url(#logoSheen)"
            opacity="0.9"
          />
          {/* R inside the bubble */}
          <path
            d="M11.2 8.4c0-.5.4-.9.9-.9h4.5c2.4 0 4.3 1.7 4.3 4 0 1.6-.95 2.95-2.4 3.6l2.35 3.45c.32.47-.02 1.1-.6 1.1h-.95c-.32 0-.62-.16-.8-.43L16 15.7h-2v3.05c0 .42-.34.75-.75.75h-1.15c-.5 0-.9-.4-.9-.9V8.4zm2.8 1.3v3.55h3.05c1.07 0 1.85-.78 1.85-1.78s-.78-1.77-1.85-1.77H14z"
            fill="#052e1f"
          />
        </svg>
      </span>
      {showText && <span>ReplyFlow<span style={{ color: 'var(--accent)' }}>.</span><span style={{ fontWeight: 400, color: 'var(--text-2)' }}>ai</span></span>}
    </Link>
  );
}
window.Logo = Logo;

// ─────── Marketing nav ───────

function MarketingNav() {
  const [scrolled, setScrolled] = useState(false);
  const { route } = useRouter();

  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);

  const isActive = (p) => route === p || (p === '/' && route === '/');

  return (
    <nav className={'nav' + (scrolled ? ' scrolled' : '')}>
      <div className="container nav-inner">
        <div className="row gap-6">
          <Logo />
          <span className="badge badge-emerald" style={{ fontSize: 10, padding: '2px 8px' }}>BETA</span>
        </div>
        <div className="nav-links middle">
          <Link to="/features" className={'nav-link ' + (isActive('/features') ? 'active' : '')}>Features</Link>
          <Link to="/pricing" className={'nav-link ' + (isActive('/pricing') ? 'active' : '')}>Pricing</Link>
          <Link to="/dashboard" className="nav-link">Product</Link>
          <a className="nav-link" href="#" onClick={(e)=>e.preventDefault()}>Customers</a>
          <a className="nav-link" href="#" onClick={(e)=>e.preventDefault()}>Docs</a>
        </div>
        <div className="row gap-2">
          <Link to="/login" className="btn btn-ghost btn-sm">Sign in</Link>
          <Link to="/signup" className="btn btn-primary btn-sm">
            Start free <Icons.arrow size={14}/>
          </Link>
        </div>
      </div>
    </nav>
  );
}
window.MarketingNav = MarketingNav;

// ─────── Footer ───────

function Footer() {
  const cols = [
    { title: 'Product', links: ['Features', 'Pricing', 'Channels', 'Integrations', 'Changelog'] },
    { title: 'Industries', links: ['Restaurants', 'Cafés', 'Salons', 'Barbershops', 'Hotels'] },
    { title: 'Company', links: ['About', 'Customers', 'Careers', 'Press', 'Contact'] },
    { title: 'Resources', links: ['Docs', 'Help center', 'API reference', 'Status', 'Privacy'] },
  ];
  return (
    <footer className="footer">
      <div className="container">
        <div style={{ display: 'grid', gridTemplateColumns: '1.4fr repeat(4, 1fr)', gap: 48, marginBottom: 56 }}>
          <div className="col gap-4">
            <Logo />
            <p className="muted" style={{ fontSize: 14, maxWidth: 280, margin: 0, lineHeight: 1.55 }}>
              The customer communication hub for restaurants, cafés, and service businesses that want to stop missing messages.
            </p>
            <div className="row gap-2" style={{ marginTop: 8 }}>
              <span className="tag"><span className="tag-dot"/>All systems operational</span>
            </div>
          </div>
          {cols.map(c => (
            <div key={c.title} className="col gap-3">
              <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--text-1)', marginBottom: 4 }}>{c.title}</div>
              {c.links.map(l => (
                <a key={l} href="#" onClick={e=>e.preventDefault()} style={{ fontSize: 14, color: 'var(--text-3)', textDecoration: 'none' }}>{l}</a>
              ))}
            </div>
          ))}
        </div>
        <hr className="hr"/>
        <div className="row" style={{ justifyContent: 'space-between', paddingTop: 32, fontSize: 13, color: 'var(--text-3)', flexWrap: 'wrap', gap: 16 }}>
          <div>© 2026 ReplyFlow Labs · Built in Lisbon, made for the world.</div>
          <div className="row gap-4">
            <span>SOC 2 · Type II</span>
            <span>GDPR ready</span>
            <span>99.99% uptime</span>
          </div>
        </div>
      </div>
    </footer>
  );
}
window.Footer = Footer;

// ─────── Background — three.js particle field + mouse light + orbs/aurora/grid ───────

function ParticleField({ density = 1, mouse, palette = 'emerald' }) {
  const ref = useRef(null);
  const stateRef = useRef({ raf: 0, dispose: null });

  useEffect(() => {
    const canvas = ref.current;
    if (!canvas || !window.THREE) return;
    const THREE = window.THREE;
    const w = () => canvas.clientWidth;
    const h = () => canvas.clientHeight;

    const renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
    renderer.setSize(w(), h(), false);

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(60, w() / h(), 0.1, 1000);
    camera.position.z = 50;

    // Particles
    const count = Math.floor(900 * density);
    const positions = new Float32Array(count * 3);
    const speeds = new Float32Array(count);
    for (let i = 0; i < count; i++) {
      positions[3*i] = (Math.random() - 0.5) * 200;
      positions[3*i+1] = (Math.random() - 0.5) * 120;
      positions[3*i+2] = (Math.random() - 0.5) * 80;
      speeds[i] = Math.random() * 0.02 + 0.005;
    }
    const geo = new THREE.BufferGeometry();
    geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));

    const colorMain = palette === 'blue' ? 0x3B82F6 : palette === 'violet' ? 0x8B5CF6 : 0x10B981;

    const mat = new THREE.PointsMaterial({
      color: colorMain,
      size: 0.6,
      transparent: true,
      opacity: 0.7,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const points = new THREE.Points(geo, mat);
    scene.add(points);

    // Glowing larger center particles
    const glowGeo = new THREE.BufferGeometry();
    const glowCount = 60;
    const glowPos = new Float32Array(glowCount * 3);
    for (let i = 0; i < glowCount; i++) {
      glowPos[3*i] = (Math.random()-0.5)*120;
      glowPos[3*i+1] = (Math.random()-0.5)*80;
      glowPos[3*i+2] = (Math.random()-0.5)*40;
    }
    glowGeo.setAttribute('position', new THREE.BufferAttribute(glowPos, 3));
    const glowMat = new THREE.PointsMaterial({
      color: 0x34D399,
      size: 2.5,
      transparent: true,
      opacity: 0.5,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const glowPoints = new THREE.Points(glowGeo, glowMat);
    scene.add(glowPoints);

    let t = 0;
    const animate = () => {
      t += 0.005;
      const pos = geo.attributes.position.array;
      for (let i = 0; i < count; i++) {
        pos[3*i+2] += speeds[i];
        if (pos[3*i+2] > 60) pos[3*i+2] = -40;
      }
      geo.attributes.position.needsUpdate = true;

      // Subtle parallax from mouse
      const mx = (mouse?.current?.x || 0);
      const my = (mouse?.current?.y || 0);
      camera.position.x += (mx * 6 - camera.position.x) * 0.03;
      camera.position.y += (-my * 4 - camera.position.y) * 0.03;
      camera.lookAt(0, 0, 0);

      points.rotation.y += 0.0006;
      glowPoints.rotation.y -= 0.0008;

      renderer.render(scene, camera);
      stateRef.current.raf = requestAnimationFrame(animate);
    };
    animate();

    const onResize = () => {
      camera.aspect = w() / h();
      camera.updateProjectionMatrix();
      renderer.setSize(w(), h(), false);
    };
    window.addEventListener('resize', onResize);

    stateRef.current.dispose = () => {
      window.removeEventListener('resize', onResize);
      cancelAnimationFrame(stateRef.current.raf);
      geo.dispose(); mat.dispose();
      glowGeo.dispose(); glowMat.dispose();
      renderer.dispose();
    };
    return () => stateRef.current.dispose && stateRef.current.dispose();
  }, [density, palette]);

  return <canvas ref={ref} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}/>;
}
window.ParticleField = ParticleField;

function BackgroundStage({ variant = 'mesh', anim = 'cinematic' }) {
  const mouseRef = useRef({ x: 0, y: 0 });
  const lightRef = useRef(null);

  useEffect(() => {
    if (anim === 'off') return;
    const onMove = (e) => {
      const x = (e.clientX / window.innerWidth) * 2 - 1;
      const y = (e.clientY / window.innerHeight) * 2 - 1;
      mouseRef.current = { x, y };
      if (lightRef.current) {
        lightRef.current.style.transform = `translate(${e.clientX - 300}px, ${e.clientY - 300}px)`;
      }
    };
    window.addEventListener('mousemove', onMove);
    return () => window.removeEventListener('mousemove', onMove);
  }, [anim]);

  return (
    <div className="bg-stage">
      {variant !== 'minimal' && <div className="bg-noise"/>}
      {variant === 'mesh' && <>
        <div className="bg-orb a"/>
        <div className="bg-orb b"/>
        <div className="bg-orb c"/>
      </>}
      {variant === 'aurora' && <div className="bg-aurora"/>}
      {variant === 'starfield' && anim !== 'off' && <ParticleField density={1} mouse={mouseRef}/>}
      {variant !== 'minimal' && <div className="bg-grid"/>}
      {anim === 'cinematic' && variant !== 'minimal' && (
        <div ref={lightRef} style={{
          position: 'absolute',
          width: 600, height: 600,
          borderRadius: '50%',
          background: 'radial-gradient(circle, rgba(16,185,129,0.08) 0%, transparent 60%)',
          pointerEvents: 'none',
          willChange: 'transform',
          transition: 'transform 0.4s cubic-bezier(0.22,1,0.36,1)',
        }}/>
      )}
    </div>
  );
}
window.BackgroundStage = BackgroundStage;

// ─────── Reveal on scroll ───────

function Reveal({ children, delay = 0, className = '', style }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const obs = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) {
        setTimeout(() => el.classList.add('in'), delay);
        obs.disconnect();
      }
    }, { threshold: 0.15 });
    obs.observe(el);
    return () => obs.disconnect();
  }, [delay]);
  return <div ref={ref} className={'reveal ' + className} style={style}>{children}</div>;
}
window.Reveal = Reveal;

window.useRouter = useRouter;
window.RouterProvider = RouterProvider;
window.Link = Link;
window.TweaksContext = TweaksContext;
window.useTw = useTw;
