/* global React */ const { useState, useEffect, useRef } = React; const PREFERS_REDUCED_MOTION = typeof window !== 'undefined' && window.matchMedia ? window.matchMedia('(prefers-reduced-motion: reduce)').matches : false; function Button({ children, variant = 'primary', icon, onClick, style }) { const base = { fontFamily: 'var(--font-sans)', fontWeight: 600, fontSize: 15, padding: '12px 22px', borderRadius: 12, border: 'none', cursor: 'pointer', display: 'inline-flex', alignItems: 'center', gap: 8, transition: 'all .18s cubic-bezier(.22,.61,.36,1)', letterSpacing: '-.005em', }; const variants = { primary: { background: 'var(--brand-navy)', color: '#fff' }, accent: { background: 'var(--brand-teal)', color: '#fff' }, pill: { background: 'var(--brand-teal)', color: '#fff', borderRadius: 999, padding: '14px 26px', boxShadow: 'var(--shadow-card)' }, ghost: { background: 'transparent', color: 'var(--brand-navy)', border: '1.5px solid var(--brand-navy)' }, link: { background: 'none', color: 'var(--brand-navy)', padding: '8px 2px' }, }; return ( ); } function Chip({ children, tone = 'teal', icon }) { const tones = { teal: { background: 'var(--teal-050)', color: 'var(--teal-800)' }, navy: { background: 'var(--navy-050)', color: 'var(--brand-navy)' }, sand: { background: 'var(--cream-200)', color: 'var(--sand-800)' }, }; return ( {icon && } {children} ); } function Eyebrow({ children }) { return (
{children}
); } function Rule() { return
; } function useIsMobile(bp = 768) { const [mobile, setMobile] = useState(window.innerWidth < bp); useEffect(() => { const h = () => setMobile(window.innerWidth < bp); window.addEventListener('resize', h, { passive: true }); return () => window.removeEventListener('resize', h); }, []); return mobile; } // Observa quando o elemento entra na viewport — dispara animações na rolagem. function useInView({ threshold = 0.15, once = true } = {}) { const ref = useRef(null); const [inView, setInView] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; if (PREFERS_REDUCED_MOTION || !('IntersectionObserver' in window)) { setInView(true); return; } const obs = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) { setInView(true); if (once) obs.disconnect(); } else if (!once) { setInView(false); } }, { threshold }); obs.observe(el); return () => obs.disconnect(); }, []); return [ref, inView]; } // Wrapper de entrada — anima opacidade + transform com stagger por índice. function Reveal({ children, show, index = 0, variant = 'fade', style }) { if (PREFERS_REDUCED_MOTION) return
{children}
; const hidden = { fade: { opacity: 0, transform: 'translateY(16px)' }, pop: { opacity: 0, transform: 'translateY(24px) scale(0.92)' }, }[variant]; const transition = { fade: 'opacity 1.1s ease, transform 1.1s ease', pop: 'opacity 1s ease, transform 1.2s cubic-bezier(.34,1.56,.64,1)', }[variant]; const step = variant === 'pop' ? 0.16 : 0.2; return (
{children}
); } // Conta de 0 até `to` quando `start` vira true. Formata em pt-BR (5.000). function Counter({ to, prefix = '', suffix = '', duration = 3200, start }) { const [val, setVal] = useState(0); useEffect(() => { if (!start) return; if (PREFERS_REDUCED_MOTION) { setVal(to); return; } let raf; const t0 = performance.now(); const tick = now => { const p = Math.min((now - t0) / duration, 1); const eased = 1 - Math.pow(1 - p, 3); // easeOutCubic setVal(to * eased); if (p < 1) raf = requestAnimationFrame(tick); else setVal(to); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [start, to]); return {prefix}{Math.round(val).toLocaleString('pt-BR')}{suffix}; } // ——— Tracking ——————————————————————————————————————————————— // Empurra eventos para o dataLayer. O GTM distribui para GA4 + Meta Pixel. function trackEvent(name, params = {}) { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: name, ...params }); } const WA_NUMBER = '5598988502010'; const WA_TEXT = 'Olá, vim pelo site da Clínica Cuidar dos Olhos e gostaria de mais informações.'; const WA_URL = `https://wa.me/${WA_NUMBER}?text=${encodeURIComponent(WA_TEXT)}`; // Ponto único de saída para o WhatsApp: dispara o evento e abre o chat. // `source` identifica de qual CTA veio (navbar, hero, doctor, contato_cta...). function openWhatsApp(source = 'site') { trackEvent('whatsapp_click', { whatsapp_source: source }); window.open(WA_URL, '_blank', 'noopener,noreferrer'); } Object.assign(window, { Button, Chip, Eyebrow, Rule, useIsMobile, useInView, Reveal, Counter, trackEvent, openWhatsApp, WA_URL, });