// surface-text.jsx — scroll-driven "ASCII glyphs snap into letters" effect.
(function () {
  const { useEffect, useRef, useState, useMemo } = React;
  const RAMPS = {
    classic: ['~','~','≈','≈','≋','^','*','°','·'],
    foam:    ['·',':','~','≈','≋','o','O','*','°','✦'],
    blocks:  ['▁','▂','▃','▄','▅','▆','▇','█'],
    dots:    ['.','·',':','∙','•','●','◉'],
    waves:   ['_','-','~','∽','≈','≋','^','⌒'],
    ink:     ['.','`','~','∽','≈','⌇','⁓','✦'],
  };
  function pickGlyph(rampName, salt) {
    const r = RAMPS[rampName] || RAMPS.classic;
    return r[salt % r.length];
  }
  const tickListeners = new Set();
  let tickRunning = false; let lastTick = 0;
  function startTicking() {
    if (tickRunning) return;
    tickRunning = true;
    const loop = (now) => {
      if (!tickListeners.size) { tickRunning = false; return; }
      if (now - lastTick > 90) { lastTick = now; tickListeners.forEach((fn) => fn(now)); }
      requestAnimationFrame(loop);
    };
    requestAnimationFrame(loop);
  }
  function subscribeTick(fn) { tickListeners.add(fn); startTicking(); return () => tickListeners.delete(fn); }
  const recomputeListeners = new Set();
  let recomputePending = false;
  function scheduleRecompute() {
    if (recomputePending) return;
    recomputePending = true;
    requestAnimationFrame(() => { recomputePending = false; recomputeListeners.forEach((fn) => fn()); });
  }
  if (typeof window !== 'undefined') {
    window.addEventListener('scroll', scheduleRecompute, { passive: true });
    window.addEventListener('resize', scheduleRecompute);
  }
  function SurfaceText({ text, ramp = 'classic', spread = 0.5, enter = 0.88, exit = 0.4, className, as: Tag = 'span', style, onProgress }) {
    const ref = useRef(null), charRefs = useRef([]);
    const [tick, setTick] = useState(0);
    const lastP = useRef(-1);
    const chars = useMemo(() => Array.from(text), [text]);
    useEffect(() => {
      const recompute = () => {
        const el = ref.current; if (!el) return;
        const r = el.getBoundingClientRect(); const vh = window.innerHeight;
        const center = (r.top + r.bottom) / 2;
        const startY = vh * enter, endY = vh * exit;
        let p = (startY - center) / (startY - endY);
        p = Math.max(0, Math.min(1, p));
        if (Math.abs(p - lastP.current) < 0.001) return;
        lastP.current = p; applyProgress(p);
        if (onProgress) onProgress(p);
      };
      const applyProgress = (p) => {
        const N = chars.length || 1;
        const useableSpread = Math.min(0.95, Math.max(0, spread));
        const denom = Math.max(0.05, 1 - useableSpread);
        for (let i = 0; i < chars.length; i++) {
          const node = charRefs.current[i]; if (!node) continue;
          const ch = chars[i]; if (ch === ' ' || ch === '\u00A0') continue;
          const startAt = (i / Math.max(1, N - 1)) * useableSpread;
          let lp = (p - startAt) / denom;
          lp = Math.max(0, Math.min(1, lp));
          const e = lp * lp * lp * (lp * (lp * 6 - 15) + 10);
          node.style.setProperty('--p', e.toFixed(4));
        }
      };
      recomputeListeners.add(recompute); recompute();
      return () => recomputeListeners.delete(recompute);
    }, [chars, enter, exit, spread, onProgress]);
    useEffect(() => {
      const unsub = subscribeTick(() => {
        if (lastP.current >= 0.999) return;
        setTick((t) => (t + 1) & 0xFFFF);
      });
      return unsub;
    }, []);
    return (
      <Tag ref={ref} className={`surface-text ${className || ''}`} style={style} aria-label={text}>
        {chars.map((ch, i) => {
          if (ch === ' ') return <span key={i} className="st-space"> </span>;
          if (ch === '\n') return <br key={i} />;
          const glyph = pickGlyph(ramp, (i * 31 + tick + (text.charCodeAt(i) || 0)) | 0);
          return (
            <span key={i} ref={(n) => (charRefs.current[i] = n)} className="st-ch" aria-hidden="true">
              <span className="st-glyph">{glyph}</span><span className="st-real">{ch}</span>
            </span>
          );
        })}
      </Tag>
    );
  }
  window.SurfaceText = SurfaceText;
})();
