/* 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,
});