const { useEffect, useRef, useState } = React;
function Container({ children, style, ...rest }) {
return (
{children}
);
}
function Rule({ style }) {
return ;
}
function Kicker({ children, dot = true, style }) {
return (
{dot && }
{children}
);
}
function Btn({ children, variant = "accent", onClick, as = "button", href, style, size = "lg", ...rest }) {
const sizes = {
lg: { height: 56, padding: "0 28px", fontSize: 16 },
md: { height: 44, padding: "0 20px", fontSize: 14 },
};
const variants = {
accent: { background: "var(--accent)", color: "var(--accent-ink)", border: "1px solid var(--accent)" },
solid: { background: "var(--fg)", color: "var(--bg)", border: "1px solid var(--fg)" },
ghost: { background: "transparent", color: "var(--fg)", border: "1px solid var(--rule-2)" },
};
const Tag = as;
return (
(e.currentTarget.style.transform = "scale(0.98)")}
onMouseUp={(e) => (e.currentTarget.style.transform = "")}
onMouseLeave={(e) => (e.currentTarget.style.transform = "")}
{...rest}
>
{children}
);
}
function Arrow({ size = 14, angle = 0 }) {
return (
);
}
function Plus({ size = 14 }) {
return (
);
}
function Check({ size = 14 }) {
return (
);
}
// Striped placeholder
function ImgPH({ label = "image", aspect = "4/5", style }) {
return (
);
}
function Reveal({ children, delay = 0, y = 16 }) {
const ref = useRef(null);
const [shown, setShown] = useState(false);
useEffect(() => {
const el = ref.current; if (!el) return;
const r = el.getBoundingClientRect();
if (r.top < window.innerHeight && r.bottom > 0) { setShown(true); return; }
const io = new IntersectionObserver(
(entries) => entries.forEach((e) => e.isIntersecting && setShown(true)),
{ threshold: 0 }
);
io.observe(el);
const t = setTimeout(() => setShown(true), 900);
return () => { io.disconnect(); clearTimeout(t); };
}, []);
return (
{children}
);
}
// Counter
function Counter({ to, decimals = 0, duration = 1600, prefix = "", suffix = "" }) {
const ref = useRef(null);
const [v, setV] = useState(0);
useEffect(() => {
const el = ref.current; if (!el) return;
const fire = () => {
const start = performance.now();
const tick = (now) => {
const t = Math.min(1, (now - start) / duration);
setV((1 - Math.pow(1 - t, 3)) * to);
if (t < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
};
const r = el.getBoundingClientRect();
if (r.top < window.innerHeight && r.bottom > 0) { fire(); return; }
const io = new IntersectionObserver((es) => es.forEach((e) => e.isIntersecting && (fire(), io.disconnect())), { threshold: 0.2 });
io.observe(el); return () => io.disconnect();
}, [to]);
return {prefix}{v.toFixed(decimals)}{suffix};
}
Object.assign(window, { Container, Rule, Kicker, Btn, Arrow, Plus, Check, ImgPH, Reveal, Counter });