// Shared primitives
const { useEffect, useRef, useState, useMemo, useCallback } = React;
// Reveal on scroll using IntersectionObserver
function useReveal() {
const ref = useRef(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.classList.add('is-visible');
io.unobserve(e.target);
}
});
}, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' });
io.observe(el);
return () => io.disconnect();
}, []);
return ref;
}
function Reveal({ children, delay = 0, as: Tag = 'div', style = {}, ...rest }) {
const ref = useReveal();
return (
{children}
);
}
// Arrow glyphs
const ArrowRight = ({ size = 14 }) => (
);
const ArrowDown = ({ size = 14 }) => (
);
// Buttons
const btnStyles = {
base: {
display: 'inline-flex',
alignItems: 'center',
gap: 10,
padding: '14px 22px',
fontFamily: 'var(--sans)',
fontSize: 14,
letterSpacing: '0.01em',
fontWeight: 500,
borderRadius: 2,
transition: 'all 200ms ease',
whiteSpace: 'nowrap',
},
primary: {
background: 'var(--ink)',
color: 'var(--bg)',
},
ghost: {
background: 'transparent',
color: 'var(--ink)',
border: '1px solid var(--rule-2)',
},
text: {
padding: 0,
color: 'var(--ink-2)',
background: 'transparent',
borderBottom: '1px solid var(--rule-2)',
borderRadius: 0,
paddingBottom: 4,
},
};
function PrimaryBtn({ children, href, onClick, style, ...rest }) {
const [hover, setHover] = useState(false);
const s = {
...btnStyles.base,
...btnStyles.primary,
...(hover ? { background: 'var(--accent)', color: 'var(--bg)' } : {}),
...style,
};
const El = href ? 'a' : 'button';
return (
setHover(true)} onMouseLeave={() => setHover(false)} {...rest}>
{children}
);
}
function GhostBtn({ children, href, onClick, style }) {
const [hover, setHover] = useState(false);
const s = {
...btnStyles.base,
...btnStyles.ghost,
...(hover ? { borderColor: 'var(--accent)', color: 'var(--accent)' } : {}),
...style,
};
const El = href ? 'a' : 'button';
return (
setHover(true)} onMouseLeave={() => setHover(false)}>
{children}
);
}
function TextLink({ children, href, onClick, style }) {
const [hover, setHover] = useState(false);
const s = {
...btnStyles.base,
...btnStyles.text,
...(hover ? { color: 'var(--accent)', borderColor: 'var(--accent)' } : {}),
...style,
};
const El = href ? 'a' : 'button';
return (
setHover(true)} onMouseLeave={() => setHover(false)}>
{children}
);
}
// Section wrapper with numeric label + eyebrow
function SectionHeader({ index, kicker, title, intro, id }) {
return (
{kicker}
{title}
{intro && (
{intro}
)}
);
}
Object.assign(window, {
Reveal, useReveal,
ArrowRight, ArrowDown,
PrimaryBtn, GhostBtn, TextLink,
SectionHeader,
});