// Shared UI primitives — exposed on window for cross-script use
const { motion, AnimatePresence, useScroll, useTransform, useSpring, useMotionValue, useInView, useMotionValueEvent, useReducedMotion } = window.FramerMotion || window.Motion || {};

// Detected once at module load: page hidden iframes (e.g. preview captures) throttle RAF,
// so initial framer-motion animations never commit. Skip in-anims when hidden.
const IS_HIDDEN = typeof document !== 'undefined' && document.visibilityState === 'hidden';

// ---- Reveal hook (triggers on viewport entry; works in hidden iframes too) ----
function useScrollReveal(rootMarginRatio = 0.08) {
  const ref = React.useRef(null);
  // If document is hidden (background tab / hidden iframe), don't animate at all — show immediately.
  const initialHidden = (typeof document !== 'undefined' && document.visibilityState === 'hidden');
  const [shown, setShown] = React.useState(initialHidden);
  React.useEffect(() => {
    if (initialHidden) return;
    let raf, timeout, fallback;

    const check = () => {
      const el = ref.current;
      if (!el) return false;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight || document.documentElement.clientHeight;
      const margin = vh * rootMarginRatio;
      const inView = r.top < vh - margin && r.bottom > margin;
      if (inView) {
        setShown(true);
        cleanup();
        return true;
      }
      return false;
    };
    const onScroll = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(check);
    };
    const cleanup = () => {
      window.removeEventListener('scroll', onScroll, true);
      document.removeEventListener('scroll', onScroll, true);
      window.removeEventListener('resize', onScroll);
      document.removeEventListener('visibilitychange', onVisible);
      cancelAnimationFrame(raf);
      clearTimeout(timeout);
      clearTimeout(fallback);
    };
    const onVisible = () => {
      if (document.visibilityState === 'visible') check();
    };

    // Run an immediate viewport check; if not in view, wait for scroll/visibility.
    if (!check()) {
      window.addEventListener('scroll', onScroll, { passive: true, capture: true });
      document.addEventListener('scroll', onScroll, { passive: true, capture: true });
      window.addEventListener('resize', onScroll);
      document.addEventListener('visibilitychange', onVisible);
      // Re-check after layout settles (fonts, images)
      timeout = setTimeout(check, 80);
      // Safety net
      fallback = setTimeout(() => setShown(true), 1500);
    }

    return cleanup;
  }, [rootMarginRatio]);
  return [ref, shown];
}

// ---- Aurora background ----
function Aurora() {
  return (
    <div className="absolute inset-0 -z-10 overflow-hidden" aria-hidden="true">
      <div
        className="aurora aurora-1"
        style={{ width: 720, height: 720, top: -200, left: -120, background: "radial-gradient(closest-side, oklch(0.65 0.16 175 / 0.55), transparent 70%)" }}
      />
      <div
        className="aurora aurora-2"
        style={{ width: 560, height: 560, top: 200, right: -160, background: "radial-gradient(closest-side, oklch(0.55 0.13 210 / 0.55), transparent 70%)" }}
      />
      <div
        className="aurora aurora-3"
        style={{ width: 480, height: 480, top: 600, left: "30%", background: "radial-gradient(closest-side, oklch(0.7 0.14 160 / 0.35), transparent 70%)" }}
      />
    </div>
  );
}

// ---- Reveal on scroll ----
function Reveal({ children, delay = 0, y = 24, className = "" }) {
  const [ref, shown] = useScrollReveal();
  // Capture whether we're starting already-shown (hidden iframe / SSR hydrate)
  const startShown = React.useRef(shown).current;
  return (
    <div ref={ref} className={className}>
      <motion.div
        initial={startShown ? false : { opacity: 0, y, filter: "blur(8px)" }}
        animate={shown ? { opacity: 1, y: 0, filter: "blur(0px)" } : { opacity: 0, y, filter: "blur(8px)" }}
        transition={{ duration: 0.9, delay, ease: [0.22, 0.61, 0.36, 1] }}
      >
        {children}
      </motion.div>
    </div>
  );
}

// ---- Animated text by word ----
function SplitText({ text, className = "", delay = 0, stagger = 0.05 }) {
  const words = text.split(" ");
  const [ref, shown] = useScrollReveal(0.05);
  const startShown = React.useRef(shown).current;
  return (
    <span ref={ref} className={className} style={{ display: "inline-block" }} aria-label={text}>
      {words.map((w, i) => (
        <span key={i} className="inline-block overflow-hidden align-bottom" style={{ marginRight: "0.25em" }}>
          <motion.span
            className="inline-block"
            initial={startShown ? false : { y: "110%" }}
            animate={shown ? { y: 0 } : { y: "110%" }}
            transition={{ duration: 0.9, delay: delay + i * stagger, ease: [0.22, 0.61, 0.36, 1] }}
          >
            {w}
          </motion.span>
        </span>
      ))}
    </span>
  );
}

// ---- Section header with chapter number + title ----
function SectionHeader({ chapter, eyebrow, title, lede }) {
  return (
    <div className="grid grid-cols-12 gap-6 items-end pb-12 md:pb-16">
      <div className="col-span-12 md:col-span-3 flex items-center gap-3">
        <span className="chapter">{chapter}</span>
        <span className="hairline flex-1 max-w-24" />
        <span className="chapter">{eyebrow}</span>
      </div>
      <div className="col-span-12 md:col-span-9">
        <h2 className="display text-5xl sm:text-6xl md:text-7xl text-bone-100">
          <SplitText text={title} stagger={0.04} />
        </h2>
        {lede && (
          <Reveal delay={0.2} className="mt-5 max-w-2xl text-bone-300/80 text-base md:text-lg leading-relaxed">
            {lede}
          </Reveal>
        )}
      </div>
    </div>
  );
}

// ---- Magnetic button ----
function MagneticButton({ children, href, onClick, primary = false, className = "" }) {
  const ref = React.useRef(null);
  const x = useMotionValue(0);
  const y = useMotionValue(0);
  const sx = useSpring(x, { stiffness: 180, damping: 18 });
  const sy = useSpring(y, { stiffness: 180, damping: 18 });

  function handleMove(e) {
    const r = ref.current.getBoundingClientRect();
    const mx = e.clientX - (r.left + r.width / 2);
    const my = e.clientY - (r.top + r.height / 2);
    x.set(mx * 0.25);
    y.set(my * 0.25);
  }
  function handleLeave() { x.set(0); y.set(0); }

  const inner = (
    <motion.span style={{ x: sx, y: sy }} className="inline-flex items-center gap-2">
      {children}
    </motion.span>
  );

  const base = `group relative inline-flex items-center justify-center px-6 py-3 rounded-full text-sm font-medium tracking-wide overflow-hidden transition-colors ${className}`;
  const variant = primary
    ? "bg-bone-100 text-ink-900 hover:bg-mint-300"
    : "border border-bone-300/20 text-bone-100 hover:border-mint-400 hover:text-mint-300";

  const Tag = href ? "a" : "button";
  return (
    <Tag
      ref={ref}
      href={href}
      onClick={onClick}
      onMouseMove={handleMove}
      onMouseLeave={handleLeave}
      className={`${base} ${variant}`}
    >
      {inner}
    </Tag>
  );
}

// ---- Animated counter ----
function Counter({ to, suffix = "", duration = 1.6 }) {
  const [ref, inView] = useScrollReveal();
  const [val, setVal] = React.useState(0);
  React.useEffect(() => {
    if (!inView) return;
    let raf;
    const start = performance.now();
    const tick = (t) => {
      const p = Math.min(1, (t - start) / (duration * 1000));
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(Math.round(eased * to));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [inView, to, duration]);
  return <span ref={ref}>{val}{suffix}</span>;
}

// ---- Icons (line) ----
const Icon = {
  Mail: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg>,
  Phone: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.86 19.86 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.86 19.86 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>,
  Pin: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0z"/><circle cx="12" cy="10" r="3"/></svg>,
  Download: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/></svg>,
  Arrow: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M5 12h14"/><path d="m13 5 7 7-7 7"/></svg>,
  ArrowDown: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M12 5v14"/><path d="m5 12 7 7 7-7"/></svg>,
  Stethoscope: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M6 3v6a4 4 0 0 0 8 0V3"/><path d="M4 3h4"/><path d="M12 3h4"/><path d="M10 13v4a4 4 0 0 0 8 0v-2"/><circle cx="18" cy="11" r="2"/></svg>,
  Brain: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M9.5 2a3 3 0 0 0-3 3v.5A3 3 0 0 0 4 8.5v1a3 3 0 0 0 1.5 2.6A3 3 0 0 0 4 14.5v1a3 3 0 0 0 3 3 3 3 0 0 0 3 3h0V2Z"/><path d="M14.5 2a3 3 0 0 1 3 3v.5A3 3 0 0 1 20 8.5v1a3 3 0 0 1-1.5 2.6A3 3 0 0 1 20 14.5v1a3 3 0 0 1-3 3 3 3 0 0 1-3 3h0V2Z"/></svg>,
  Book: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20V3H6.5A2.5 2.5 0 0 0 4 5.5v14z"/><path d="M4 19.5A2.5 2.5 0 0 0 6.5 22H20v-5"/></svg>,
  Award: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><circle cx="12" cy="9" r="6"/><path d="m9 14-2 7 5-3 5 3-2-7"/></svg>,
  Beaker: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M9 3h6"/><path d="M10 3v6L5 19a2 2 0 0 0 2 3h10a2 2 0 0 0 2-3l-5-10V3"/><path d="M7 14h10"/></svg>,
  Sparkle: (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z"/></svg>,
  Linkedin: (p) => <svg viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M20.45 20.45h-3.55v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.36V9h3.41v1.56h.05c.47-.9 1.63-1.85 3.36-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12zM7.12 20.45H3.56V9h3.56v11.45z"/></svg>,
  Github: (p) => <svg viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.1.79-.25.79-.56v-2c-3.2.7-3.88-1.36-3.88-1.36-.52-1.33-1.27-1.68-1.27-1.68-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.02 1.76 2.69 1.25 3.34.96.1-.75.4-1.25.73-1.54-2.55-.29-5.24-1.28-5.24-5.7 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.46.11-3.04 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.78 0c2.2-1.49 3.18-1.18 3.18-1.18.62 1.58.23 2.75.11 3.04.74.81 1.18 1.84 1.18 3.1 0 4.43-2.7 5.4-5.27 5.69.41.36.78 1.06.78 2.14v3.17c0 .31.21.67.8.56 4.57-1.52 7.85-5.83 7.85-10.91C23.5 5.65 18.35.5 12 .5z"/></svg>,
  X: (p) => <svg viewBox="0 0 24 24" fill="currentColor" {...p}><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>,
};

// ---- Top progress bar ----
function ScrollProgress() {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf;
    const update = () => {
      const h = document.documentElement.scrollHeight - window.innerHeight;
      const p = h > 0 ? window.scrollY / h : 0;
      el.style.transform = `scaleX(${Math.max(0, Math.min(1, p))})`;
    };
    const onScroll = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(update);
    };
    update();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", update);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", update);
      cancelAnimationFrame(raf);
    };
  }, []);
  return (
    <div
      ref={ref}
      style={{ transformOrigin: "0 0", transform: "scaleX(0)", transition: "transform 0.12s ease-out" }}
      className="fixed top-0 left-0 right-0 h-px bg-mint-400/70 z-50"
    />
  );
}

// ---- Marquee (clinical principles) ----
function Marquee({ items }) {
  const row = [...items, ...items];
  return (
    <div className="overflow-hidden border-y border-bone-300/10 py-6 select-none">
      <div className="marquee-track flex whitespace-nowrap gap-12 pr-12">
        {row.map((it, i) => (
          <div key={`${it}-${i}`} className="flex items-center gap-3 shrink-0">
            <span className="italic-soft text-3xl md:text-4xl text-bone-100/90">{it}</span>
            <span className="text-mint-400">✦</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// Expose to window
Object.assign(window, {
  Aurora, Reveal, SplitText, SectionHeader, MagneticButton, Counter, Icon, ScrollProgress, Marquee, useScrollReveal, IS_HIDDEN
});
