// Shared components — Nav, Footer, Icon, Button
const Icon = ({ name, size = 20, style }) => (
);
const Button = ({ children, variant = 'primary', size, href, onClick, icon, iconRight, type, ...rest }) => {
const cls = ['btn', `btn-${variant}`, size === 'lg' && 'btn-lg'].filter(Boolean).join(' ');
const Tag = href ? 'a' : 'button';
return (
{icon && }
{children}
{iconRight && }
);
};
const Nav = ({ currentPage, onNav }) => {
const [open, setOpen] = React.useState(false);
React.useEffect(() => { if (window.lucide) lucide.createIcons(); }, [open]);
React.useEffect(() => {
document.body.style.overflow = open ? 'hidden' : '';
return () => { document.body.style.overflow = ''; };
}, [open]);
const go = (key) => { setOpen(false); onNav(key); };
return (
{/* Rendered as sibling of
);
};
const Footer = ({ onNav }) => {
return (
);
};
const PageHero = ({ eyebrow, title, titleAccent, lead, crumbs, bg }) => (
{bg && }
{crumbs &&
{crumbs}
}
{title} {titleAccent && {titleAccent}}
{lead &&
{lead}
}
);
const QuotePull = ({ quote, name, role }) => (
“{quote}”
{name} · {role}
[ portrait photo ]
);
const CTABand = ({ title, body, primary, secondary, onNav }) => (
{title}
{body}
{primary && }
{secondary && }
);
const fmtMoney = (n) => {
if (n >= 1000000) return '$' + (n / 1000000) + 'M';
if (n >= 1000) return '$' + (n / 1000).toLocaleString('en-US', { maximumFractionDigits: 1 }) + 'K';
return '$' + n.toLocaleString();
};
const fmtMoneyFull = (n) => '$' + n.toLocaleString();
Object.assign(window, { Icon, Button, Nav, Footer, PageHero, QuotePull, CTABand, fmtMoney, fmtMoneyFull });