/* eslint-disable */
// Reveal-on-scroll hook + theme toggle + accent-color setter
function useReveal() {
  React.useEffect(() => {
    let raf = 0;
    const tick = () => {
      const vh = window.innerHeight;
      document.querySelectorAll('.reveal:not(.in)').forEach(el => {
        const r = el.getBoundingClientRect();
        // element top is above 88% of viewport AND bottom below 0
        if (r.top < vh * 0.88 && r.bottom > 0) {
          el.classList.add('in');
        }
      });
    };
    const schedule = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => { raf = 0; tick(); });
    };

    // initial pass (twice — once now, once after layout settles)
    tick();
    setTimeout(tick, 60);
    setTimeout(tick, 240);

    window.addEventListener('scroll', schedule, { passive: true });
    window.addEventListener('resize', schedule);

    const mo = new MutationObserver(schedule);
    mo.observe(document.body, { childList: true, subtree: true });

    return () => {
      window.removeEventListener('scroll', schedule);
      window.removeEventListener('resize', schedule);
      mo.disconnect();
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);
}

function applyAccent(name) {
  const map = {
    white:   { c: 'oklch(0.97 0.005 270)', fg: 'oklch(0.14 0.005 270)' },
    lime:    { c: 'oklch(0.86 0.19 128)',  fg: 'oklch(0.16 0.02 128)'  },
    orange:  { c: 'oklch(0.74 0.18 50)',   fg: 'oklch(0.16 0.02 50)'   },
    yellow:  { c: 'oklch(0.88 0.17 95)',   fg: 'oklch(0.18 0.02 95)'   },
    blue:    { c: 'oklch(0.70 0.18 250)',  fg: 'oklch(0.16 0.02 250)'  },
  };
  const t = map[name] || map.white;
  document.documentElement.style.setProperty('--accent', t.c);
  document.documentElement.style.setProperty('--accent-fg', t.fg);
}

function applyTheme(theme) {
  document.documentElement.dataset.theme = theme;
}

function applyDensity(d) {
  const map = { tight: '8px', cozy: '14px', loose: '22px' };
  document.documentElement.style.setProperty('--gap', map[d] || map.cozy);
}

function applyType(font) {
  const map = {
    'inter':         "'Inter', sans-serif",
    'inter-tight':   "'Inter Tight', 'Inter', sans-serif",
    'space-grotesk': "'Space Grotesk', sans-serif",
  };
  document.documentElement.style.setProperty('--font-display', map[font] || map['inter']);
  document.documentElement.style.setProperty('--font-mono',    map[font] || map['inter']);
  document.documentElement.style.setProperty('--font-fine',    map[font] || map['inter']);
}

window.useReveal = useReveal;
window.applyAccent = applyAccent;
window.applyTheme = applyTheme;
window.applyDensity = applyDensity;
window.applyType = applyType;

/**
 * Split a string into per-word spans with --w index for staggered CSS animation.
 * Adds `.in` on mount (next frame) so animation triggers.
 *  - text:   string to split (whitespace-separated)
 *  - baseDelay: starting delay in ms before first word
 *  - tone: optional 'em' to render those words muted (uses <em>)
 */
function SplitText({ text, baseDelay = 0, className = '', as = 'span' }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const t = setTimeout(() => {
      ref.current && ref.current.classList.add('in');
    }, 30);
    return () => clearTimeout(t);
  }, [text]);
  // Words only. Render each as inline-block with a real text-node space between siblings.
  const tokens = (text || '').match(/\S+/g) || [];
  const Comp = as;
  return (
    <Comp ref={ref} className={`split-text ${className}`} style={{ '--base-delay': `${baseDelay}ms` }}>
      {tokens.map((tok, i) => (
        <React.Fragment key={i}>
          <span className="split-word" style={{ '--w': i }}>{tok}</span>
          {i < tokens.length - 1 ? ' ' : ''}
        </React.Fragment>
      ))}
    </Comp>
  );
}

window.SplitText = SplitText;

/* ---------- Scroll hooks (shared by index + case pages) ----------
   Defined here in utils.jsx so BOTH pages have them (app.jsx only loads on
   the home page). */
function useScrolled(threshold = 24) {
  const [scrolled, setScrolled] = React.useState(false);
  React.useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > threshold);
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, [threshold]);
  return scrolled;
}
window.useScrolled = useScrolled;

// True once the hero has scrolled (mostly) out of view. Falls back to a small
// threshold when there's no .hero on the page (e.g. the case study page).
function useHeroPassed() {
  const [passed, setPassed] = React.useState(false);
  React.useEffect(() => {
    let raf = 0;
    const compute = () => {
      raf = 0;
      const hero = document.querySelector('.hero');
      const trigger = hero
        ? hero.getBoundingClientRect().bottom + window.scrollY - 90
        : 24;
      setPassed(window.scrollY > trigger);
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(compute); };
    compute();
    setTimeout(compute, 120);
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);
  return passed;
}
window.useHeroPassed = useHeroPassed;

/* ---------- Invert cursor ----------
   A circular dot that follows the mouse and inverts whatever is behind it
   (photographic negative) via backdrop-filter: invert(1).
   Disabled on touch / coarse-pointer devices. */
(function initInvertCursor() {
  const isFine = window.matchMedia('(hover: hover) and (pointer: fine)').matches;
  if (!isFine) return;

  const setup = () => {
    if (document.querySelector('.invert-cursor')) return;
    document.documentElement.classList.add('has-invert-cursor');

    const dot = document.createElement('div');
    dot.className = 'invert-cursor';
    document.body.appendChild(dot);

    let tx = window.innerWidth / 2, ty = window.innerHeight / 2; // target
    let cx = tx, cy = ty;                                         // current
    let active = false;
    let raf = 0;

    const render = () => {
      cx += (tx - cx) * 0.22;
      cy += (ty - cy) * 0.22;
      dot.style.transform = `translate(${cx}px, ${cy}px) translate(-50%, -50%)`;
      raf = requestAnimationFrame(render);
    };
    raf = requestAnimationFrame(render);

    window.addEventListener('mousemove', (e) => {
      tx = e.clientX; ty = e.clientY;
      if (!active) { active = true; dot.classList.add('is-active'); }
      // grow over interactive elements
      const hot = e.target.closest('a, button, [role="button"], input, textarea, .card');
      dot.classList.toggle('is-hover', !!hot);
    }, { passive: true });

    window.addEventListener('mouseout', (e) => {
      if (!e.relatedTarget && !e.toElement) {
        active = false; dot.classList.remove('is-active');
      }
    });

    document.addEventListener('mousedown', () => dot.classList.add('is-hover'));
    document.addEventListener('mouseup', () => dot.classList.remove('is-hover'));
  };

  if (document.body) setup();
  else document.addEventListener('DOMContentLoaded', setup);
})();
