/* ========== WhatsApp ========== */ function goWhatsapp(phone) { window.open('https://api.whatsapp.com/send?phone=' + phone, '_blank'); } /* ========== Form helpers ========== */ function clearForm() { var ids = ['email', 'telefone', 'empresa', 'nome', 'mensagem']; ids.forEach(function(id){ var el = document.getElementById(id); if (el) el.value = ''; }); } function getURLParameterNew(name) { try { return decodeURIComponent((new RegExp(name + '=([^&;]+?)(&|#|;|$)').exec(location.href) || [,''])[1].replace(/\+/g, '%20')) || null; } catch(e) { return null; } } /* ========== CRM integration (same as v3) ========== */ function sendCRM() { fetch('https://app.pipe.run/webservice/integradorJson?hash=07a2befe-93a4-45f3-a44d-06e04ee305e', { headers: { accept: 'application/json, text/javascript, */*; q=0.01', 'content-type': 'text/plain; charset=UTF-8' }, referrerPolicy: 'strict-origin-when-cross-origin', body: JSON.stringify({ leads: [{ user: 'contato@siscode.com.br', id: (document.getElementById('email')||{}).value || '', title: (document.getElementById('empresa')||{}).value || '', name: (document.getElementById('nome')||{}).value || '', email: (document.getElementById('email')||{}).value || '', personal_phone: (document.getElementById('telefone')||{}).value || '', mobile_phone: (document.getElementById('telefone')||{}).value || '', company: (document.getElementById('empresa')||{}).value || '', last_conversion: { source: 'Formulário do Site' }, custom_fields: { Mensagem: (document.getElementById('mensagem')||{}).value || '' } }] }), method: 'POST', mode: 'no-cors', credentials: 'omit' }).catch(function(){}); } /* ========== Contact form submit (same Lambda URL as v3) ========== */ function enviaMensagem(language) { var nome = document.getElementById('nome'); var email = document.getElementById('email'); var telefone = document.getElementById('telefone'); var mensagem = document.getElementById('mensagem'); var empresa = document.getElementById('empresa'); var erroEl = document.getElementById('erro'); var sucessoEl = document.getElementById('sucesso'); var submitf = document.getElementById('submitf'); if (!nome || !email || !telefone) return false; if (nome.value.trim() === '' || email.value.trim() === '' || telefone.value.trim() === '') { if (erroEl) erroEl.innerHTML = language === 'pt' ? 'Preencha todos os campos antes de enviar a mensagem...' : 'Fill in all fields before sending the message...'; return false; } if (submitf) { submitf.disabled = true; submitf.classList.add('loading'); } if (erroEl) erroEl.innerHTML = ''; var body = new URLSearchParams({ mensagem: (mensagem && mensagem.value.trim()) || '', nome: nome.value.trim(), empresa: (empresa && empresa.value.trim()) || '', email: email.value.trim(), telefone: telefone.value.trim(), campanha: getURLParameterNew('utm_campaign') || '', grupo: getURLParameterNew('utm_medium') || '', anuncio: getURLParameterNew('utm_source') || '' }); fetch('./contact.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString() }) .then(function(r){ return r.json().catch(function(){ return { success: false, message: 'Resposta inválida do servidor.' }; }); }) .then(function(data){ if (data.success) { if (erroEl) erroEl.innerHTML = ''; if (window.fbq) { try { fbq('track', 'Lead'); } catch(e){} } if (sucessoEl) sucessoEl.innerHTML = data.message || (language === 'pt' ? 'Email enviado com sucesso! Obrigado.' : 'Email sent successfully! Thanks.'); clearForm(); if (submitf) { submitf.disabled = false; submitf.classList.remove('loading'); } // window.location.href = './thanks'; // Comentado para evitar erro 404 em ambiente estático local } else { if (erroEl) erroEl.innerHTML = data.message; if (submitf) { submitf.disabled = false; submitf.classList.remove('loading'); } } }) .catch(function(){ if (erroEl) erroEl.innerHTML = language === 'pt' ? 'Problema ao enviar e-mail, tente novamente ou envie para contato@siscode.com.br' : 'Problem sending email, try again or send a direct email to contato@siscode.com.br'; if (submitf) { submitf.disabled = false; submitf.classList.remove('loading'); } }); return false; } /* ======================================================== EFFECTS: scroll progress, reveal, counters, nav, tilt ======================================================== */ (function(){ 'use strict'; /* Disable browser scroll-restoration globally — prevents the browser from "jumping to top, then restoring" when late-loading content (fonts, SDK, images) changes document height. We handle scroll explicitly. */ if ('scrollRestoration' in history) history.scrollRestoration = 'manual'; /* DEBUG: spy on scroll/focus calls to identify which script is causing the "jump to top" bug. Enable with ?scrolldebug=1 in URL. Logs every window.scrollTo / window.scroll / scrollIntoView / focus call with stack. */ if (/[?&]scrolldebug=1/.test(location.search)) { var _scrollTo = window.scrollTo; window.scrollTo = function(){ console.warn('[scroll-debug] scrollTo called with', arguments, new Error().stack); return _scrollTo.apply(window, arguments); }; var _scroll = window.scroll; window.scroll = function(){ console.warn('[scroll-debug] scroll called with', arguments, new Error().stack); return _scroll.apply(window, arguments); }; var _scIV = Element.prototype.scrollIntoView; Element.prototype.scrollIntoView = function(){ console.warn('[scroll-debug] scrollIntoView on', this, new Error().stack); return _scIV.apply(this, arguments); }; var _focus = HTMLElement.prototype.focus; HTMLElement.prototype.focus = function(){ if (this !== document.activeElement) { console.warn('[scroll-debug] focus() on', this, new Error().stack); } return _focus.apply(this, arguments); }; // Also spy on history state changes — some analytics trigger "virtual page views" // which can reset scroll position in Chrome. var _push = history.pushState, _replace = history.replaceState; history.pushState = function(){ console.warn('[scroll-debug] history.pushState', arguments, new Error().stack); return _push.apply(history, arguments); }; history.replaceState = function(){ console.warn('[scroll-debug] history.replaceState', arguments, new Error().stack); return _replace.apply(history, arguments); }; // And location.hash mutations Object.defineProperty(Location.prototype, 'hash', { configurable: true, set: function(v){ console.warn('[scroll-debug] location.hash =', v, new Error().stack); return Reflect.set(Object.getPrototypeOf(location), 'hash', v, location); }, get: function(){ return Reflect.get(Object.getPrototypeOf(location), 'hash', location); } }); window.addEventListener('scroll', function(){ if (!window.__lastScrollY) window.__lastScrollY = 0; var jump = Math.abs(window.scrollY - window.__lastScrollY); if (jump > 100) console.warn('[scroll-debug] scroll jumped by', jump, 'px (from', window.__lastScrollY, 'to', window.scrollY + ')'); window.__lastScrollY = window.scrollY; }, { passive: true }); } var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; /* ---- Nav behavior (scrolled, burger, submenus) ---- */ var header = document.getElementById('siteHeader'); var burger = document.getElementById('menuToggle'); var nav = document.getElementById('mainNav'); function onScroll() { if (header) header.classList.toggle('scrolled', window.scrollY > 20); var bar = document.querySelector('.scroll-progress'); if (bar) { var h = document.documentElement; var scrolled = (h.scrollTop || document.body.scrollTop); var max = (h.scrollHeight - h.clientHeight) || 1; bar.style.width = (scrolled / max * 100) + '%'; } } window.addEventListener('scroll', onScroll, { passive: true }); /* Do NOT call onScroll() on boot — user explicitly doesn't want scroll events firing at load. Nav .scrolled class and progress bar will sync on first scroll. */ if (burger && nav) { burger.addEventListener('click', function(){ var open = nav.classList.toggle('open'); burger.setAttribute('aria-expanded', open ? 'true' : 'false'); document.body.style.overflow = open ? 'hidden' : ''; }); // Dropdown triggers ALWAYS preventDefault — their href="#" is just a placeholder, // never meant to navigate. Without this, desktop clicks scroll to top. nav.querySelectorAll('.has-sub > a').forEach(function(a){ a.addEventListener('click', function(e){ e.preventDefault(); if (window.innerWidth < 992) { a.parentElement.classList.toggle('open'); } }); }); nav.querySelectorAll('a').forEach(function(a){ if (!a.parentElement.classList.contains('has-sub')) { a.addEventListener('click', function(){ if (window.innerWidth < 992) { nav.classList.remove('open'); burger.setAttribute('aria-expanded', 'false'); document.body.style.overflow = ''; } }); } }); } /* ---- Scroll reveal via IntersectionObserver ---- */ var revealEls = document.querySelectorAll('.reveal'); if ('IntersectionObserver' in window && !reduced) { var io = new IntersectionObserver(function(entries){ entries.forEach(function(e){ if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { threshold: .12, rootMargin: '0px 0px -40px 0px' }); revealEls.forEach(function(el){ io.observe(el); }); } else { revealEls.forEach(function(el){ el.classList.add('in'); }); } /* ---- Counter-up animation ---- */ function animateCount(el) { var target = parseFloat(el.getAttribute('data-count') || el.textContent) || 0; var suffix = el.getAttribute('data-suffix') || ''; var duration = 1800; var start = performance.now(); var initial = 0; function tick(now) { var p = Math.min(1, (now - start) / duration); var eased = 1 - Math.pow(1 - p, 3); var value = initial + (target - initial) * eased; el.textContent = (target % 1 === 0 ? Math.round(value) : value.toFixed(1)) + suffix; if (p < 1) requestAnimationFrame(tick); } requestAnimationFrame(tick); } var counters = document.querySelectorAll('.count-up'); if (counters.length && 'IntersectionObserver' in window && !reduced) { var cio = new IntersectionObserver(function(entries){ entries.forEach(function(e){ if (e.isIntersecting) { animateCount(e.target); cio.unobserve(e.target); } }); }, { threshold: .4 }); counters.forEach(function(el){ cio.observe(el); }); } /* ---- Tilt on cards ---- */ if (!reduced) { document.querySelectorAll('.tilt').forEach(function(card){ var rx = 0, ry = 0; card.addEventListener('mousemove', function(ev){ var r = card.getBoundingClientRect(); var x = ev.clientX - r.left, y = ev.clientY - r.top; rx = ((y / r.height) - 0.5) * -6; ry = ((x / r.width) - 0.5) * 6; card.style.transform = 'perspective(800px) rotateX('+rx+'deg) rotateY('+ry+'deg)'; }); card.addEventListener('mouseleave', function(){ card.style.transform = ''; }); }); } /* ---- Spotlight: track mouse for .spotlight ---- */ if (!reduced) { document.querySelectorAll('.spotlight').forEach(function(el){ el.addEventListener('mousemove', function(ev){ var r = el.getBoundingClientRect(); el.style.setProperty('--mx', (ev.clientX - r.left) + 'px'); el.style.setProperty('--my', (ev.clientY - r.top) + 'px'); }); }); } /* ---- Ripple effect ---- */ document.addEventListener('click', function(e){ var t = e.target.closest('.ripple'); if (!t) return; var r = t.getBoundingClientRect(); t.style.setProperty('--rx', (e.clientX - r.left) + 'px'); t.style.setProperty('--ry', (e.clientY - r.top) + 'px'); t.classList.remove('active'); void t.offsetWidth; t.classList.add('active'); }); /* ---- Smooth anchor scroll for in-page links ---- Also guards pure "#" / empty hashes from the default browser behavior (which is to scroll to top — this was making the whole page "jump up" whenever a user clicked a placeholder link). */ document.addEventListener('click', function(e){ var a = e.target.closest('a[href^="#"]'); if (!a) return; var href = a.getAttribute('href'); if (!href || href === '#' || href === '#!') { e.preventDefault(); return; } var target = document.querySelector(href); if (target) { e.preventDefault(); var top = target.getBoundingClientRect().top + window.pageYOffset - 80; window.scrollTo({ top: top, behavior: reduced ? 'auto' : 'smooth' }); } }); /* ---- WhatsApp mobile peek ---- */ setTimeout(function(){ document.querySelectorAll('.fab-whatsapp .fab-msg').forEach(function(m){ m.style.opacity = 1; m.style.transform = 'translateX(0)'; }); setTimeout(function(){ document.querySelectorAll('.fab-whatsapp .fab-msg').forEach(function(m){ m.style.opacity = ''; m.style.transform = ''; }); }, 6000); }, 1800); })(); /* ========== Loading dots spinner style (injected) ========== */ (function(){ if (document.getElementById('v4-dots-style')) return; var s = document.createElement('style'); s.id = 'v4-dots-style'; s.innerHTML = '.dots-loader{display:inline-flex;gap:4px}.dots-loader i{width:6px;height:6px;border-radius:50%;background:var(--brand);display:inline-block;animation:dotsBounce 1s infinite}.dots-loader i:nth-child(2){animation-delay:.15s}.dots-loader i:nth-child(3){animation-delay:.3s}@keyframes dotsBounce{0%,80%,100%{transform:translateY(0);opacity:.4}40%{transform:translateY(-6px);opacity:1}}.btn-v4.loading{opacity:.7;cursor:wait}'; document.head.appendChild(s); })();