const AnimationController = ({ children }) => { React.useEffect(() => { // ── 1. REVEAL UNIFICADO — cobre data-reveal="true" (CSS classes) // E data-*-reveal (inline opacity/transform) de todas as páginas ── const setupScrollReveal = () => { // Sistema A: data-reveal="true" (usa classes CSS .revealed) const revealEls = document.querySelectorAll('[data-reveal="true"]'); if (revealEls.length > 0) { const obsA = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('revealed'); obsA.unobserve(entry.target); } }); }, { threshold: 0.15 }); revealEls.forEach(el => obsA.observe(el)); } // Sistema B: data-*-reveal com delay (inline opacity/transform) // Cobre: data-metodo-reveal, data-plano-reveal, data-ops-reveal, data-mon-reveal const revealAttrEls = document.querySelectorAll( '[data-metodo-reveal], [data-plano-reveal], [data-ops-reveal], [data-mon-reveal]' ); if (revealAttrEls.length > 0) { const obsB = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting && !entry.target._revealed) { entry.target._revealed = true; const el = entry.target; const delay = parseInt(el.dataset.metodoReveal || '0') || parseInt(el.dataset.planoReveal || '0') || parseInt(el.dataset.opsReveal || '0') || parseInt(el.dataset.monReveal || '0'); setTimeout(() => { el.style.opacity = '1'; el.style.transform = 'translateY(0) translateX(0)'; }, delay); obsB.unobserve(el); } }); }, { threshold: 0.10 }); revealAttrEls.forEach(el => obsB.observe(el)); } }; setupScrollReveal(); // ── 3. BARRAS ANIMADAS (gráfico de gargalos com width animation) ── const setupBarAnimation = () => { const barContainers = document.querySelectorAll('[data-bar-animate="true"]'); if (barContainers.length === 0) return; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting && !entry.target.dataset.animationStarted) { entry.target.dataset.animationStarted = 'true'; const bars = entry.target.querySelectorAll('[data-bar-width]'); bars.forEach((bar, idx) => { const width = bar.dataset.barWidth; setTimeout(() => { bar.style.animation = `barGrow 0.8s cubic-bezier(0.25, 1, 0.5, 1) forwards`; bar.style.width = width; }, idx * 80); }); observer.unobserve(entry.target); } }); }, { threshold: 0.15 }); barContainers.forEach(el => observer.observe(el)); }; setTimeout(setupBarAnimation, 200); // ── 4. CONTADORES ANIMADOS (números contando de 0 até valor) ── const setupCounters = () => { const counterElements = document.querySelectorAll('[data-counter]'); if (counterElements.length === 0) return; const animateCounter = (el) => { const finalValue = parseInt(el.dataset.counter, 10); if (isNaN(finalValue)) return; const duration = 1200; // 1.2s const startTime = Date.now(); const format = el.dataset.counterFormat || 'number'; const update = () => { const elapsed = Date.now() - startTime; const progress = Math.min(elapsed / duration, 1); // easing ease-out const eased = 1 - Math.pow(1 - progress, 3); const current = Math.floor(finalValue * eased); if (format === 'number') { el.textContent = current.toLocaleString('pt-BR'); } else if (format === 'percentage') { el.textContent = current + '%'; } else { el.textContent = current; } if (progress < 1) { requestAnimationFrame(update); } else { el.textContent = finalValue.toLocaleString('pt-BR'); } }; update(); }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting && !entry.target.dataset.counterStarted) { entry.target.dataset.counterStarted = 'true'; animateCounter(entry.target); observer.unobserve(entry.target); } }); }, { threshold: 0.15 }); counterElements.forEach(el => observer.observe(el)); }; setTimeout(setupCounters, 200); // ── 5. CARDS HOVER SOFISTICADO (glow + translateY) ── const setupCardHover = () => { const cards = document.querySelectorAll('[data-card-hover="true"]'); cards.forEach(card => { card.classList.add('service-card-hover'); }); }; setupCardHover(); // ── 6. BADGE PULSE (ripple no "SDR Ativo") ── const setupBadgePulse = () => { const badges = document.querySelectorAll('[data-badge-pulse="true"]'); badges.forEach(badge => { const dotEl = badge.querySelector('[data-pulse-dot]'); if (dotEl) { dotEl.classList.add('badge-pulse'); } }); }; setupBadgePulse(); // ── 7. BADGE BLINK (piscar suave no "Ao vivo") ── const setupBadgeBlink = () => { const badges = document.querySelectorAll('[data-badge-blink="true"]'); badges.forEach(badge => { badge.classList.add('badge-blink'); }); }; setupBadgeBlink(); }, []); return null; }; Object.assign(window, { AnimationController });