/* ===== Shared hooks & helpers (loaded first) ===== */
const { useState, useEffect, useRef, useCallback } = React;

const prefersReduced = () =>
  window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

/* Boolean: is the ref's element in view (once or repeatedly) */
function useInView(options) {
  const { once = true, threshold = 0.25, rootMargin = '0px 0px -10% 0px' } = options || {};
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          setInView(true);
          if (once) io.unobserve(e.target);
        } else if (!once) {
          setInView(false);
        }
      });
    }, { threshold, rootMargin });
    io.observe(el);
    return () => io.disconnect();
  }, [once, threshold, rootMargin]);
  return [ref, inView];
}

/* Count up to target when active */
function useCountUp(target, active, duration = 1600) {
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!active) return;
    if (prefersReduced()) { setVal(target); return; }
    let raf, start;
    const step = (t) => {
      if (start == null) start = t;
      const p = Math.min((t - start) / duration, 1);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(target * eased);
      if (p < 1) raf = requestAnimationFrame(step);
      else setVal(target);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [target, active, duration]);
  return val;
}

/* Scroll progress (0..1) through a tall element while it passes the viewport */
function useScrollProgress(ref) {
  const [p, setP] = useState(0);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf = null;
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => {
        raf = null;
        const rect = el.getBoundingClientRect();
        const vh = window.innerHeight;
        const total = rect.height - vh;
        if (total <= 0) { setP(0); return; }
        const scrolled = Math.min(Math.max(-rect.top, 0), total);
        setP(scrolled / total);
      });
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [ref]);
  return p;
}

/* Parallax: translateY based on element position in viewport */
function useParallax(strength = 30) {
  const ref = useRef(null);
  const [y, setY] = useState(0);
  useEffect(() => {
    if (prefersReduced()) return;
    const el = ref.current;
    if (!el) return;
    let raf = null;
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => {
        raf = null;
        const rect = el.getBoundingClientRect();
        const vh = window.innerHeight;
        const center = rect.top + rect.height / 2;
        const ratio = (center - vh / 2) / vh; // -1..1
        setY(-ratio * strength);
      });
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => { window.removeEventListener('scroll', onScroll); if (raf) cancelAnimationFrame(raf); };
  }, [strength]);
  return [ref, y];
}

/* Global reveal observer — call once after mount */
function initReveals() {
  const els = document.querySelectorAll('[data-reveal]');
  if (prefersReduced()) {
    els.forEach((el) => el.classList.add('is-visible'));
    return;
  }
  const io = new IntersectionObserver((entries) => {
    entries.forEach((e) => {
      if (e.isIntersecting) {
        const delay = e.target.getAttribute('data-reveal-delay');
        if (delay) e.target.style.transitionDelay = delay + 'ms';
        e.target.classList.add('is-visible');
        io.unobserve(e.target);
      }
    });
  }, { threshold: 0.15, rootMargin: '0px 0px -8% 0px' });
  els.forEach((el) => io.observe(el));
}

/* Striped placeholder component */
function Striped({ label, variant = '', className = '', style, children }) {
  const v = variant === 'dark' ? 'striped--dark' : variant === 'blue' ? 'striped--blue' : '';
  return (
    <div className={`striped ${v} ${className}`} style={style}>
      {children}
      {label && <span className="striped__label">{label}</span>}
    </div>
  );
}

/* The brand logo glyph (from spec) */
function Logo({ size = 18, color = 'rgb(84, 84, 84)' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 256 256" fill="none" aria-hidden="true">
      <path fill={color} d="M 160 88 L 194 34 L 216 0 L 256 0 L 256 40 L 221.5 93.5 L 200 128 L 256 128 L 256 256 L 96 256 L 96 168 L 64.246 220 L 40 256 L 0 256 L 0 216 L 34 162 L 56 128 L 0 128 L 0 0 L 160 0 Z" />
    </svg>
  );
}

Object.assign(window, {
  prefersReduced, useInView, useCountUp, useScrollProgress, useParallax,
  initReveals, Striped, Logo,
});
