Boas-vindas ao WordPress. Esse é o seu primeiro post. Edite-o ou exclua-o, e então comece a escrever!

Uma resposta

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

/** * SITE ONPAGE PADRÃO — main.js * Funcionalidades: Slider, Header sticky, Menu mobile, * Cookie banner, Scroll animations, Contador, Back to top */ 'use strict'; /* ============================================================ SLIDER ============================================================ */ (function initSlider() { const slider = document.getElementById('slider'); if (!slider) return; const slides = slider.querySelectorAll('.slide'); const dots = slider.querySelectorAll('.slider__dot'); const btnPrev = document.getElementById('slider-prev'); const btnNext = document.getElementById('slider-next'); const INTERVAL = 6000; let current = 0; let timer = null; let startX = 0; function goTo(index) { slides[current].classList.remove('active'); slides[current].setAttribute('aria-hidden', 'true'); dots[current].classList.remove('active'); dots[current].setAttribute('aria-selected', 'false'); current = (index + slides.length) % slides.length; slides[current].classList.add('active'); slides[current].removeAttribute('aria-hidden'); dots[current].classList.add('active'); dots[current].setAttribute('aria-selected', 'true'); } function next() { goTo(current + 1); } function prev() { goTo(current - 1); } function startAuto() { timer = setInterval(next, INTERVAL); } function stopAuto() { clearInterval(timer); } if (btnNext) btnNext.addEventListener('click', () => { stopAuto(); next(); startAuto(); }); if (btnPrev) btnPrev.addEventListener('click', () => { stopAuto(); prev(); startAuto(); }); dots.forEach(dot => { dot.addEventListener('click', () => { const idx = parseInt(dot.dataset.slide, 10); stopAuto(); goTo(idx); startAuto(); }); }); // Touch / swipe slider.addEventListener('touchstart', e => { startX = e.changedTouches[0].screenX; stopAuto(); }, { passive: true }); slider.addEventListener('touchend', e => { const diff = startX - e.changedTouches[0].screenX; if (Math.abs(diff) > 50) diff > 0 ? next() : prev(); startAuto(); }, { passive: true }); // Pause on hover slider.addEventListener('mouseenter', stopAuto); slider.addEventListener('mouseleave', startAuto); // Keyboard slider.addEventListener('keydown', e => { if (e.key === 'ArrowLeft') { stopAuto(); prev(); startAuto(); } if (e.key === 'ArrowRight') { stopAuto(); next(); startAuto(); } }); startAuto(); })(); /* ============================================================ STICKY HEADER ============================================================ */ (function initHeader() { const header = document.getElementById('header'); if (!header) return; function update() { if (window.scrollY > 80) { header.classList.add('scrolled'); } else { header.classList.remove('scrolled'); } } window.addEventListener('scroll', update, { passive: true }); update(); })(); /* ============================================================ MOBILE MENU ============================================================ */ (function initMobileMenu() { const toggle = document.getElementById('menu-toggle'); const nav = document.getElementById('main-nav'); if (!toggle || !nav) return; function close() { nav.classList.remove('open'); toggle.setAttribute('aria-expanded', 'false'); toggle.setAttribute('aria-label', 'Abrir menu'); document.body.style.overflow = ''; } toggle.addEventListener('click', () => { const isOpen = nav.classList.toggle('open'); toggle.setAttribute('aria-expanded', String(isOpen)); toggle.setAttribute('aria-label', isOpen ? 'Fechar menu' : 'Abrir menu'); document.body.style.overflow = isOpen ? 'hidden' : ''; }); // Close on link click nav.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', close); }); // Close on outside click document.addEventListener('click', e => { if (!header.contains(e.target)) close(); }); // Close on Escape document.addEventListener('keydown', e => { if (e.key === 'Escape') close(); }); // Header reference for outside click const header = document.getElementById('header'); })(); /* ============================================================ ACTIVE NAV LINK ON SCROLL ============================================================ */ (function initScrollSpy() { const sections = document.querySelectorAll('section[id]'); const links = document.querySelectorAll('.nav-link[href^="#"]'); if (!sections.length || !links.length) return; const headerH = parseInt(getComputedStyle(document.documentElement) .getPropertyValue('--header-h')) || 72; const observer = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { const id = entry.target.id; links.forEach(link => { link.classList.toggle('active', link.getAttribute('href') === `#${id}`); link.setAttribute('aria-current', link.getAttribute('href') === `#${id}` ? 'true' : 'false'); }); } }); }, { rootMargin: `-${headerH}px 0px -50% 0px`, threshold: 0 }); sections.forEach(s => observer.observe(s)); })(); /* ============================================================ SCROLL ANIMATIONS (fade-in) ============================================================ */ (function initScrollAnimations() { const els = document.querySelectorAll('.fade-in'); if (!els.length) return; if (!('IntersectionObserver' in window)) { els.forEach(el => el.classList.add('visible')); return; } const observer = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); observer.unobserve(entry.target); } }); }, { threshold: 0.12 }); els.forEach(el => observer.observe(el)); })(); /* ============================================================ ANIMATED COUNTER ============================================================ */ (function initCounters() { const counters = document.querySelectorAll('.number-item__value[data-count]'); if (!counters.length) return; const DURATION = 2000; function animateCounter(el) { const target = parseInt(el.dataset.count, 10); const start = performance.now(); function step(now) { const elapsed = now - start; const progress = Math.min(elapsed / DURATION, 1); // Ease out quad const eased = 1 - (1 - progress) * (1 - progress); el.textContent = Math.floor(eased * target).toLocaleString('pt-BR'); if (progress < 1) requestAnimationFrame(step); else el.textContent = target.toLocaleString('pt-BR'); } requestAnimationFrame(step); } const observer = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { animateCounter(entry.target); observer.unobserve(entry.target); } }); }, { threshold: 0.5 }); counters.forEach(el => observer.observe(el)); })(); /* ============================================================ BACK TO TOP ============================================================ */ (function initBackToTop() { const btn = document.getElementById('back-to-top'); if (!btn) return; window.addEventListener('scroll', () => { btn.classList.toggle('visible', window.scrollY > 500); }, { passive: true }); btn.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); })(); /* ============================================================ COOKIE BANNER ============================================================ */ (function initCookieBanner() { const banner = document.getElementById('cookie-banner'); const btnAccept = document.getElementById('cookie-accept'); const btnReject = document.getElementById('cookie-reject'); if (!banner) return; const STORAGE_KEY = 'cookie_consent'; function hideBanner() { banner.classList.remove('visible'); banner.addEventListener('transitionend', () => { banner.hidden = true; }, { once: true }); } // Show if not yet decided if (!localStorage.getItem(STORAGE_KEY)) { // Delay slightly so page loads first setTimeout(() => banner.classList.add('visible'), 1200); } else { banner.hidden = true; } btnAccept && btnAccept.addEventListener('click', () => { localStorage.setItem(STORAGE_KEY, 'accepted'); hideBanner(); }); btnReject && btnReject.addEventListener('click', () => { localStorage.setItem(STORAGE_KEY, 'rejected'); hideBanner(); }); })(); /* ============================================================ CONTACT FORM VALIDATION ============================================================ */ (function initContactForm() { const form = document.getElementById('contact-form'); if (!form) return; const submitBtn = document.getElementById('form-submit'); const successEl = document.getElementById('form-success'); const errorEl = document.getElementById('form-error'); function showError(input, msg) { input.classList.add('invalid'); const errEl = input.closest('.form-group')?.querySelector('.form-error'); if (errEl) errEl.textContent = msg; } function clearError(input) { input.classList.remove('invalid'); const errEl = input.closest('.form-group')?.querySelector('.form-error'); if (errEl) errEl.textContent = ''; } function validateField(field) { const val = field.value.trim(); clearError(field); if (field.required && !val) { showError(field, 'Este campo é obrigatório.'); return false; } if (field.type === 'email' && val && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) { showError(field, 'Informe um e-mail válido.'); return false; } return true; } // Real-time validation on blur form.querySelectorAll('.form-input').forEach(field => { field.addEventListener('blur', () => validateField(field)); field.addEventListener('input', () => { if (field.classList.contains('invalid')) validateField(field); }); }); // Privacy checkbox const privacyCheck = document.getElementById('privacy'); if (privacyCheck) { privacyCheck.addEventListener('change', () => { const errEl = privacyCheck.closest('.form-group--checkbox')?.querySelector('.form-error'); if (errEl) errEl.textContent = ''; }); } form.addEventListener('submit', async e => { e.preventDefault(); // Validate all fields let valid = true; form.querySelectorAll('.form-input').forEach(field => { if (!validateField(field)) valid = false; }); if (privacyCheck && !privacyCheck.checked) { const errEl = privacyCheck.closest('.form-group--checkbox')?.querySelector('.form-error'); if (errEl) errEl.textContent = 'Você precisa aceitar a Política de Privacidade.'; valid = false; } if (!valid) return; // Submit submitBtn.classList.add('loading'); submitBtn.disabled = true; successEl.hidden = true; errorEl.hidden = true; try { const data = new FormData(form); /** * INTEGRAÇÃO DO FORMULÁRIO: * Opção 1 — Formspree: altere o action do form para https://formspree.io/f/SEU_ID * Opção 2 — PHP: crie form-handler.php na raiz (exemplo abaixo) * Opção 3 — EmailJS: use a biblioteca emailjs-com */ const res = await fetch(form.action, { method: 'POST', body: data, headers: { 'Accept': 'application/json' } }); if (res.ok) { successEl.hidden = false; form.reset(); successEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } else { throw new Error('Server error'); } } catch { errorEl.hidden = false; } finally { submitBtn.classList.remove('loading'); submitBtn.disabled = false; } }); })(); /* ============================================================ FOOTER YEAR ============================================================ */ const yearEl = document.getElementById('footer-year'); if (yearEl) yearEl.textContent = new Date().getFullYear(); /* ============================================================ SMOOTH SCROLL — anchor links ============================================================ */ document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', e => { const target = document.querySelector(anchor.getAttribute('href')); if (!target) return; e.preventDefault(); target.scrollIntoView({ behavior: 'smooth' }); }); });