// Kelappa Landing — main page composition
const { useState: useStateL } = React;
// ── Counter (abacus.jasoncameron.dev — no-auth pageview counter) ─
const COUNTER_NS = 'kelappa-2026';
const counterHit = (key) => {
const url = `https://abacus.jasoncameron.dev/hit/${COUNTER_NS}/${key}`;
if (typeof navigator !== 'undefined' && navigator.sendBeacon) {
navigator.sendBeacon(url);
} else {
fetch(url, { method: 'POST', keepalive: true }).catch(() => {});
}
};
// ── Responsive: <768px counts as mobile ─────────────────────
function useIsMobile(bp = 768) {
const [m, setM] = React.useState(
typeof window !== 'undefined' ? window.innerWidth < bp : false
);
React.useEffect(() => {
const onR = () => setM(window.innerWidth < bp);
window.addEventListener('resize', onR);
return () => window.removeEventListener('resize', onR);
}, [bp]);
return m;
}
// ── Reusable: smile underline under any inline span ────────
function SmileSpan({ children, color, height = 18 }) {
return (
{children}
);
}
// ── Section shell ─────────────────────────────────────────
function Sec({ bg, color, children, pad = '120px 40px', padMobile = '72px 20px', id }) {
const isMobile = useIsMobile();
return (
{copy.hero.sub}
{copy.manifesto.p1}
{copy.manifesto.p2}
{copy.apps.sub}
{a.desc}
{copy.studio.p}
{copy.studio.p2}
{copy.cta.sub}
{copy.cta.btn} →