// Scroll utilities + shared luxury primitives

// useScrollProgress — reports 0..1 progress through an element while it scrolls past viewport
const useScrollProgress = (ref) => {
  const [p, setP] = React.useState(0);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    // Find scrolling parent (artboard root)
    let scroller = el.parentElement;
    while (scroller && scroller !== document.body) {
      const o = getComputedStyle(scroller).overflowY;
      if (o === 'auto' || o === 'scroll') break;
      scroller = scroller.parentElement;
    }
    if (!scroller || scroller === document.body) scroller = window;
    let raf = 0;
    const onScroll = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        const rect = el.getBoundingClientRect();
        const sRect = scroller === window
          ? { top: 0, height: window.innerHeight }
          : scroller.getBoundingClientRect();
        const vh = sRect.height;
        const total = rect.height - vh;
        const passed = sRect.top - rect.top + 0; // negative when above
        const px = scroller === window ? -rect.top : (sRect.top - rect.top);
        const prog = Math.max(0, Math.min(1, px / total));
        setP(prog);
      });
    };
    scroller.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => { scroller.removeEventListener('scroll', onScroll); cancelAnimationFrame(raf); };
  }, []);
  return p;
};

const LReveal = ({ children, delay = 0, as: As = 'div', className = '', ...rest }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    el.classList.add('armed');
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setTimeout(() => el.classList.add('in'), delay); io.disconnect(); }
    }, { threshold: 0.05, rootMargin: '0px 0px -10% 0px' });
    io.observe(el);
    // Failsafe — never leave content invisible
    const t = setTimeout(() => el.classList.add('in'), 2200 + delay);
    return () => { io.disconnect(); clearTimeout(t); };
  }, [delay]);
  return <As ref={ref} className={`ccl-rev ${className}`} {...rest}>{children}</As>;
};

// Letter-by-letter reveal for headline
const LLetters = ({ text, className = '' }) => {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) {
        el.classList.add('in');
        const spans = el.querySelectorAll('span');
        spans.forEach((s, i) => { s.style.transitionDelay = `${i * 18}ms`; });
        io.disconnect();
      }
    }, { threshold: 0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [text]);
  return (
    <span ref={ref} className={`ccl-letters ${className}`}>
      {text.split('').map((c, i) => (
        <span key={i}>{c === ' ' ? '\u00A0' : c}</span>
      ))}
    </span>
  );
};

const LCountUp = ({ to, suffix = '', decimals = 0, duration = 1800 }) => {
  const [v, setV] = React.useState(0);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) {
        const start = performance.now();
        const tick = (now) => {
          const t = Math.min(1, (now - start) / duration);
          const eased = 1 - Math.pow(1 - t, 4);
          setV(to * eased);
          if (t < 1) requestAnimationFrame(tick);
        };
        requestAnimationFrame(tick);
        io.disconnect();
      }
    }, { threshold: 0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [to, duration]);
  const display = decimals > 0 ? v.toFixed(decimals) : Math.round(v).toLocaleString();
  return <span ref={ref}>{display}{suffix}</span>;
};

const LIcon = ({ name, size = 18, stroke = 1.4 }) => {
  const paths = {
    arrR: <path d="M5 12h14M13 6l6 6-6 6"/>,
    arrUR: <path d="M7 17L17 7M9 7h8v8"/>,
    chevD: <path d="M6 9l6 6 6-6"/>,
    chip: <><rect x="6" y="6" width="12" height="12" rx="2"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M1 9h3M1 15h3M20 9h3M20 15h3"/></>,
    lock: <><rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></>,
    shield: <><path d="M12 3l8 4v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7z"/><path d="M9 12l2 2 4-4"/></>,
    plug: <><path d="M9 7V3M15 7V3M6 11h12v3a6 6 0 0 1-12 0z"/><path d="M12 17v4"/></>,
    globe: <><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></>,
  };
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor"
         strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round" style={{flexShrink:0}}>
      {paths[name]}
    </svg>
  );
};

Object.assign(window, { useScrollProgress, LReveal, LLetters, LCountUp, LIcon });
