/* 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;