/* BlurText — word-by-word reveal */ const BlurText = ({ text, className = '', delay = 0, stagger = 100, as = 'h1', children }) => { const ref = React.useRef(null); const [inView, setInView] = React.useState(false); React.useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setInView(true); io.disconnect(); } }, { threshold: 0.2 }); io.observe(el); return () => io.disconnect(); }, []); // If children provided (rich content with accent spans), split children into word spans // Otherwise split text by spaces let parts = []; if (children) { React.Children.forEach(children, (child, ci) => { if (typeof child === 'string') { const words = child.split(/(\s+)/); words.forEach((w, wi) => { if (w.trim()) parts.push({ kind: 'word', text: w, key: `c${ci}-w${wi}` }); else parts.push({ kind: 'space', text: w, key: `c${ci}-s${wi}` }); }); } else if (React.isValidElement(child)) { // Treat child as one "word" preserving its styling (e.g. accent span) parts.push({ kind: 'word', node: child, key: `c${ci}-el` }); } }); } else if (text) { text.split(/(\s+)/).forEach((w, i) => { if (w.trim()) parts.push({ kind: 'word', text: w, key: `w${i}` }); else parts.push({ kind: 'space', text: w, key: `s${i}` }); }); } const Tag = as; let wordIdx = 0; return ( {parts.map(p => { if (p.kind === 'space') return {p.text || '\u00A0'}; const idx = wordIdx++; const style = { transitionDelay: inView ? `${delay + idx * stagger}ms` : '0ms', }; return ( {p.node || p.text} ); })} ); }; /* Generic fade-up that triggers on scroll-in */ const FadeUp = ({ children, delay = 0, className = '', as = 'div', style = {} }) => { const ref = React.useRef(null); const [inView, setInView] = React.useState(false); React.useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setInView(true); io.disconnect(); } }, { threshold: 0.15 }); io.observe(el); return () => io.disconnect(); }, []); const Tag = as; return ( {children} ); }; window.BlurText = BlurText; window.FadeUp = FadeUp;