try{sessionStorage.removeItem('op-bf-visto')}catch(e){}; /* ========================================================= OPERALIZE SISTEMAS · JavaScript Cada módulo só roda se o bloco existir na página. No Elementor: cole em Elementor > Custom Code (antes de ) ou num widget HTML no fim da página. Os seletores usam data-op="…" (atributo), não IDs. No Elementor: Avançado > Atributos > data-op|nome ========================================================= */ (function () { document.documentElement.classList.add('js'); const $ = (s, r = document) => r.querySelector(s); const $$ = (s, r = document) => Array.from(r.querySelectorAll(s)); /* Cabeçalho: fica sólido ao rolar · botão voltar ao topo */ const header = $('[data-op="header"]'); const totop = $('[data-op="totop"]'); const onScroll = () => { header && header.classList.toggle('is-scrolled', scrollY > 40); totop && totop.classList.toggle('is-visible', scrollY > 700); }; addEventListener('scroll', onScroll, { passive: true }); onScroll(); /* Menu do celular */ const toggle = $('[data-op="menu-toggle"]'); const mobile = $('[data-op="mobile-menu"]'); if (toggle && mobile) { const set = open => { mobile.classList.toggle('is-open', open); header && header.classList.toggle('is-open', open); toggle.setAttribute('aria-expanded', open); }; toggle.addEventListener('click', () => set(!mobile.classList.contains('is-open'))); $$('a', mobile).forEach(a => a.addEventListener('click', () => set(false))); } /* Celular 3D (gira ao aparecer na tela; clique repete; segue o mouse) */ $$('[data-op="phone3d"]').forEach(st => { const el = $('[data-op="p-phone"]', st), glare = $('[data-op="p-glare"]', st), shade = $('[data-op="p-shade"]', st), shadow = $('[data-op="p-shadow"]', st), ring = $('[data-op="p-ring"]', st); const D = parseFloat(st.dataset.duration || '1.8') * 1000, spins = parseFloat(st.dataset.spins || '2'); const area = st.closest('section') || st; let start = 1e12, mx = 0, my = 0, cx = 0, cy = 0; const go = () => { if (start > 1e11) start = performance.now(); }; new IntersectionObserver(es => es.some(e => e.isIntersecting) && go(), { threshold: .15 }).observe(st); setInterval(() => { if (start > 1e11) { const r = st.getBoundingClientRect(); if (r.top < innerHeight * .95 && r.bottom > 0) go(); } }, 250); let last = 0; area.addEventListener('mousemove', ev => { const r = area.getBoundingClientRect(); mx = Math.max(-1, Math.min(1, (ev.clientX - r.left) / r.width * 2 - 1)); my = Math.max(-1, Math.min(1, (ev.clientY - r.top) / r.height * 2 - 1)); }); area.addEventListener('mouseleave', () => { mx = 0; my = 0; }); el.addEventListener('click', () => { start = performance.now(); }); const tick = now => { last = now; const k = Math.min(1.15, st.clientHeight * .8 / 620, st.clientWidth / 380); const t = Math.min(1, Math.max(0, (now - start - 80) / D)); const e = 1 - Math.pow(1 - t, 5), e3 = 1 - Math.pow(1 - t, 3); const back = t < 1 ? 1 + 2.2 * Math.pow(t - 1, 3) + 1.2 * Math.pow(t - 1, 2) : 1; cx += (mx - cx) * .06; cy += (my - cy) * .06; const idle = Math.min(1, Math.max(0, (now - start - 80 - D) / 1200)); const fl = Math.sin((now - start) / 1100) * 10 * idle; const ry = -22 - 360 * spins * (1 - e) + Math.sin((now - start) / 2800) * 7 * idle + cx * 24 * idle; const rx = 8 + 34 * (1 - e3) - cy * 12 * idle; const rz = -3 + 18 * (1 - e3); el.style.opacity = Math.min(1, t * 6); el.style.transform = `translateY(${360 * (1 - back) - fl}px) scale(${k * (.42 + .58 * e3)}) rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`; const p = Math.min(1, Math.max(0, (now - start - 80 - D * .82) / 1500)); el.style.setProperty('--p', p.toFixed(3)); const a = ((((ry + 180) % 360) + 360) % 360) - 180; glare.style.transform = `translateX(${(-a * 2.2 - cx * 12).toFixed(1)}%)`; shade.style.opacity = Math.min(.6, Math.max(0, Math.abs(Math.sin(a * Math.PI / 180)) - .42) * 1.2).toFixed(3); shadow.style.opacity = (e3 * (.95 - fl / 50)).toFixed(3); shadow.style.transform = `scaleX(${(.5 + .5 * e3 + fl / 90).toFixed(3)})`; ring.style.opacity = (p * .9).toFixed(3); ring.style.transform = `translate(-50%,-50%) scale(${(.8 + .2 * p).toFixed(3)})`; }; const raf = n => { requestAnimationFrame(raf); tick(n); }; requestAnimationFrame(raf); setInterval(() => { const n = performance.now(); if (n - last > 60) tick(n); }, 50); }); /* Palavra digitando no título (no Elementor Pro: widget Animated Headline > Typing) */ $$('[data-op="typed"]').forEach(el => { const words = (el.dataset.words || el.textContent).split(',').map(w => w.trim()).filter(Boolean); let w = 0, c = words[0].length, del = true; const tick = () => { const word = words[w]; c += del ? -1 : 1; el.textContent = word.slice(0, c); let wait = del ? 55 : 95; if (!del && c === word.length) { del = true; wait = 1600; } else if (del && c === 0) { del = false; w = (w + 1) % words.length; wait = 300; } setTimeout(tick, wait); }; setTimeout(tick, 1800); }); /* Fade ao rolar + contadores (no Elementor: Motion Effects e widget Counter) */ const countUp = el => { const to = parseFloat(el.dataset.count), t0 = performance.now(), dur = 1400; const step = t => { const p = Math.min(1, (t - t0) / dur); el.textContent = Math.round(to * (1 - Math.pow(1 - p, 3))); if (p < 1) requestAnimationFrame(step); }; requestAnimationFrame(step); }; const reveal = el => { el.classList.add('is-visible'); $$('[data-count]', el).forEach(countUp); if (el.matches('[data-count]')) countUp(el); }; const targets = $$('[data-reveal]'); if ('IntersectionObserver' in window) { const io = new IntersectionObserver(entries => entries.forEach(e => { if (e.isIntersecting) { reveal(e.target); io.unobserve(e.target); } }), { threshold: 0.15, rootMargin: '0px 0px -40px 0px' }); targets.forEach(el => io.observe(el)); } else targets.forEach(reveal); setTimeout(() => targets.forEach(el => el.classList.contains('is-visible') || el.getBoundingClientRect().top < innerHeight && reveal(el)), 2500); /* Clientes em destaque: logo central ampliado e laterais */ $$('[data-op="clients"]').forEach(root => { const items = $$('[data-op="client"]', root); const nameEl = $('[data-op="client-name"]', root); const bar = $('[data-op="client-bar"]', root); if (!items.length) return; let i = 0, timer, paused = false; const n = items.length, DUR = 4000; const show = k => { i = (k + n) % n; items.forEach((el, j) => { let off = j - i; if (off > n / 2) off -= n; if (off < -n / 2) off += n; el.style.order = off + n; el.classList.toggle('is-active', off === 0); el.classList.toggle('is-near', Math.abs(off) === 1); el.classList.toggle('is-far', Math.abs(off) > 2); el.setAttribute('aria-current', off === 0 ? 'true' : 'false'); }); if (nameEl) nameEl.textContent = $('.op-client__name', items[i])?.textContent || $('img', items[i])?.alt || ''; if (bar) { bar.style.transition = 'none'; bar.style.width = '0'; void bar.offsetWidth; bar.style.transition = `width ${DUR}ms linear`; bar.style.width = paused ? '0' : '100%'; } clearTimeout(timer); if (!paused) timer = setTimeout(() => show(i + 1), DUR); }; items.forEach((el, j) => el.addEventListener('click', e => { const link = el.dataset.href; if (j === i && link) { window.open(link, '_blank', 'noopener'); return; } e.preventDefault(); show(j); })); $('[data-op="client-prev"]', root)?.addEventListener('click', () => show(i - 1)); $('[data-op="client-next"]', root)?.addEventListener('click', () => show(i + 1)); const stage = $('[data-op="featured"]', root); stage.addEventListener('mouseenter', () => { paused = true; show(i); }); stage.addEventListener('mouseleave', () => { paused = false; show(i); }); let sx = 0; stage.addEventListener('touchstart', e => { sx = e.touches[0].clientX; }, { passive: true }); stage.addEventListener('touchend', e => { const dx = e.changedTouches[0].clientX - sx; if (Math.abs(dx) > 40) show(i + (dx < 0 ? 1 : -1)); }); show(0); }); /* Faixa de logos: rola sozinha e dá para arrastar com o dedo ou o mouse */ $$('[data-op="logo-strip"]').forEach(strip => { const track = strip.firstElementChild; if (!track) return; Array.from(track.children).forEach(ch => { const c = ch.cloneNode(true); c.setAttribute('aria-hidden', 'true'); track.appendChild(c); }); let x = 0, half = 0, drag = false, lastX = 0, vel = 0; const speed = parseFloat(strip.dataset.speed || '0.5'); const measure = () => { half = track.scrollWidth / 2; }; measure(); addEventListener('resize', measure); let lastT = 0; const step = () => { lastT = performance.now(); if (!drag) { x -= speed + vel; vel *= 0.94; } if (half) { if (x <= -half) x += half; if (x > 0) x -= half; } track.style.transform = `translate3d(${x}px,0,0)`; }; const loop = () => { requestAnimationFrame(loop); step(); }; requestAnimationFrame(loop); setInterval(() => { if (performance.now() - lastT > 60) step(); }, 33); strip.addEventListener('pointerdown', e => { drag = true; lastX = e.clientX; vel = 0; strip.classList.add('is-dragging'); strip.setPointerCapture(e.pointerId); }); strip.addEventListener('pointermove', e => { if (!drag) return; const dx = e.clientX - lastX; lastX = e.clientX; x += dx; vel = -dx * 0.5; }); const up = () => { drag = false; strip.classList.remove('is-dragging'); }; strip.addEventListener('pointerup', up); strip.addEventListener('pointercancel', up); }); /* Black Friday: aviso (1,5 s) > popup grande > botão pequeno (no Elementor Pro: 2 Popups + 1 botão fixo; ver o guia) */ const teaser = $('[data-op="bf-teaser"]'); const pill = $('[data-op="bf-pill"]'); const modal = $('[data-op="bf-modal"]'); if (teaser && pill && modal) { const KEY = 'op-bf-visto'; const state = s => { teaser.classList.toggle('is-open', s === 'teaser'); pill.classList.toggle('is-open', s === 'pill'); modal.classList.toggle('is-open', s === 'modal'); document.body.style.overflow = s === 'modal' ? 'hidden' : ''; }; const delay = parseFloat(teaser.dataset.delay || '1500'); setTimeout(() => state(sessionStorage.getItem(KEY) ? 'pill' : 'teaser'), delay); teaser.addEventListener('click', () => state('modal')); $('[data-op="bf-teaser-close"]').addEventListener('click', e => { e.stopPropagation(); sessionStorage.setItem(KEY, 1); state('pill'); }); pill.addEventListener('click', () => state('modal')); const close = () => { sessionStorage.setItem(KEY, 1); state('pill'); $('[data-op="bf-loading"]').classList.remove('is-open'); }; $$('[data-op="bf-close"]').forEach(b => b.addEventListener('click', close)); modal.addEventListener('click', e => { if (e.target === modal) close(); }); addEventListener('keydown', e => { if (e.key === 'Escape' && modal.classList.contains('is-open')) close(); }); const loading = $('[data-op="bf-loading"]'); const manual = $('[data-op="bf-manual"]'); $$('[data-op="bf-option"]').forEach(opt => opt.addEventListener('click', e => { e.preventDefault(); const url = opt.href; $('[data-op="bf-choice"]').textContent = opt.dataset.label || ''; manual.href = url; loading.classList.add('is-open'); setTimeout(() => { window.location.href = url; }, 1200); })); } })(); (()=>{const root=document.querySelector('[data-op="clients"]');if(!root)return;const pulse=()=>root.querySelectorAll('[data-op="client"]').forEach(el=>{el.classList.remove('op-client-switch');void el.offsetWidth;el.classList.add('op-client-switch')});root.querySelectorAll('[data-op="client-prev"],[data-op="client-next"]').forEach(button=>button.addEventListener('click',pulse));})();