/* helpers.jsx — shared icon set (inline Lucide paths) + scroll reveal */
const { useEffect, useRef, useState } = React;
/* Lucide-style outline icons, 2px stroke, currentColor */
const ICON_PATHS = {
search: '',
code: '',
bot: '',
arrowUpRight: '',
arrowRight: '',
check: '',
play: '',
star: '',
trendingUp: '',
target: '',
link: '',
gauge: '',
mail: '',
phone: '',
mapPin: '',
clock: '',
zap: '',
shield: '',
fileSearch: '',
layers: '',
workflow: '',
penTool: '',
menu: '',
x: '',
linkedin: '',
twitter: '',
github: '',
crown: '',
award: '',
building: '',
};
function Icon({ name, size = 20, stroke = 1.75, fill, style, ...rest }) {
return (
);
}
/* Scroll-reveal: opt into the hidden start-state (html.reveal-ready), then add
.is-in to elements as they enter the viewport. Uses setTimeout throttling
(rAF can be frozen in background/throttled iframes). */
function useReveal() {
const ref = useRef(null);
useEffect(() => {
const motionOk = !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!motionOk) return;
const root = document.documentElement;
let pending = false, settle = [];
const mark = () => {
pending = false;
const vh = window.innerHeight || document.documentElement.clientHeight;
const els = ref.current ? ref.current.querySelectorAll('.reveal:not(.is-in)') : [];
let stagger = 0;
els.forEach((el) => {
const r = el.getBoundingClientRect();
if (r.top < vh * 0.92 && r.bottom > -40) {
el.style.transitionDelay = (Math.min(stagger++, 5) * 70) + 'ms';
el.classList.add('is-in');
}
});
};
const onScroll = () => { if (!pending) { pending = true; setTimeout(mark, 60); } };
// enable hidden start-state, then reveal what's already on-screen
root.classList.add('reveal-ready');
requestAnimationFrame(() => requestAnimationFrame(mark));
[80, 300, 700].forEach((t) => settle.push(setTimeout(mark, t)));
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll, { passive: true });
return () => {
settle.forEach(clearTimeout);
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onScroll);
};
}, []);
return ref;
}
Object.assign(window, { Icon, useReveal });