// Shared hooks, primitives, and heartbeat
const { useState, useEffect, useRef, useMemo, useCallback } = React;

// --- Synchronized heartbeat (one interval, many subscribers) ---
const heartbeatSubs = new Set();
let heartbeatState = false;
setInterval(() => {
  heartbeatState = !heartbeatState;
  heartbeatSubs.forEach(fn => fn(heartbeatState));
}, 1000);

function useHeartbeat() {
  const [hb, setHb] = useState(heartbeatState);
  useEffect(() => {
    heartbeatSubs.add(setHb);
    return () => heartbeatSubs.delete(setHb);
  }, []);
  return hb;
}

// --- Reduced-motion awareness ---
function usePrefersReducedMotion() {
  const [pr, setPr] = useState(false);
  useEffect(() => {
    const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
    const update = () => setPr(mq.matches);
    update();
    mq.addEventListener('change', update);
    return () => mq.removeEventListener('change', update);
  }, []);
  return pr;
}

// --- Intro fade wrapper (stagger children via delay prop) ---
function FadeUp({ children, delay = 0, as: As = 'div', className = '', style = {}, ...rest }) {
  const ref = useRef(null);
  const [on, setOn] = useState(false);
  useEffect(() => {
    const t = setTimeout(() => setOn(true), 60 + delay * 1000);
    return () => clearTimeout(t);
  }, [delay]);
  return (
    <As
      ref={ref}
      className={className}
      style={{
        ...style,
        opacity: on ? 1 : 0,
        transform: on ? 'translateY(0)' : 'translateY(12px)',
        transition: 'opacity 800ms cubic-bezier(0.2, 0.7, 0.2, 1), transform 800ms cubic-bezier(0.2, 0.7, 0.2, 1)',
      }}
      {...rest}
    >
      {children}
    </As>
  );
}

// --- Wordmark ---
function Wordmark({ size = 20 }) {
  const hb = useHeartbeat();
  const height = size * 1.6;
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center',
      height,
      opacity: hb ? 1 : 0.88,
      transition: 'opacity 600ms ease',
    }}>
      <img
        src="/attora-mark.png"
        alt="Attora"
        style={{ height: '100%', width: 'auto', display: 'block' }}
      />
    </div>
  );
}

// --- Navbar ---
function Navbar({ onJoin }) {
  return (
    <nav style={{
      position: 'absolute', inset: '0 0 auto 0',
      zIndex: 30,
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      padding: '28px clamp(24px, 5vw, 56px)',
    }}>
      <Wordmark size={20} />
      <button onClick={onJoin} className="btn-ghost">Apply for Beta</button>
    </nav>
  );
}
const navLinkStyle = {
  fontFamily: 'var(--font-display)',
  fontSize: 13,
  fontWeight: 400,
  color: 'var(--fg-mute)',
  textDecoration: 'none',
  letterSpacing: '0.01em',
};

// --- Headline options (cycled via Tweak) ---
const HEADLINES = [
  { eyebrow: "Commerce Intelligence", h1: "Revenue your competitors can't reach.", sub: "Most funnels convert 3% of traffic. Attora works the other 97 — through conversations your buyers actually respond to, powered by data no one else has." },
  { eyebrow: "Signal, not noise", h1: "97% of your traffic leaves without a trace. We find them anyway.", sub: "Attora identifies your anonymous shoppers and reaches them through channels that still convert. Built for operators who don't settle for first-party only." },
  { eyebrow: "For DTC operators", h1: "The revenue channel hiding in your analytics gap.", sub: "Between the cookie deprecation and the attribution black hole, there's a 97% audience your competitors can't see. We turn it into your most profitable segment." },
  { eyebrow: "Post-funnel commerce", h1: "Your funnel is leaking.\nWe fish in the lake below.", sub: "Attora picks up the shoppers your pixel lost — enriches them, reaches them, and converts them through conversations that don't feel like marketing." },
];

// --- Status pill ---
function StatusPill({ children }) {
  const hb = useHeartbeat();
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 10,
      fontFamily: 'var(--font-mono)',
      fontSize: 11,
      letterSpacing: '0.2em',
      textTransform: 'uppercase',
      color: 'var(--fg-mute)',
    }}>
      <span style={{
        width: 6, height: 6, borderRadius: '50%',
        background: 'var(--accent)',
        opacity: hb ? 1 : 0.3,
        transition: 'opacity 600ms ease',
        boxShadow: hb ? '0 0 8px var(--accent)' : 'none',
      }} />
      {children}
    </div>
  );
}

// --- SVG signal line (Variation 1 background) ---
function SignalLine({ seed = 1, stroke = 'rgba(232, 168, 85, 0.2)', strokeWidth = 0.18, amplitude = 2.5 }) {
  const points = useMemo(() => {
    const pts = [];
    let r = seed * 9301;
    const rnd = () => { r = (r * 9301 + 49297) % 233280; return r / 233280; };
    for (let x = 0; x <= 100; x += 2) {
      const y = 50 + (rnd() - 0.5) * amplitude * 2 + Math.sin(x * 0.2 + seed) * amplitude * 0.5;
      pts.push(`${x},${y.toFixed(2)}`);
    }
    return pts.join(' ');
  }, [seed, amplitude]);
  return (
    <svg viewBox="0 0 100 100" preserveAspectRatio="none" style={{ width: '100%', height: '100%' }}>
      <polyline points={points} fill="none" stroke={stroke} strokeWidth={strokeWidth} vectorEffect="non-scaling-stroke" />
    </svg>
  );
}

// --- In-viewport hook ---
function useInView(threshold = 0.2) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setInView(true); io.disconnect(); }
    }, { threshold });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [threshold]);
  return [ref, inView];
}

// --- Count-up ---
function CountUp({ to, duration = 900, prefix = '', suffix = '', decimals = 0 }) {
  const [ref, inView] = useInView(0.4);
  const [v, setV] = useState(0);
  useEffect(() => {
    if (!inView) return;
    const start = performance.now();
    let raf;
    const tick = (t) => {
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setV(to * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [inView, to, duration]);
  return <span ref={ref} style={{ fontVariantNumeric: 'tabular-nums' }}>{prefix}{v.toFixed(decimals)}{suffix}</span>;
}

// --- Section wrapper with eyebrow label ---
function Section({ id, label, children, style = {} }) {
  return (
    <section id={id} style={{
      position: 'relative',
      padding: 'clamp(96px, 12vh, 160px) clamp(24px, 6vw, 96px)',
      borderTop: '1px solid var(--line)',
      ...style,
    }}>
      {label && (
        <div className="eyebrow" style={{ marginBottom: 56, display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--accent)' }} className="hb" />
          {label}
        </div>
      )}
      {children}
    </section>
  );
}

// --- Section divider: thin line with centered amber dot ---
function DotDivider() {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 16,
      margin: '0 auto', maxWidth: 1400,
      padding: '0 clamp(24px, 6vw, 96px)',
    }}>
      <div style={{ flex: 1, height: 1, background: 'var(--line)' }} />
      <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--accent)' }} className="hb" />
      <div style={{ flex: 1, height: 1, background: 'var(--line)' }} />
    </div>
  );
}

Object.assign(window, {
  useHeartbeat, usePrefersReducedMotion, FadeUp, Wordmark, Navbar,
  HEADLINES, StatusPill, SignalLine, useInView, CountUp, Section, DotDivider,
});
