// site/sections.jsx — page-level sections. Each section is a standalone component. const { useState, useEffect, useRef } = React; // ─── parse helper: extract prefix(+), number, suffix(%, x, etc.) from "73%" / "+340" / "2.5x" function parseStatValue(str) { if (str == null) return null; const m = String(str).match(/^(\+|-)?\s*([0-9]+(?:[.,][0-9]+)?)\s*(.*)$/); if (!m) return null; const prefix = m[1] || ''; const numStr = m[2]; const suffix = m[3] || ''; const decimals = (numStr.split(/[.,]/)[1] || '').length; const useComma = numStr.indexOf(',') >= 0; const num = parseFloat(numStr.replace(',', '.')); if (isNaN(num)) return null; return { prefix, num, suffix, decimals, useComma }; } // ─── useCountUp hook ────────────────────────────────────────────────────── function useCountUp(target, active) { const [val, setVal] = useState(active ? target : 0); const rafRef = useRef(null); useEffect(() => { if (!active) return; const reduce = typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (reduce) { setVal(target); return; } const duration = 1200; const start = performance.now(); const tick = (now) => { const p = Math.min(1, (now - start) / duration); const eased = 1 - Math.pow(1 - p, 3); setVal(target * eased); if (p < 1) rafRef.current = requestAnimationFrame(tick); }; rafRef.current = requestAnimationFrame(tick); return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current); }; }, [target, active]); return val; } function CountUpValue({ raw, active }) { const parsed = parseStatValue(raw); if (!parsed) return <>{raw}; const current = useCountUp(parsed.num, active); let formatted; if (parsed.decimals > 0) { formatted = current.toFixed(parsed.decimals); if (parsed.useComma) formatted = formatted.replace('.', ','); } else { formatted = String(Math.round(current)); } return <>{parsed.prefix}{formatted}{parsed.suffix}; } // ─── Nav ────────────────────────────────────────────────────────────────── function SiteNav({ tweaks }) { const t = useT(); const { lang, setLang } = useLang(); const [scrolled, setScrolled] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 16); onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); return () => window.removeEventListener('scroll', onScroll); }, []); return (
trackEvent('nav_click', { target: 'contact' })}>{t('nav.contact')}
); } // ─── Hero ───────────────────────────────────────────────────────────────── function HeroSection({ variant }) { const t = useT(); return (
{variant === 'centered' && } {variant === 'split' && } {variant === 'stacked' && }
); } function HeroChannelsPreview({ t, variant }) { const channels = ['email', 'whatsapp', 'telegram', 'sms', 'push']; return (
{channels.map(id => (
{t('ch.' + id + '.name').replace(/\s*\*+$/, '')}
))}
); } function HeroCentered({ t }) { return (
{t('hero.eyebrow')}

{t('hero.payoff')}

{t('hero.subline')}

trackEvent('cta_hero_primary', { section: 'hero' })}>{t('hero.cta.primary')} trackEvent('cta_hero_secondary', { section: 'hero' })}>{t('hero.cta.secondary')}
); } function HeroSplit({ t }) { return (
{t('hero.eyebrow')}

{t('hero.payoff')}

{t('hero.subline')}

{t('hero.cta.primary')} {t('hero.cta.secondary')}
); } function HeroStacked({ t }) { return (
{t('hero.payoff')}

{t('hero.c.before')} {t('hero.c.after')}

{t('hero.subline')}

trackEvent('cta_hero_primary', { section: 'hero' })}>{t('hero.cta.primary')} trackEvent('cta_hero_secondary', { section: 'hero' })}>{t('hero.cta.secondary')}
); } // ─── Trusted-by strip (right under hero) ─────────────────────────────────── function MarqueeStrip() { const t = useT(); return (
{t('sp.eyebrow')}
{CUSTOMERS.map((c, i) => )}
); } // ─── Pain section ───────────────────────────────────────────────────────── function PainSection() { const t = useT(); return (
{t('pain.eyebrow')}

{t('pain.headline')}

{t('pain.body')}

{t('pain.body2')}

{t('pain.bridge.before')}
    {PAIN_ROWS.map(r =>
  • {t(r.before)}
  • )}
{t('pain.bridge.after')}
    {PAIN_ROWS.map(r =>
  • {t(r.after)}
  • )}
); } // ─── Solution bridge — short interstitial ────────────────────────────────── function SolutionBridge() { const t = useT(); return (
{t('sol.eyebrow')}

{t('sol.headline')}

); } // ─── Channels ───────────────────────────────────────────────────────────── function ChannelsSection({ layout }) { const t = useT(); const channels = ['email', 'whatsapp', 'telegram', 'sms', 'push']; return (
{t('ch.eyebrow')}

{t('ch.headline')}

{t('ch.subline')}

{layout === 'grid' && (
{channels.map(id => (

{t('ch.' + id + '.name')}

{t('ch.' + id + '.tagline')}

{t('ch.' + id + '.body')}

))}
)} {layout === 'row' && (
{channels.map(id => (
{t('ch.' + id + '.name')}
{t('ch.' + id + '.tagline')}
))}
)} {layout === 'stack' && (
{channels.map((id, i) => (

{t('ch.' + id + '.name')}

{t('ch.' + id + '.tagline')}

{t('ch.' + id + '.body')}

))}
)}

{t('ch.app.note')}

); } // ─── Stats / industry numbers — yellow flood ─────────────────────────────── function StatsSection() { const t = useT(); const gridRef = useRef(null); const [inView, setInView] = useState(false); useEffect(() => { const el = gridRef.current; if (!el || inView) return; const obs = new IntersectionObserver((entries) => { for (const e of entries) { if (e.isIntersecting) { setInView(true); obs.disconnect(); break; } } }, { threshold: 0.3 }); obs.observe(el); return () => obs.disconnect(); }, [inView]); return (
{t('stats.eyebrow')}

{t('stats.headline')}

{t('stats.subline')}

{STATS.map(s => (
{t(s.labelKey)}
{t(s.sourceKey)}
))}
); } // ─── Customer logos grid ────────────────────────────────────────────────── function CustomersSection() { const t = useT(); return (
{t('sp.eyebrow')}

{t('sp.headline')}

{CUSTOMERS.map((c, i) => )}
); } // ─── Pricing ────────────────────────────────────────────────────────────── function PricingSection({ layout }) { const t = useT(); return (
{t('pr.eyebrow')}

{t('pr.headline')}

{t('pr.subline')}

{layout === 'cards' && } {layout === 'table' && ( <>
)}

{t('pr.note.1')}

{t('pr.note.2')}

); } function PricingCards({ t }) { return (
{PLANS.map(plan => (
{plan.popular &&
{t('pr.popular')}
}
{t(plan.nameKey)}
{t(plan.tagKey)}
{t(plan.priceKey)} {plan.id !== 'free' && plan.id !== 'custom' && {t('pr.month')}}
{plan.id !== 'free' && plan.id !== 'custom' &&
{t('pr.billing')}
}
{plan.id === 'custom' ? t('pr.contactsRaw', { n: t(plan.contactsKey) }) : t('pr.contactsUpTo', { n: t(plan.contactsKey) })}
trackEvent('cta_plan', { plan_id: plan.id })}> {t(plan.ctaKey)}
))}
); } function PricingTable({ t }) { // Compact comparison table — same content, denser layout. const rows = [ { label: 'pr.contactsUpTo.label', values: ['pr.free.contacts','pr.starter.contacts','pr.pro.contacts'], isContact: true }, { label: null, headerPrice: true }, { label: 'pr.free.f1', values: ['pr.free.f1','pr.starter.f1','pr.pro.f1'] }, { label: 'pr.free.f2', values: ['pr.free.f2','pr.starter.f2','pr.pro.f2'] }, { label: 'pr.free.f3', values: ['pr.free.f3','pr.starter.f3','pr.pro.f3'] }, { label: 'pr.free.f4', values: ['pr.free.f4','pr.starter.f4','pr.pro.f4'] }, { label: 'pr.free.f5', values: ['pr.free.f5','pr.starter.f5','pr.pro.f5'] }, ]; return (
{PLANS.map(p => (
{p.popular && {t('pr.popular')}}
{t(p.nameKey)}
{t(p.priceKey)} {p.id !== 'free' && p.id !== 'custom' && {t('pr.month')}}
{p.id !== 'free' && p.id !== 'custom' &&
{t('pr.billing')}
}
{p.id === 'custom' ? t('pr.contactsRaw', { n: t(p.contactsKey) }) : t('pr.contactsUpTo', { n: t(p.contactsKey) })}
))}
{[0,1,2,3,4].map(i => (
{[i18nLabelFor(i)]}
{PLANS.map(p => { const baseKey = 'pr.' + p.id + '.f' + (i+1); const valKey = baseKey + '.value'; const valRaw = t(valKey); const val = valRaw === valKey ? t(baseKey) : valRaw; const lc = val.toLowerCase(); const notIncluded = val === '—' || lc.includes('non incluso') || lc.includes('not included'); return (
{notIncluded ? : <>{val}}
); })}
))}
{PLANS.map(p => (
trackEvent('cta_plan', { plan_id: p.id })}> {t(p.ctaKey)}
))}
); } // helper for pricing-table row labels (the channel name shown on the left col) function i18nLabelFor(i) { const labels = ['Email', 'Telegram', 'WhatsApp *', 'Push **', 'SMS']; return labels[i]; } function NotIncludedIcon() { return ( ); } function CheckIcon() { return ( ); } // ─── FAQ ────────────────────────────────────────────────────────────────── function FaqSection() { const t = useT(); const [open, setOpen] = useState(0); return (
{t('faq.eyebrow')}

{t('faq.headline')}

{FAQS.map((f, i) => (
{ if (e.currentTarget.open) { trackEvent('faq_open', { index: i }); setOpen(i); } }}> {t(f.q)}
{t(f.a)}
))}
); } // ─── Final CTA ──────────────────────────────────────────────────────────── function FinalCta() { const t = useT(); return (
{t('cta.eyebrow')}

{t('cta.headline')}

{t('cta.subline')}

trackEvent('cta_final_primary', { section: 'final' })}>{t('cta.button')} trackEvent('cta_final_email', { section: 'final' })}>{t('cta.email.note')}
); } // ─── Footer ─────────────────────────────────────────────────────────────── function SiteFooter() { const t = useT(); const { lang, setLang } = useLang(); return ( ); } Object.assign(window, { SiteNav, HeroSection, MarqueeStrip, PainSection, SolutionBridge, ChannelsSection, StatsSection, CustomersSection, PricingSection, FaqSection, FinalCta, SiteFooter, });