const { useState: useCS, useEffect: useCE, useRef: useCR } = React;

/* ---------- Reveal: simple intersection-based fade/slide ---------- */
function Reveal({ children, delay = 0, as: As = 'div', className = '', ...rest }) {
  const ref = useCR(null);
  useCE(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.12 });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return (
    <As ref={ref} className={`reveal ${className}`} style={{transitionDelay: `${delay}ms`}} {...rest}>
      {children}
    </As>
  );
}

/* ---------- Word-by-word title reveal ---------- */
function WordReveal({ children, className = '', as: As = 'h2', delay = 0 }) {
  // children is a flat array of strings/jsx — split each string into words,
  // and stagger their reveal once the heading enters the viewport.
  const ref = useCR(null);
  const [shown, setShown] = useCS(false);
  useCE(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) { setShown(true); io.unobserve(e.target); }
      });
    }, { threshold: 0.18 });
    io.observe(el);
    return () => io.disconnect();
  }, []);

  let wordIdx = 0;
  const renderNode = (node) => {
    if (typeof node === 'string') {
      // split, preserve trailing space
      return node.split(/(\s+)/).map((part, i) => {
        if (/^\s+$/.test(part)) return part;
        if (part === '') return null;
        const idx = wordIdx++;
        return (
          <span key={`w-${idx}`} className="wr-word"
                style={{ transitionDelay: `${delay + idx * 60}ms`, animationDelay: `${delay + idx * 60}ms` }}>
            <span className="wr-inner">{part}</span>
          </span>
        );
      });
    }
    if (React.isValidElement(node)) {
      // recursive for em/strong nested
      return React.cloneElement(node, { key: `el-${wordIdx}` }, React.Children.map(node.props.children, renderNode));
    }
    if (Array.isArray(node)) return node.map(renderNode);
    return node;
  };

  return (
    <As ref={ref} className={`wr ${shown ? 'in' : ''} ${className}`}>
      {React.Children.map(children, renderNode)}
    </As>
  );
}

/* ---------- ChapterHead: clean static eyebrow + animated title + lede ---------- */
function ChapterHead({ chapter, label, title, lede, ledeRight = true }) {
  return (
    <div className="ch-head">
      <div className="ch-mark">
        <span className="ch-num mono">{chapter}</span>
        <span className="ch-label mono">{label}</span>
      </div>

      <div className={`ch-body ${lede && ledeRight ? 'split' : ''}`}>
        <WordReveal as="h2" className="ch-title">{title}</WordReveal>
        {lede && (
          <Reveal delay={200}>
            <p className="ch-lede">{lede}</p>
          </Reveal>
        )}
      </div>

      <style>{`
        .ch-head {
          position: relative;
          margin-bottom: 72px;
        }
        .ch-mark {
          margin-bottom: 28px;
          display: inline-flex;
          align-items: baseline;
          gap: 14px;
          padding-bottom: 14px;
          border-bottom: 1px solid var(--line);
        }
        .ch-num { font-size: 11px; color: var(--text-3); letter-spacing: 0.14em; }
        .ch-label { font-size: 11px; color: var(--text-2); letter-spacing: 0.18em; text-transform: uppercase; }

        .ch-body { display: grid; grid-template-columns: 1.5fr 1fr; gap: 56px; align-items: end; }
        .ch-body:not(.split) { grid-template-columns: 1fr; }
        @media (max-width: 900px) { .ch-body { grid-template-columns: 1fr; gap: 20px; } }

        .ch-title {
          font-family: var(--serif);
          font-size: clamp(40px, 5.2vw, 72px);
          line-height: 1.04;
          letter-spacing: -0.025em;
          color: var(--text);
          margin: 0;
          text-wrap: balance;
          font-weight: 400;
          max-width: 16ch;
        }
        .ch-title em { font-style: italic; color: var(--text-2); }
        .ch-lede {
          color: var(--text-2);
          font-size: 16px;
          line-height: 1.6;
          max-width: 44ch;
          padding-bottom: 10px;
          margin: 0;
        }

        /* word-by-word reveal */
        .wr-word {
          display: inline-block;
          overflow: hidden;
          vertical-align: top;
        }
        .wr-word .wr-inner {
          display: inline-block;
          transform: translateY(105%);
          opacity: 0;
          transition: transform 0.8s cubic-bezier(0.2,0.7,0.2,1), opacity 0.5s ease;
        }
        .wr.in .wr-word .wr-inner { transform: none; opacity: 1; }
        body.no-motion .wr-word .wr-inner { transform: none; opacity: 1; transition: none; }
      `}</style>
    </div>
  );
}

/* ---------- SectionWorld: no-op, kept for back-compat ---------- */
function SectionWorld() { return null; }

/* ---------- Scroll progress bar (top) ---------- */
function ScrollProgress() {
  const [p, setP] = useCS(0);
  useCE(() => {
    const onScroll = () => {
      const doc = document.documentElement;
      const max = doc.scrollHeight - doc.clientHeight;
      setP(max > 0 ? window.scrollY / max : 0);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return (
    <div className="scroll-prog" style={{transform: `scaleX(${p})`}}>
      <style>{`
        .scroll-prog {
          position: fixed; top: 0; left: 0; right: 0;
          height: 2px;
          background: linear-gradient(90deg, transparent, var(--accent) 50%, transparent);
          transform-origin: 0 50%;
          z-index: 60;
          pointer-events: none;
          transition: transform 0.1s linear;
          box-shadow: 0 0 14px var(--accent);
        }
      `}</style>
    </div>
  );
}

/* ---------- Scroll rail: fixed left section navigator ---------- */
function ScrollRail({ sections }) {
  const [active, setActive] = useCS(0);
  useCE(() => {
    const onScroll = () => {
      let cur = 0;
      sections.forEach((s, i) => {
        const el = document.getElementById(s.id);
        if (!el) return;
        const r = el.getBoundingClientRect();
        if (r.top < window.innerHeight * 0.45) cur = i;
      });
      setActive(cur);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, [sections]);

  return (
    <div className="scroll-rail" aria-hidden="true">
      <ul>
        {sections.map((s, i) => (
          <li key={s.id} className={i === active ? 'active' : ''}>
            <a href={`#${s.id}`} title={s.label}>
              <span className="sr-dot"></span>
              <span className="sr-label mono">{s.label}</span>
            </a>
          </li>
        ))}
      </ul>
      <style>{`
        .scroll-rail {
          position: fixed;
          top: 50%;
          left: 20px;
          transform: translateY(-50%);
          z-index: 40;
          pointer-events: auto;
        }
        @media (max-width: 1100px) { .scroll-rail { display: none; } }
        .scroll-rail ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 16px; }
        .scroll-rail a {
          display: flex; align-items: center; gap: 12px;
          padding: 4px 4px;
          color: var(--text-3);
          transition: color 0.2s;
          position: relative;
        }
        .sr-dot {
          width: 6px; height: 6px;
          border-radius: 50%;
          background: var(--text-3);
          opacity: 0.4;
          transition: all 0.3s;
          flex-shrink: 0;
        }
        .sr-label {
          font-size: 10px;
          letter-spacing: 0.14em;
          text-transform: uppercase;
          opacity: 0;
          transform: translateX(-6px);
          transition: opacity 0.2s, transform 0.3s, color 0.2s;
          color: var(--text-3);
        }
        .scroll-rail a:hover .sr-label,
        .scroll-rail li.active .sr-label { opacity: 1; transform: none; }
        .scroll-rail li.active .sr-dot {
          background: var(--accent);
          opacity: 1;
          box-shadow: 0 0 10px var(--accent);
          width: 8px; height: 8px;
        }
        .scroll-rail li.active .sr-label { color: var(--text); }
        .scroll-rail a:hover { color: var(--text); }
        .scroll-rail a:hover .sr-dot { opacity: 1; background: var(--text-2); }
      `}</style>
    </div>
  );
}

/* ---------- Parallax wrapper: subtle translate on scroll ---------- */
function Parallax({ speed = 0.2, children, className = '' }) {
  const ref = useCR(null);
  useCE(() => {
    if (!ref.current) return;
    let raf = 0;
    const update = () => {
      if (!ref.current) return;
      const r = ref.current.getBoundingClientRect();
      const center = r.top + r.height / 2 - window.innerHeight / 2;
      ref.current.style.transform = `translateY(${(-center * speed).toFixed(1)}px)`;
      raf = 0;
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => { window.removeEventListener('scroll', onScroll); if (raf) cancelAnimationFrame(raf); };
  }, [speed]);
  return <div ref={ref} className={className} style={{willChange:'transform'}}>{children}</div>;
}

Object.assign(window, { Reveal, ChapterHead, WordReveal, SectionWorld, ScrollProgress, ScrollRail, Parallax });
