// Shared primitives
const { useEffect, useRef, useState, useMemo, useLayoutEffect } = React;

function useInView(opts = {}) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          setInView(true);
          io.unobserve(el);
        }
      });
    }, { threshold: opts.threshold ?? 0.15, rootMargin: opts.rootMargin ?? '0px 0px -10% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return [ref, inView];
}

// Reveal wrapper — plays once on view
function Reveal({ children, delay = 0, className = '', as = 'div', style, ...rest }) {
  const [ref, inView] = useInView();
  const Tag = as;
  const cls = `reveal ${inView ? 'in' : ''} ${className}`;
  const s = { transitionDelay: `${delay}ms`, ...style };
  return <Tag ref={ref} className={cls} style={s} {...rest}>{children}</Tag>;
}

// Mask reveal — each child line slides up behind mask
function MaskLines({ lines, delay = 0, tag = 'h1', className, style }) {
  const [ref, inView] = useInView();
  const Tag = tag;
  return (
    <Tag ref={ref} className={`${className || ''} ${inView ? 'in' : ''}`} style={style}>
      {lines.map((ln, i) => (
        <span key={i} className="mask-line" style={{ transitionDelay: `${delay + i * 90}ms` }}>
          <span style={{ transitionDelay: `${delay + i * 90}ms` }}>{ln}</span>
        </span>
      ))}
    </Tag>
  );
}

// Label tag (numbered)
function Label({ n, children }) {
  return (
    <div className="mono" style={{ display: 'flex', gap: 14, alignItems: 'center', color: 'var(--fg-mute)' }}>
      {n && <span style={{ color: 'var(--accent)' }}>{n}</span>}
      <span>{children}</span>
    </div>
  );
}

// Primary CTA button
function CTA({ children, variant = 'primary', onClick, arrow = true, size = 'md' }) {
  const base = {
    display: 'inline-flex', alignItems: 'center', gap: 14,
    padding: size === 'lg' ? '22px 30px' : '16px 24px',
    borderRadius: 999,
    fontSize: size === 'lg' ? 16 : 14,
    fontWeight: 500,
    letterSpacing: '-0.01em',
    transition: 'all .35s cubic-bezier(.2,.7,.2,1)',
    border: '1px solid transparent',
    position: 'relative', overflow: 'hidden',
  };
  const styles = variant === 'primary'
    ? { ...base, background: 'var(--fg)', color: 'var(--bg)' }
    : variant === 'accent'
    ? { ...base, background: 'var(--accent)', color: 'var(--bg)' }
    : { ...base, background: 'transparent', color: 'var(--fg)', border: '1px solid var(--line)' };

  return (
    <button onClick={onClick} className="cta-btn" style={styles}
      onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateY(-2px)'; }}
      onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; }}
    >
      <span>{children}</span>
      {arrow && (
        <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
          <path d="M3 11L11 3M11 3H5M11 3V9" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
        </svg>
      )}
    </button>
  );
}

// Section wrapper with vertical rhythm
function Section({ children, id, style, pad = 'lg' }) {
  return <section id={id} className={`section-pad-${pad}`} style={style}>{children}</section>;
}

// Thin rule
function Rule({ color = 'var(--line-soft)' }) {
  return <div style={{ height: 1, background: color, width: '100%' }} />;
}

// Tracks progress of a target element through viewport (0 = enters bottom, 1 = leaves top)
function useScrollProgress(targetRef, opts = {}) {
  const [p, setP] = useState(0);
  useEffect(() => {
    if (!targetRef.current) return;
    let raf = 0;
    const calc = () => {
      const el = targetRef.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const start = opts.start ?? vh;          // when top enters bottom of vp
      const end = opts.end ?? -r.height;       // when bottom leaves top
      const span = start - end;
      const raw = (start - r.top) / span;
      setP(Math.max(0, Math.min(1, raw)));
    };
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => { raf = 0; calc(); });
    };
    calc();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); };
  }, []);
  return p;
}

// Parallax translate
function Parallax({ children, strength = 0.2, className, style }) {
  const ref = useRef(null);
  const p = useScrollProgress(ref);
  const y = (p - 0.5) * strength * 200; // -strength*100 to +strength*100 px
  return (
    <div ref={ref} className={className} style={{ ...style, transform: `translate3d(0, ${y}px, 0)`, willChange: 'transform' }}>
      {children}
    </div>
  );
}

// Split text per-character reveal on scroll
function SplitReveal({ text, className, style, charDelay = 22, delay = 0, tag = 'span' }) {
  const [ref, inView] = useInView();
  const chars = [...text];
  const Tag = tag;
  return (
    <Tag ref={ref} className={className} style={style}>
      {chars.map((c, i) => (
        <span key={i} style={{
          display: 'inline-block',
          transform: inView ? 'translateY(0) rotate(0)' : 'translateY(110%) rotate(6deg)',
          opacity: inView ? 1 : 0,
          transition: `transform .9s cubic-bezier(.2,.7,.2,1) ${delay + i * charDelay}ms, opacity .6s ${delay + i * charDelay}ms`,
          whiteSpace: c === ' ' ? 'pre' : 'normal',
        }}>{c}</span>
      ))}
    </Tag>
  );
}

// Image placeholder with scroll-driven clip-path mask reveal + parallax inside
function MaskedImage({ label, caption, ratio = '4 / 5', tone = 'warm', strength = 0.15, children }) {
  const ref = useRef(null);
  const p = useScrollProgress(ref);
  const clip = Math.max(0, Math.min(100, (1 - p * 1.6) * 100));
  const y = (p - 0.5) * strength * 260;

  const bgs = {
    warm: 'repeating-linear-gradient(135deg, oklch(0.22 0.01 70) 0 14px, oklch(0.19 0.01 70) 14px 28px)',
    cool: 'repeating-linear-gradient(135deg, oklch(0.22 0.008 230) 0 14px, oklch(0.19 0.008 230) 14px 28px)',
    amber: 'repeating-linear-gradient(135deg, oklch(0.72 0.13 70) 0 22px, oklch(0.64 0.13 70) 22px 44px)',
    dark: 'repeating-linear-gradient(135deg, oklch(0.18 0.005 85) 0 14px, oklch(0.14 0.005 85) 14px 28px)',
  };

  return (
    <figure ref={ref} style={{ position: 'relative', width: '100%', aspectRatio: ratio, overflow: 'hidden', background: 'var(--bg-2)' }}>
      <div style={{
        position: 'absolute', inset: 0,
        clipPath: `inset(0 0 ${clip}% 0)`,
        transition: 'clip-path .1s linear',
      }}>
        <div style={{
          position: 'absolute', inset: '-8%',
          background: bgs[tone] || bgs.warm,
          transform: `translate3d(0, ${y}px, 0) scale(1.1)`,
          willChange: 'transform',
        }} />
        {children}
      </div>
      {label && (
        <figcaption style={{
          position: 'absolute', left: 20, bottom: 20, right: 20,
          display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end',
          color: 'var(--fg)', mixBlendMode: 'difference',
        }}>
          <span className="mono">{label}</span>
          {caption && <span className="mono" style={{ textAlign: 'right' }}>{caption}</span>}
        </figcaption>
      )}
    </figure>
  );
}

// Animated counter
function Counter({ to, suffix = '', duration = 1400 }) {
  const [ref, inView] = useInView();
  const [val, setVal] = 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);
      setVal(Math.round(to * eased));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [inView]);
  return <span ref={ref}>{val}{suffix}</span>;
}

Object.assign(window, { useInView, useScrollProgress, Reveal, MaskLines, Label, CTA, Section, Rule, Parallax, SplitReveal, MaskedImage, Counter });
