File: /home/u353189757/domains/adaptia.com.br/public_html/script.js
const ADAPT_WHATSAPP_NUMBER = "5547984718479";
const ADAPT_FORM_ENDPOINT = "api/form-submit.php";
const ADAPT_TRACK_ENDPOINT = "api/track.php";
const ADAPT_VISIT_ID_KEY = "adaptia_visit_id";
const defaultMessage = "Olá, Adapt IA! Quero conversar sobre uma proposta para minha empresa.";
function buildWhatsappUrl(message) {
return `https://wa.me/${ADAPT_WHATSAPP_NUMBER}?text=${encodeURIComponent(message)}`;
}
function getFormValue(data, fieldName) {
const value = String(data.get(fieldName) || "").trim();
return value || "Não informado";
}
function createVisitId() {
const randomPart = Math.random().toString(36).slice(2, 10);
return `${Date.now().toString(36)}-${randomPart}`;
}
function getVisitId() {
try {
const existingId = window.sessionStorage.getItem(ADAPT_VISIT_ID_KEY);
if (existingId) {
return existingId;
}
const nextId = createVisitId();
window.sessionStorage.setItem(ADAPT_VISIT_ID_KEY, nextId);
return nextId;
} catch (error) {
return createVisitId();
}
}
function getViewportData() {
return {
width: window.innerWidth || document.documentElement.clientWidth || 0,
height: window.innerHeight || document.documentElement.clientHeight || 0,
dpr: window.devicePixelRatio || 1
};
}
function cloneFormData(data) {
const payload = new FormData();
data.forEach((value, key) => {
payload.append(key, value);
});
payload.append("page_url", window.location.href);
payload.append("referrer", document.referrer || "");
payload.append("visit_id", getVisitId());
payload.append("client_time", new Date().toISOString());
return payload;
}
async function sendFormSubmission(data) {
const controller = new AbortController();
const timeoutId = window.setTimeout(() => controller.abort(), 2600);
try {
const response = await fetch(ADAPT_FORM_ENDPOINT, {
method: "POST",
body: cloneFormData(data),
signal: controller.signal
});
const result = await response.json().catch(() => ({}));
if (!response.ok || !result.ok) {
const error = new Error("form_submission_failed");
error.status = response.status;
error.payload = result;
throw error;
}
return result;
} finally {
window.clearTimeout(timeoutId);
}
}
function updateWhatsappLinks() {
document.querySelectorAll("[data-whatsapp-message]").forEach((link) => {
const message = link.getAttribute("data-whatsapp-message") || defaultMessage;
link.setAttribute("href", buildWhatsappUrl(message));
link.setAttribute("target", "_blank");
link.setAttribute("rel", "noopener noreferrer");
});
}
function setupHeader() {
const header = document.querySelector("[data-header]");
const menu = document.querySelector("[data-menu]");
const toggle = document.querySelector("[data-menu-toggle]");
const toggleLabel = toggle ? toggle.querySelector(".sr-only") : null;
if (header) {
const syncHeaderState = () => {
header.classList.toggle("is-scrolled", window.scrollY > 12);
};
syncHeaderState();
window.addEventListener("scroll", syncHeaderState, { passive: true });
}
if (!menu || !toggle) return;
const setMenuState = (isOpen) => {
menu.classList.toggle("is-open", isOpen);
document.body.classList.toggle("nav-menu-open", isOpen);
toggle.setAttribute("aria-expanded", String(isOpen));
if (toggleLabel) {
toggleLabel.textContent = isOpen ? "Fechar menu" : "Abrir menu";
}
};
const closeMenu = () => setMenuState(false);
toggle.addEventListener("click", () => {
setMenuState(!menu.classList.contains("is-open"));
});
menu.querySelectorAll("a").forEach((link) => {
link.addEventListener("click", closeMenu);
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
closeMenu();
}
});
}
function setupServiceShortcuts() {
const serviceSelect = document.querySelector("#servico");
document.querySelectorAll("[data-service-shortcut]").forEach((link) => {
link.addEventListener("click", () => {
const service = link.getAttribute("data-service-shortcut");
if (serviceSelect && service) {
serviceSelect.value = service;
}
});
});
}
function setupWhatsappForm() {
const form = document.querySelector("[data-whatsapp-form]");
const status = document.querySelector("[data-form-status]");
if (!form) return;
form.addEventListener("submit", (event) => {
event.preventDefault();
if (!form.checkValidity()) {
form.reportValidity();
return;
}
const data = new FormData(form);
const message = [
"Olá, Adapt IA! Quero solicitar uma proposta.",
"",
`Nome: ${getFormValue(data, "nome")}`,
`Empresa: ${getFormValue(data, "empresa")}`,
`WhatsApp do cliente: ${getFormValue(data, "whatsapp")}`,
`Serviço de interesse: ${getFormValue(data, "servico")}`,
`Faixa de investimento: ${getFormValue(data, "investimento")}`,
`Prazo desejado: ${getFormValue(data, "prazo")}`,
"",
`Mensagem complementar: ${getFormValue(data, "mensagem")}`
].join("\n");
if (status) {
status.textContent = "Abrindo WhatsApp com a mensagem preenchida.";
}
const whatsappUrl = buildWhatsappUrl(message);
const openedWindow = window.open(whatsappUrl, "_blank", "noopener,noreferrer");
if (!openedWindow) {
window.location.href = whatsappUrl;
}
});
}
function setupCapturedWhatsappForm() {
const form = document.querySelector("[data-whatsapp-form]");
const status = document.querySelector("[data-form-status]");
if (!form) return;
form.addEventListener("submit", async (event) => {
event.preventDefault();
if (!form.checkValidity()) {
form.reportValidity();
return;
}
const data = new FormData(form);
const message = [
"Olá, Adapt IA! Quero solicitar uma proposta.",
"",
`Nome: ${getFormValue(data, "nome")}`,
`Empresa: ${getFormValue(data, "empresa")}`,
`WhatsApp do cliente: ${getFormValue(data, "whatsapp")}`,
`Serviço de interesse: ${getFormValue(data, "servico")}`,
`Faixa de investimento: ${getFormValue(data, "investimento")}`,
`Prazo desejado: ${getFormValue(data, "prazo")}`,
"",
`Mensagem complementar: ${getFormValue(data, "mensagem")}`
].join("\n");
const whatsappUrl = buildWhatsappUrl(message);
const openedWindow = window.open("about:blank", "_blank");
if (openedWindow) {
try {
openedWindow.opener = null;
} catch (error) {
// Some browsers block opener changes on new windows.
}
}
if (status) {
status.textContent = "Registrando dados e abrindo WhatsApp.";
}
try {
await sendFormSubmission(data);
if (status) {
status.textContent = "Dados registrados. Abrindo WhatsApp.";
}
} catch (error) {
if (error.status === 422) {
if (openedWindow && !openedWindow.closed) {
openedWindow.close();
}
if (status) {
status.textContent = "Revise nome, WhatsApp e serviço antes de enviar.";
}
return;
}
if (status) {
status.textContent = "Abrindo WhatsApp. O registro local pode estar indisponível.";
}
}
if (openedWindow && !openedWindow.closed) {
openedWindow.location.href = whatsappUrl;
return;
}
window.location.href = whatsappUrl;
});
}
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
const parallaxEasing = {
easeInOutCubic(progress) {
return progress < 0.5 ? 4 * progress ** 3 : 1 - (-2 * progress + 2) ** 3 / 2;
},
easeInOutSine(progress) {
return -(Math.cos(Math.PI * progress) - 1) / 2;
},
smootherstep(progress) {
return progress ** 3 * (progress * (progress * 6 - 15) + 10);
}
};
function parseParallaxDistance(value, fallback) {
const parsed = Number.parseFloat(value);
return Number.isFinite(parsed) ? parsed : fallback;
}
function setupScrollParallax() {
const parallaxRoots = Array.from(document.querySelectorAll("[data-parallax]"));
if (!parallaxRoots.length) return;
const compactQuery = window.matchMedia("(max-width: 980px)");
const reduceMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
const rootMap = new Map();
let frameId = null;
const shouldDisableParallax = () => compactQuery.matches || reduceMotionQuery.matches;
const items = parallaxRoots
.map((root) => {
const layer = root.matches("[data-parallax-layer]") ? root : root.querySelector("[data-parallax-layer]");
if (!layer) return null;
const distance = parseParallaxDistance(root.getAttribute("data-parallax-distance"), 0);
const compactDistance = parseParallaxDistance(root.getAttribute("data-parallax-distance-compact"), distance);
const easeName = root.getAttribute("data-parallax-ease") || "smootherstep";
const mode = root.getAttribute("data-parallax-mode") === "page" ? "page" : "view";
const item = {
root,
layer,
mode,
distance,
compactDistance,
distancePx: 0,
active: true,
ease: parallaxEasing[easeName] || parallaxEasing.smootherstep
};
rootMap.set(root, item);
return item;
})
.filter(Boolean);
if (!items.length) return;
const clearParallax = () => {
items.forEach((item) => {
item.layer.style.setProperty("--parallax-y", "0px");
});
};
const refreshDistances = () => {
if (shouldDisableParallax()) {
items.forEach((item) => {
item.distancePx = 0;
});
return;
}
const isCompact = compactQuery.matches;
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
items.forEach((item) => {
const distanceVh = isCompact ? item.compactDistance : item.distance;
item.distancePx = (distanceVh / 100) * viewportHeight;
});
};
const updateParallax = () => {
frameId = null;
if (shouldDisableParallax()) {
clearParallax();
return;
}
const scrollY = window.scrollY || window.pageYOffset;
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
items.forEach((item) => {
if (!item.active && item.mode !== "page") return;
let progress = 0;
if (item.mode === "page") {
progress = clamp(scrollY / viewportHeight, 0, 1);
} else {
const rect = item.root.getBoundingClientRect();
const travel = viewportHeight + rect.height;
progress = clamp((viewportHeight - rect.top) / travel, 0, 1);
}
const easedProgress = item.ease(progress);
const offset = item.distancePx * easedProgress;
item.layer.style.setProperty("--parallax-y", `${offset.toFixed(2)}px`);
});
};
const scheduleUpdate = () => {
if (frameId !== null) return;
frameId = window.requestAnimationFrame(updateParallax);
};
refreshDistances();
if ("IntersectionObserver" in window) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const item = rootMap.get(entry.target);
if (item) {
item.active = entry.isIntersecting;
}
});
scheduleUpdate();
},
{ rootMargin: "20% 0px" }
);
items.forEach((item) => {
if (item.mode !== "page") {
observer.observe(item.root);
}
});
}
window.addEventListener("scroll", scheduleUpdate, { passive: true });
window.addEventListener(
"resize",
() => {
refreshDistances();
scheduleUpdate();
},
{ passive: true }
);
if (compactQuery.addEventListener) {
compactQuery.addEventListener("change", () => {
refreshDistances();
scheduleUpdate();
});
}
if (reduceMotionQuery.addEventListener) {
reduceMotionQuery.addEventListener("change", () => {
refreshDistances();
scheduleUpdate();
});
}
scheduleUpdate();
}
function setupNodePaths() {
const paths = Array.from(document.querySelectorAll("[data-node-path]"));
if (!paths.length) return;
const compactQuery = window.matchMedia("(max-width: 980px)");
const reduceMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
let frameId = null;
const clearReachedStates = (path) => {
path.querySelectorAll("[data-path-node].is-reached").forEach((node) => {
node.classList.remove("is-reached");
});
path.querySelectorAll("[data-path-stage].is-reached").forEach((stage) => {
stage.classList.remove("is-reached");
});
};
const setNodeReached = (node, reached) => {
const wasReached = node.classList.contains("is-reached");
if (wasReached === reached) return;
node.classList.toggle("is-reached", reached);
const stage = node.closest("[data-path-stage]");
if (stage) {
stage.classList.toggle("is-reached", reached);
}
};
const updatePaths = () => {
frameId = null;
const compact = compactQuery.matches;
const scrollDriven = compact && !reduceMotionQuery.matches;
const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 1;
const triggerY = viewportHeight * 0.72;
paths.forEach((path) => {
const nodes = Array.from(path.querySelectorAll("[data-path-node]"));
if (!nodes.length) return;
const pathRect = path.getBoundingClientRect();
const nodeRects = nodes.map((node) => node.getBoundingClientRect());
path.classList.toggle("is-scroll-driven", scrollDriven);
if (compact) {
const firstCenter = nodeRects[0].top + nodeRects[0].height / 2 - pathRect.top;
const lastRect = nodeRects[nodeRects.length - 1];
const lastCenter = lastRect.top + lastRect.height / 2 - pathRect.top;
const railStart = Math.max(0, firstCenter - 48);
const railEnd = Math.min(pathRect.height, lastCenter + 48);
const railLength = Math.max(1, railEnd - railStart);
const railTopInViewport = pathRect.top + railStart;
const activeLength = clamp(triggerY - railTopInViewport, 0, railLength);
const progress = scrollDriven ? activeLength / railLength : 1;
path.style.setProperty("--rail-start-y", `${railStart.toFixed(2)}px`);
path.style.setProperty("--rail-length", `${railLength.toFixed(2)}px`);
path.style.setProperty("--path-progress", progress.toFixed(4));
if (scrollDriven) {
const activeEnd = railTopInViewport + activeLength;
nodes.forEach((node, index) => {
const rect = nodeRects[index];
const center = rect.top + rect.height / 2;
setNodeReached(node, activeEnd >= center - 0.5);
});
} else {
clearReachedStates(path);
}
return;
}
const firstRect = nodeRects[0];
const lastRect = nodeRects[nodeRects.length - 1];
const firstCenterX = firstRect.left + firstRect.width / 2 - pathRect.left;
const lastCenterX = lastRect.left + lastRect.width / 2 - pathRect.left;
const centerY = firstRect.top + firstRect.height / 2 - pathRect.top;
path.style.setProperty("--rail-start-x", `${firstCenterX.toFixed(2)}px`);
path.style.setProperty("--rail-start-y", `${centerY.toFixed(2)}px`);
path.style.setProperty("--rail-length", `${Math.max(1, lastCenterX - firstCenterX).toFixed(2)}px`);
path.style.setProperty("--path-progress", "1");
clearReachedStates(path);
});
};
const scheduleUpdate = () => {
if (frameId !== null) return;
frameId = window.requestAnimationFrame(updatePaths);
};
window.addEventListener("scroll", scheduleUpdate, { passive: true });
window.addEventListener("resize", scheduleUpdate, { passive: true });
if (compactQuery.addEventListener) {
compactQuery.addEventListener("change", scheduleUpdate);
}
if (reduceMotionQuery.addEventListener) {
reduceMotionQuery.addEventListener("change", scheduleUpdate);
}
scheduleUpdate();
}
function setupSiteTracking() {
const sectionElements = Array.from(document.querySelectorAll("[data-track-section]"));
if (!sectionElements.length) return;
const visitId = getVisitId();
const startedAt = Date.now();
const titleBySectionId = new Map();
const sectionByElement = new Map();
const sectionDurations = new Map();
let activeSection = null;
let activeSince = startedAt;
let maxScrollDepth = 0;
let lastScrollMilestoneIndex = -1;
let clickCount = 0;
let frameId = null;
let flushTimer = null;
let queue = [];
const trackedSections = sectionElements.map((element, index) => {
const id = element.id || element.getAttribute("data-section-id") || `section-${index + 1}`;
const heading = element.querySelector("h1, h2, h3");
const title = element.getAttribute("data-section-title") || (heading ? heading.textContent.trim() : id);
const section = { id, title, element };
titleBySectionId.set(id, title);
sectionByElement.set(element, section);
return section;
});
const elapsed = () => Date.now() - startedAt;
const getSafeHref = (anchor) => {
const rawHref = anchor.getAttribute("href") || "";
if (!rawHref) return "";
if (rawHref.startsWith("#")) return rawHref;
try {
const url = new URL(rawHref, window.location.href);
if (url.origin === window.location.origin) {
return `${url.pathname}${url.hash}`;
}
return `${url.hostname}${url.pathname}`;
} catch (error) {
return "";
}
};
const describeTarget = (element) => {
const trackedTarget =
element.closest("a, button, input, select, textarea, [role='button'], [data-service-shortcut], [data-whatsapp-message]") ||
element;
const tag = trackedTarget.tagName || "";
const isField = ["INPUT", "SELECT", "TEXTAREA"].includes(tag);
const anchor = trackedTarget.closest("a");
const className = typeof trackedTarget.className === "string"
? trackedTarget.className.split(/\s+/).filter(Boolean).slice(0, 3).join(" ")
: "";
const textLabel = isField
? ""
: (trackedTarget.getAttribute("aria-label") || trackedTarget.textContent || "").replace(/\s+/g, " ").trim().slice(0, 100);
return {
tag,
id: trackedTarget.id || "",
classes: className,
role: trackedTarget.getAttribute("role") || "",
name: trackedTarget.getAttribute("name") || "",
type: trackedTarget.getAttribute("type") || "",
label: textLabel,
href: anchor ? getSafeHref(anchor) : "",
link_host: anchor ? (() => {
try {
return new URL(anchor.href, window.location.href).hostname;
} catch (error) {
return "";
}
})() : ""
};
};
const sectionForElement = (element) => {
const sectionElement = element.closest("[data-track-section]");
return sectionElement ? sectionByElement.get(sectionElement) || null : null;
};
const getCurrentSection = () => {
const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 1;
let bestSection = trackedSections[0];
let bestScore = -Infinity;
trackedSections.forEach((section) => {
const rect = section.element.getBoundingClientRect();
const visibleHeight = Math.max(0, Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0));
if (visibleHeight <= 0) return;
const centerDistance = Math.abs(rect.top + rect.height / 2 - viewportHeight / 2);
const score = visibleHeight * 1000 - centerDistance;
if (score > bestScore) {
bestScore = score;
bestSection = section;
}
});
return bestSection;
};
const sectionSnapshot = (now = Date.now()) => {
const snapshot = new Map(sectionDurations);
if (activeSection) {
snapshot.set(activeSection.id, (snapshot.get(activeSection.id) || 0) + Math.max(0, now - activeSince));
}
return Array.from(snapshot.entries()).map(([id, duration]) => ({
id,
title: titleBySectionId.get(id) || id,
duration_ms: Math.round(duration)
}));
};
const buildPayload = (events) => ({
visit_id: visitId,
page_url: window.location.href,
referrer: document.referrer || "",
viewport: getViewportData(),
events
});
const flushTrackingQueue = (useBeacon = false) => {
if (!queue.length) return;
if (flushTimer !== null) {
window.clearTimeout(flushTimer);
flushTimer = null;
}
const events = queue.splice(0, queue.length);
const body = JSON.stringify(buildPayload(events));
if (useBeacon && "sendBeacon" in navigator) {
const sent = navigator.sendBeacon(ADAPT_TRACK_ENDPOINT, new Blob([body], { type: "application/json" }));
if (sent) return;
}
fetch(ADAPT_TRACK_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
keepalive: useBeacon
}).catch(() => {});
};
const scheduleFlush = () => {
if (flushTimer !== null) return;
flushTimer = window.setTimeout(() => flushTrackingQueue(false), 5000);
};
const queueTrackingEvent = (event) => {
queue.push({ ...event, t: Math.round(elapsed()) });
if (queue.length >= 12 || event.type === "click") {
flushTrackingQueue(false);
return;
}
scheduleFlush();
};
const setActiveSection = (nextSection) => {
const now = Date.now();
if (!nextSection) return;
if (!activeSection) {
activeSection = nextSection;
activeSince = now;
queueTrackingEvent({ type: "section_enter", section_id: nextSection.id, section_title: nextSection.title });
return;
}
if (activeSection.id === nextSection.id) return;
const duration = Math.max(0, now - activeSince);
sectionDurations.set(activeSection.id, (sectionDurations.get(activeSection.id) || 0) + duration);
queueTrackingEvent({
type: "section_leave",
section_id: activeSection.id,
section_title: activeSection.title,
duration_ms: Math.round(duration)
});
activeSection = nextSection;
activeSince = now;
queueTrackingEvent({ type: "section_enter", section_id: nextSection.id, section_title: nextSection.title });
};
const updateScrollDepth = () => {
const documentHeight = Math.max(
document.documentElement.scrollHeight,
document.body.scrollHeight,
window.innerHeight || 0
);
const depth = clamp(((window.scrollY + (window.innerHeight || 0)) / documentHeight) * 100, 0, 100);
if (depth > maxScrollDepth) {
maxScrollDepth = Math.round(depth);
}
const milestones = [25, 50, 75, 90, 100];
let milestoneIndex = -1;
milestones.forEach((milestone, index) => {
if (maxScrollDepth >= milestone) {
milestoneIndex = index;
}
});
if (milestoneIndex > lastScrollMilestoneIndex) {
lastScrollMilestoneIndex = milestoneIndex;
queueTrackingEvent({ type: "scroll_depth", max_scroll_depth: maxScrollDepth });
}
};
const updateActiveSection = () => {
frameId = null;
updateScrollDepth();
setActiveSection(getCurrentSection());
};
const scheduleActiveSectionUpdate = () => {
if (frameId !== null) return;
frameId = window.requestAnimationFrame(updateActiveSection);
};
const queueSessionState = (type = "session_ping") => {
queueTrackingEvent({
type,
duration_ms: Math.round(elapsed()),
max_scroll_depth: maxScrollDepth,
click_count: clickCount,
sections: sectionSnapshot()
});
};
queueTrackingEvent({
type: "session_start",
section_id: trackedSections[0].id,
section_title: trackedSections[0].title,
viewport: getViewportData()
});
scheduleActiveSectionUpdate();
document.addEventListener(
"click",
(event) => {
const target = event.target instanceof Element ? event.target : null;
if (!target) return;
const section = sectionForElement(target) || activeSection || getCurrentSection();
clickCount += 1;
queueTrackingEvent({
type: "click",
section_id: section.id,
section_title: section.title,
x: Math.round(event.clientX),
y: Math.round(event.clientY),
viewport: getViewportData(),
target: describeTarget(target)
});
},
{ capture: true }
);
window.addEventListener("scroll", scheduleActiveSectionUpdate, { passive: true });
window.addEventListener("resize", scheduleActiveSectionUpdate, { passive: true });
window.setInterval(() => {
queueSessionState("session_ping");
flushTrackingQueue(false);
}, 15000);
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
queueSessionState("session_ping");
flushTrackingQueue(true);
}
});
window.addEventListener("pagehide", () => {
queueSessionState("session_end");
flushTrackingQueue(true);
});
}
document.addEventListener("DOMContentLoaded", () => {
updateWhatsappLinks();
setupHeader();
setupServiceShortcuts();
setupCapturedWhatsappForm();
setupNodePaths();
setupScrollParallax();
setupSiteTracking();
});