/** * SWDG Remote Popup v1 (STABLE) * - Motor único (popup.js) + um JSON por cliente. */ (function () { "use strict"; // ========= Utils ========= const $ = (sel, root = document) => root.querySelector(sel); const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel)); // Modo de teste temporário por URL: ?teste=true // Afeta somente o roteamento de e-mail do webhook; o restante do lead segue normal. function swdgIsUrlTestMode() { try { const value = String(new URLSearchParams(window.location.search).get("teste") || "").trim().toLowerCase(); return value === "true" || value === "1"; } catch (_) { return false; } } let SWDG_FLOW_HISTORY = []; let SWDG_FLOW_DATA = {}; let SWDG_FLOW_ORIGINAL_FORM_HTML = ""; let SWDG_FLOW_CURRENT_STEP = ""; // ========= Continuidade curta do Flow/Chat ========= // Guarda somente progresso e respostas por 30 minutos na sessão do navegador. // Não cria histórico no servidor e é limpo após um lead comercial enviado. const SWDG_FLOW_SESSION_TTL_MS = 30 * 60 * 1000; function swdgFlowSessionKey(kind = "flow") { const cid = (typeof clientId !== "undefined" && clientId) ? String(clientId) : "default"; return `swdg_${kind}_progress_${cid}_${String(location.pathname || "/")}`; } function swdgFlowLoadSession(kind = "flow") { try { const raw = sessionStorage.getItem(swdgFlowSessionKey(kind)); if (!raw) return null; const data = JSON.parse(raw); if (!data || typeof data !== "object" || !data.savedAt) return null; if ((Date.now() - Number(data.savedAt)) > SWDG_FLOW_SESSION_TTL_MS) { sessionStorage.removeItem(swdgFlowSessionKey(kind)); return null; } return data; } catch (_) { return null; } } function swdgFlowSaveSession(currentStep = "", extra = null, kind = "flow") { try { const prev = swdgFlowLoadSession(kind) || {}; const payload = { ...prev, ...(extra && typeof extra === "object" ? extra : {}), savedAt: Date.now() }; if (kind === "flow") { payload.currentStep = currentStep || SWDG_FLOW_CURRENT_STEP || prev.currentStep || ""; payload.history = Array.isArray(SWDG_FLOW_HISTORY) ? SWDG_FLOW_HISTORY.slice(-20) : []; payload.answers = (SWDG_FLOW_DATA && typeof SWDG_FLOW_DATA === "object") ? { ...SWDG_FLOW_DATA } : {}; } sessionStorage.setItem(swdgFlowSessionKey(kind), JSON.stringify(payload)); } catch (_) { } } function swdgFlowClearSession(kind = "flow") { try { sessionStorage.removeItem(swdgFlowSessionKey(kind)); } catch (_) { } } function swdgTrackFunnel(stage, mode) { try { const eventName = String(stage || "").trim(); if (!eventName) return; const dedupeKey = [eventName, mode || SWDG_OPEN_MODE || "default", String(location.pathname || "")].join("|"); window.__SWDG_FUNNEL_CACHE__ = window.__SWDG_FUNNEL_CACHE__ || {}; const now = Date.now(); const last = Number(window.__SWDG_FUNNEL_CACHE__[dedupeKey] || 0); if (last && (now - last) < 3000) return; window.__SWDG_FUNNEL_CACHE__[dedupeKey] = now; window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: eventName, clientId: (typeof clientId !== "undefined" ? clientId : ""), popup_mode: mode || SWDG_OPEN_MODE || "default", page_url: location.href }); } catch (_) { } } // ========= Contexto opcional de bolsas e descontos ========= // Nesta versão o contexto é usado somente na mensagem do WhatsApp. // Não é acrescentado ao formulário, GTM, webhook ou payload do n8n. const SWDG_BENEFIT_CONTEXT_KEY = "swdg_popup_benefit_context"; let SWDG_BENEFIT_CONTEXT = null; function swdgBenefitSafeText(value, maxLength = 140) { return String(value || "").replace(/[\r\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim().slice(0, maxLength); } function swdgClearBenefitContext() { SWDG_BENEFIT_CONTEXT = null; try { sessionStorage.removeItem(SWDG_BENEFIT_CONTEXT_KEY); } catch (_) { } try { window.SWDG_SELECTED_BENEFIT = null; } catch (_) { } } function swdgCaptureBenefitFromTrigger(trigger) { const benefitId = swdgBenefitSafeText(trigger && trigger.getAttribute ? trigger.getAttribute("data-swdg-benefit") : "", 80); if (!benefitId) { swdgClearBenefitContext(); return null; } const benefitLabel = swdgBenefitSafeText( (trigger && trigger.getAttribute && trigger.getAttribute("data-swdg-benefit-label")) || benefitId, 140 ); const benefitOrigin = swdgBenefitSafeText( (trigger && trigger.getAttribute && trigger.getAttribute("data-origin")) || "bolsas-e-descontos", 100 ); const context = { interesse: "bolsas_e_descontos", beneficio_id: benefitId, beneficio_nome: benefitLabel, origem_secao: benefitOrigin, selecionado_em: swdgNowIso() }; SWDG_BENEFIT_CONTEXT = context; try { sessionStorage.setItem(SWDG_BENEFIT_CONTEXT_KEY, JSON.stringify(context)); } catch (_) { } try { window.SWDG_SELECTED_BENEFIT = { id: benefitId, label: benefitLabel, origin: benefitOrigin, selectedAt: context.selecionado_em }; } catch (_) { } return context; } function swdgBuildConversationalWhatsMessage(nome, whats, curso) { const finalName = swdgBenefitSafeText(nome, 120); const finalWhats = swdgBenefitSafeText(whats, 40); const finalCourse = swdgBenefitSafeText(curso, 180); const courseInterest = finalCourse ? ` e tenho interesse no curso de ${finalCourse}` : ""; if (SWDG_BENEFIT_CONTEXT && SWDG_BENEFIT_CONTEXT.beneficio_id === "geral") { return `Olá! Meu nome é ${finalName}${courseInterest}. Gostaria de conhecer as opções de bolsas e descontos disponíveis para o meu caso. Meu WhatsApp para contato é ${finalWhats}.`; } if (SWDG_BENEFIT_CONTEXT && SWDG_BENEFIT_CONTEXT.beneficio_nome) { const benefitName = swdgBenefitSafeText(SWDG_BENEFIT_CONTEXT.beneficio_nome, 140); return `Olá! Meu nome é ${finalName}${courseInterest}. Vi a opção “${benefitName}” e gostaria de saber quais condições estão disponíveis para o meu caso. Meu WhatsApp para contato é ${finalWhats}.`; } const normalCourse = finalCourse ? ` sobre o curso de ${finalCourse}` : ""; return `Olá! Meu nome é ${finalName} e gostaria de receber mais informações${normalCourse}. Meu WhatsApp para contato é ${finalWhats}.`; } // Fonte única da mensagem usada pelo botão tradicional e pelo QR Code. // Centralizar aqui evita que os dois caminhos sejam alterados de forma diferente. function swdgBuildWhatsMessageFromLead(data) { const lead = (data && typeof data === "object") ? data : {}; const answers = (lead.flow_answers && typeof lead.flow_answers === "object") ? lead.flow_answers : {}; const premium = lead.__commercial_qualification === true ? swdgBuildCommercialQualification(lead, answers) : null; if (premium && premium.commercial && premium.whatsapp) return premium.whatsapp; return swdgBuildConversationalWhatsMessage( lead.nome || "", lead.whats || lead.whats_formatado || "", lead.curso || "" ); } // API somente de leitura para a futura integração controlada com o n8n. // ========= Qualificação comercial premium (Flow / Chat) ========= // Mantém os dados enxutos: gera somente a mensagem do WhatsApp e a Observação. function swdgQualNorm(v) { try { return String(v == null ? "" : v).normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim(); } catch (_) { return String(v == null ? "" : v).toLowerCase().trim(); } } function swdgQualFindValue(answers, patterns) { const src = (answers && typeof answers === "object") ? answers : {}; const pats = Array.isArray(patterns) ? patterns : [patterns]; for (const [key, value] of Object.entries(src)) { const nk = swdgQualNorm(key); if (pats.some(p => nk.includes(swdgQualNorm(p)))) return String(value == null ? "" : value).trim(); } return ""; } function swdgResolveLeadCourse(data) { const direct = swdgBenefitSafeText(data && data.curso ? data.curso : "", 180); if (direct && !direct.includes("────")) return direct; try { const el = document.querySelector("#swdg_curso"); const v = swdgBenefitSafeText(el && el.value ? el.value : "", 180); if (v && !v.includes("────")) return v; } catch (_) {} try { const u = new URL(location.href); const keys = ["curso_nome", "cursolista", "curso", "cursointeresse"]; for (const k of keys) { const v = swdgBenefitSafeText(u.searchParams.get(k) || "", 180); if (v) return v.replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim(); } } catch (_) {} return ""; } function swdgBuildCommercialQualification(data, answers) { const lead = (data && typeof data === "object") ? data : {}; const a = (answers && typeof answers === "object") ? answers : {}; const nome = swdgBenefitSafeText(lead.nome || a.nome || "", 120); const curso = swdgResolveLeadCourse({ ...lead, curso: lead.curso || a.curso || a.curso_nome || "" }); const alunoRaw = swdgQualFindValue(a, ["ja_e_aluno", "ja e aluno", "aluno_atual", "aluno"]); const alunoNorm = swdgQualNorm(alunoRaw); const isStudent = alunoNorm === "sim" || alunoNorm.startsWith("sim ") || alunoNorm.includes("ja sou aluno") || alunoNorm.includes("já sou aluno"); const isNotStudent = alunoNorm === "nao" || alunoNorm === "não" || alunoNorm.startsWith("nao ") || alunoNorm.startsWith("não ") || alunoNorm.includes("ainda nao sou aluno") || alunoNorm.includes("ainda não sou aluno"); const modRaw = swdgQualFindValue(a, ["modalidade", "ead", "semipresencial"]); const modNorm = swdgQualNorm(modRaw); let modalidade = ""; const hasEad = modNorm.includes("ead") || modNorm.includes("digital") || modNorm.includes("online"); const hasSemi = modNorm.includes("semi") || modNorm.includes("hibrid") || modNorm.includes("híbr" ); if (hasEad && hasSemi) modalidade = "EAD/Semipresencial"; else if (hasEad) modalidade = "EAD"; else if (hasSemi) modalidade = "Semipresencial"; else if (modNorm === "sim" || modNorm.includes("verificado somente ead e semi") || modNorm.includes("e o que eu quero") || modNorm.includes("é o que eu quero") || modNorm.includes("quero prosseguir") || modNorm.includes("estou procurando")) modalidade = "EAD/Semipresencial"; // Aluno atual não é lead comercial: não gera observação nem copy comercial. if (isStudent) return { commercial: false, isStudent: true, nome, curso, modalidade, observacao: "", whatsapp: "" }; const parts = []; // O curso já possui campo próprio no lead/dashboard. Evitamos duplicá-lo na Observação. if (modalidade) parts.push(modalidade); if (isNotStudent) parts.push("Não é aluno"); parts.push("Interesse comercial confirmado"); const observacao = parts.join(" | "); let whatsapp = ""; if (nome) whatsapp = `Olá! Meu nome é ${nome}.`; else whatsapp = "Olá!"; if (curso && modalidade === "EAD/Semipresencial") { whatsapp += ` Tenho interesse no curso de ${curso} e gostaria de conhecer melhor as opções EAD e Semipresencial.`; } else if (curso && modalidade) { whatsapp += ` Tenho interesse no curso de ${curso} na modalidade ${modalidade}.`; } else if (curso) { whatsapp += ` Tenho interesse no curso de ${curso} e gostaria de receber mais informações.`; } else if (modalidade === "EAD/Semipresencial") { whatsapp += " Gostaria de conhecer melhor as opções de cursos EAD e Semipresenciais."; } else if (modalidade) { whatsapp += ` Gostaria de conhecer melhor as opções de cursos na modalidade ${modalidade}.`; } else { whatsapp += " Gostaria de conhecer melhor as opções de cursos disponíveis."; } if (isNotStudent) whatsapp += " Ainda não sou aluno da instituição"; if (curso) whatsapp += " e gostaria de receber informações sobre o curso."; else whatsapp += " e gostaria de receber mais informações."; return { commercial: true, isStudent: false, nome, curso, modalidade, observacao, whatsapp }; } window.SWDGBenefitContext = window.SWDGBenefitContext || {}; window.SWDGBenefitContext.getCurrent = function () { return SWDG_BENEFIT_CONTEXT ? { ...SWDG_BENEFIT_CONTEXT } : null; }; window.SWDGBenefitContext.clear = swdgClearBenefitContext; // ========= Retorno de visitante (Nível 1) ========= const SWDG_VISITOR_KEY = "swdg_vid"; const SWDG_VISITOR_META_KEY = "swdg_vid_meta"; const SWDG_VISITOR_NAME_KEY = "swdg_known_name"; const SWDG_LEAD_DATA_KEY = "swdg_known_lead"; const SWDG_VISITOR_WINDOW_DAYS = 90; function swdgNowIso() { try { return new Date().toISOString(); } catch (_) { return ""; } } function swdgCreateVisitorId() { return "vid_" + Date.now() + "_" + Math.random().toString(36).slice(2, 10); } function swdgReadStorage(key) { try { return localStorage.getItem(key); } catch (_) { return null; } } function swdgWriteStorage(key, value) { try { localStorage.setItem(key, value); } catch (_) { } } function swdgReadVisitorMeta() { try { const raw = swdgReadStorage(SWDG_VISITOR_META_KEY); if (!raw) return null; const parsed = JSON.parse(raw); return (parsed && typeof parsed === "object") ? parsed : null; } catch (_) { return null; } } function swdgPersistKnownName(name) { const finalName = sanitizeNameFinal ? sanitizeNameFinal(name) : String(name || "").trim(); if (!finalName) return; swdgWriteStorage(SWDG_VISITOR_NAME_KEY, finalName); try { if (window.SWDG_VISITOR && typeof window.SWDG_VISITOR === "object") { window.SWDG_VISITOR.knownName = finalName; } } catch (_) { } } function swdgReadKnownLead() { try { const raw = swdgReadStorage(SWDG_LEAD_DATA_KEY); if (!raw) return null; const parsed = JSON.parse(raw); return (parsed && typeof parsed === "object") ? parsed : null; } catch (_) { return null; } } function swdgNormalizeKnownWhats(value) { return String(value || "").replace(/\D+/g, ""); } function swdgPersistKnownLead(name, whats) { try { const finalName = sanitizeNameFinal ? sanitizeNameFinal(name) : String(name || "").trim(); const finalWhats = swdgNormalizeKnownWhats(whats); if (!finalWhats) return null; const current = swdgReadKnownLead() || null; const sameLead = !!(current && String(current.whats || "") === finalWhats); const lead = { name: finalName || String((current && current.name) || "").trim() || "", whats: finalWhats, contactCount: sameLead ? Math.max(1, Number(current.contactCount || 0) + 1) : 1, firstContactAt: sameLead ? (current.firstContactAt || swdgNowIso()) : swdgNowIso(), lastContactAt: swdgNowIso() }; swdgWriteStorage(SWDG_LEAD_DATA_KEY, JSON.stringify(lead)); try { if (window.SWDG_VISITOR && typeof window.SWDG_VISITOR === "object") { window.SWDG_VISITOR.knownLeadName = lead.name || null; window.SWDG_VISITOR.knownLeadWhats = lead.whats || null; window.SWDG_VISITOR.leadContactCount = lead.contactCount || 0; } } catch (_) { } return lead; } catch (_) { return null; } } function swdgGetKnownLeadData() { const lead = swdgReadKnownLead(); return { known_name: String((lead && lead.name) || "").trim(), known_whats: String((lead && lead.whats) || "").trim(), lead_contact_count: Math.max(0, Number(lead && lead.contactCount ? lead.contactCount : 0)) }; } function swdgInitVisitorTracker() { let vid = swdgReadStorage(SWDG_VISITOR_KEY); let meta = swdgReadVisitorMeta(); const now = new Date(); const nowIso = swdgNowIso(); if (!vid) { vid = swdgCreateVisitorId(); swdgWriteStorage(SWDG_VISITOR_KEY, vid); } const hadSeenBefore = !!(meta && meta.lastSeenAt); const lastSeenMs = hadSeenBefore ? Date.parse(meta.lastSeenAt) : NaN; const windowMs = SWDG_VISITOR_WINDOW_DAYS * 24 * 60 * 60 * 1000; const withinWindow = hadSeenBefore && Number.isFinite(lastSeenMs) && ((now.getTime() - lastSeenMs) <= windowMs); const visitCount = Math.max(1, Number(meta && meta.visitCount ? meta.visitCount : 0) + (hadSeenBefore ? 1 : 0)); const firstSeenAt = (meta && meta.firstSeenAt) ? meta.firstSeenAt : nowIso; const nextMeta = { firstSeenAt, lastSeenAt: nowIso, visitCount }; try { swdgWriteStorage(SWDG_VISITOR_META_KEY, JSON.stringify(nextMeta)); } catch (_) { } const knownName = String(swdgReadStorage(SWDG_VISITOR_NAME_KEY) || "").trim() || null; const knownLead = swdgGetKnownLeadData(); window.SWDG_VISITOR = { vid, isReturning: hadSeenBefore, withinWindow, visitCount, firstSeenAt, lastSeenAt: nowIso, knownName, knownLeadName: knownLead.known_name || null, knownLeadWhats: knownLead.known_whats || null, leadContactCount: knownLead.lead_contact_count || 0 }; } function swdgVisitorShouldShowWelcome() { try { return !!(window.SWDG_VISITOR && window.SWDG_VISITOR.isReturning === true && window.SWDG_VISITOR.withinWindow === true); } catch (_) { return false; } } function swdgGetReturningWelcomeText(fallback) { try { if (!(window.SWDG_VISITOR && window.SWDG_VISITOR.isReturning === true && window.SWDG_VISITOR.withinWindow === true)) { return String(fallback || ""); } const visitCount = Number(window.SWDG_VISITOR.visitCount || 0); if (visitCount >= 4) return "Ainda com dúvidas? Posso te ajudar por aqui 😊"; if (visitCount === 3) return "Vamos continuar seu atendimento?"; if (visitCount === 2) return "Que bom ter você de volta 😊"; return "Que bom ter você de volta 😊"; } catch (_) { return String(fallback || ""); } } function swdgGetReturningContinueText(fallback) { return swdgVisitorShouldShowWelcome() ? "Que bom ter você de volta 😊 Vamos continuar?" : String(fallback || ""); } swdgInitVisitorTracker(); // ========= Randomização (JSON: string OU array) ========= // Permite configurar no JSON: "brandName": "X" OU ["X","Y"], e idem para brandSub/title. function swdgToArray(v) { if (v == null) return []; return Array.isArray(v) ? v.filter((x) => x != null && String(x).trim() !== "") : [v]; } function swdgPickOne(v) { const arr = swdgToArray(v).map((x) => String(x)); if (!arr.length) return ""; return arr[Math.floor(Math.random() * arr.length)]; } // ========= Business Hours (JSON opcional) ========= function swdgParseHHMM(s) { const m = String(s || "").trim().match(/^(\d{1,2}):(\d{2})$/); if (!m) return null; const hh = Math.max(0, Math.min(23, parseInt(m[1], 10))); const mm = Math.max(0, Math.min(59, parseInt(m[2], 10))); return hh * 60 + mm; } function swdgGetBusinessContext(cfg) { // Retorna { mode: "open"|"weekend"|"night"|"closed", message, buttonText } const bh = cfg && cfg.businessHours ? cfg.businessHours : null; if (!bh || bh.enabled !== true) return { mode: "open" }; const now = new Date(); const day = now.getDay(); // 0 dom ... 6 sab const isWeekend = (day === 0 || day === 6); const startMin = swdgParseHHMM(bh.start || "08:00"); const endMin = swdgParseHHMM(bh.end || "18:00"); const nowMin = now.getHours() * 60 + now.getMinutes(); let isOpen = true; if (startMin != null && endMin != null) { // janela no mesmo dia (ex.: 08:00-18:00) isOpen = (nowMin >= startMin && nowMin <= endMin); } if (isWeekend) { return { mode: "weekend", message: bh.weekendMessage || bh.closedMessage || "", closedMessage: bh.closedMessage || "", buttonText: bh.closedButton || "" }; } if (!isOpen) { // Fora do horário em dias úteis: "nightMessage" (ou closedMessage) return { mode: "night", message: bh.nightMessage || bh.closedMessage || "", closedMessage: bh.closedMessage || "", buttonText: bh.closedButton || "" }; } return { mode: "open" }; } // ========= Theme (JSON opcional) ========= // Ex.: "theme": { "primary":"#0c2c46", "secondary":"#ffd400", "button":"#1fd25c", "buttonHover":"#12a243", "pulse": true } function swdgIsHexColor(v) { const s = String(v || "").trim(); return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(s); } function swdgHexToRgb(hex) { const h = String(hex || "").trim().replace("#", ""); const full = (h.length === 3) ? (h[0] + h[0] + h[1] + h[1] + h[2] + h[2]) : h; if (!/^[0-9a-fA-F]{6}$/.test(full)) return null; const r = parseInt(full.slice(0, 2), 16); const g = parseInt(full.slice(2, 4), 16); const b = parseInt(full.slice(4, 6), 16); return { r, g, b }; } function swdgDarkenHex(hex, amount) { const rgb = swdgHexToRgb(hex); if (!rgb) return ""; const clamp = (n) => Math.max(0, Math.min(255, n)); const r = clamp(Math.round(rgb.r * (1 - amount))); const g = clamp(Math.round(rgb.g * (1 - amount))); const b = clamp(Math.round(rgb.b * (1 - amount))); return "#" + [r, g, b].map(x => x.toString(16).padStart(2, "0")).join(""); } function swdgRgbToHex(rgb) { if (!rgb) return ""; const clamp = (n) => Math.max(0, Math.min(255, Math.round(n))); return "#" + [clamp(rgb.r), clamp(rgb.g), clamp(rgb.b)].map(x => x.toString(16).padStart(2, "0")).join(""); } function swdgMixHex(base, target, targetWeight) { const a = swdgHexToRgb(base); const b = swdgHexToRgb(target); if (!a || !b) return swdgIsHexColor(base) ? base : (swdgIsHexColor(target) ? target : ""); const w = Math.max(0, Math.min(1, Number(targetWeight) || 0)); return swdgRgbToHex({ r: a.r + (b.r - a.r) * w, g: a.g + (b.g - a.g) * w, b: a.b + (b.b - a.b) * w, }); } function swdgRelativeLuminance(hex) { const rgb = swdgHexToRgb(hex); if (!rgb) return 0; const channel = (value) => { const s = value / 255; return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4); }; return 0.2126 * channel(rgb.r) + 0.7152 * channel(rgb.g) + 0.0722 * channel(rgb.b); } function swdgContrastRatio(a, b) { const l1 = swdgRelativeLuminance(a); const l2 = swdgRelativeLuminance(b); return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05); } function swdgReadableText(background, preferred) { const dark = "#111827"; const light = "#ffffff"; if (swdgIsHexColor(preferred) && swdgContrastRatio(background, preferred) >= 4.5) return preferred; return swdgContrastRatio(background, dark) >= swdgContrastRatio(background, light) ? dark : light; } function swdgBuildThemeBackground(mode, c1, c2, angle, split) { const color1 = swdgIsHexColor(c1) ? c1 : "#062033"; const color2 = swdgIsHexColor(c2) ? c2 : swdgDarkenHex(color1, 0.22) || "#072235"; const a = Number.isFinite(+angle) ? +angle : 135; const s = Number.isFinite(+split) ? Math.max(0, Math.min(90, +split)) : 36; const m = String(mode || "").toLowerCase(); if (m === "radial") { return `radial-gradient(circle at 18% 10%, ${color1} 0%, ${color1} ${s}%, ${color2} 100%)`; } if (m === "solid") { return color1; } return `linear-gradient(${a}deg, ${color1} 0%, ${color1} ${s}%, ${color2} 100%)`; } function swdgBuildTopBar(mode, c1, c2) { const color1 = swdgIsHexColor(c1) ? c1 : "#ffd400"; const color2 = swdgIsHexColor(c2) ? c2 : "#fff2a8"; const m = String(mode || "").toLowerCase(); if (m === "solid") return color1; if (m === "none" || m === "off" || m === "disabled") return "transparent"; return `linear-gradient(90deg, rgba(255,255,255,0), ${color1}, ${color2}, rgba(255,255,255,0))`; } function swdgApplyTheme(root, cfg) { const t = (cfg && cfg.theme) ? cfg.theme : {}; const visualStyle = String((cfg && cfg.visualStyle) || "premium").toLowerCase() === "clean" ? "clean" : "premium"; root.setAttribute("data-swdg-visual", visualStyle); if (visualStyle === "clean") { const clean = (cfg && cfg.cleanTheme && typeof cfg.cleanTheme === "object") ? cfg.cleanTheme : {}; const background = swdgIsHexColor(clean.background) ? clean.background : "#f5f7fb"; const accent = swdgIsHexColor(clean.accent) ? clean.accent : "#2563eb"; const button = swdgIsHexColor(clean.button) ? clean.button : accent; const textMode = String(clean.textMode || "auto").toLowerCase(); const requestedText = textMode === "manual" && swdgIsHexColor(clean.text) ? clean.text : ""; const text = swdgReadableText(background, requestedText); const darkBackground = swdgRelativeLuminance(background) < 0.32; const surface = swdgMixHex(background, "#ffffff", darkBackground ? 0.075 : 0.72); const inputBg = swdgMixHex(background, "#ffffff", darkBackground ? 0.12 : 0.88); const inputText = swdgReadableText(inputBg, text); const mutedTextCandidate = swdgMixHex(text, background, 0.24); const mutedText = swdgContrastRatio(background, mutedTextCandidate) >= 3.2 ? mutedTextCandidate : text; const border = swdgMixHex(background, accent, darkBackground ? 0.58 : 0.34); const buttonText = swdgReadableText(button, ""); const buttonHover = swdgMixHex(button, swdgRelativeLuminance(button) > 0.42 ? "#000000" : "#ffffff", 0.11); const placeholder = swdgMixHex(inputText, inputBg, 0.46); root.style.colorScheme = darkBackground ? "dark" : "light"; root.style.setProperty("--swdg-primary", background); root.style.setProperty("--swdg-primaryDark", background); root.style.setProperty("--swdg-primaryDarker", background); root.style.setProperty("--swdg-secondary", accent); root.style.setProperty("--swdg-textOnPrimary", text); root.style.setProperty("--swdg-textAccent", accent); root.style.setProperty("--swdg-inputBorderColor", border); root.style.setProperty("--swdg-button", button); root.style.setProperty("--swdg-buttonHover", buttonHover); root.style.setProperty("--swdg-buttonText", buttonText); root.style.setProperty("--swdg-bg", background); root.style.setProperty("--swdg-topbar", accent); root.style.setProperty("--swdg-clean-bg", background); root.style.setProperty("--swdg-clean-surface", surface); root.style.setProperty("--swdg-clean-text", text); root.style.setProperty("--swdg-clean-muted", mutedText); root.style.setProperty("--swdg-clean-input-bg", inputBg); root.style.setProperty("--swdg-clean-input-text", inputText); root.style.setProperty("--swdg-clean-placeholder", placeholder); root.style.setProperty("--swdg-clean-border", border); root.setAttribute("data-swdg-pulse", "0"); root.setAttribute("data-swdg-glow", "0"); root.setAttribute("data-swdg-whats-icon", t.whatsIcon === false ? "0" : "1"); return; } root.style.colorScheme = "light"; const primary = swdgIsHexColor(t.primary) ? t.primary : "#0c2c46"; const textPrimary = swdgIsHexColor(t.textPrimary) ? t.textPrimary : (swdgIsHexColor(t.textOnPrimary) ? t.textOnPrimary : "#ffffff"); const textAccent = swdgIsHexColor(t.textAccent) ? t.textAccent : (swdgIsHexColor(t.secondary) ? t.secondary : "#ffd400"); const button = swdgIsHexColor(t.button) ? t.button : "#1fd25c"; const bgColor1 = swdgIsHexColor(t.bgColor1) ? t.bgColor1 : primary; const bgColor2 = swdgIsHexColor(t.bgColor2) ? t.bgColor2 : (swdgIsHexColor(t.secondary) ? swdgDarkenHex(primary, 0.18) || primary : swdgDarkenHex(primary, 0.18) || primary); const topBarColor1 = swdgIsHexColor(t.topBarColor1) ? t.topBarColor1 : textAccent; const topBarColor2 = swdgIsHexColor(t.topBarColor2) ? t.topBarColor2 : "#fff2a8"; const inputBorderColor = swdgIsHexColor(t.inputBorderColor) ? t.inputBorderColor : textAccent; root.style.setProperty("--swdg-primary", primary); root.style.setProperty("--swdg-primaryDark", swdgDarkenHex(primary, 0.20) || primary); root.style.setProperty("--swdg-primaryDarker", swdgDarkenHex(primary, 0.35) || primary); root.style.setProperty("--swdg-secondary", textAccent); root.style.setProperty("--swdg-button", button); if (swdgIsHexColor(t.buttonHover)) root.style.setProperty("--swdg-buttonHover", t.buttonHover); root.style.setProperty("--swdg-textOnPrimary", textPrimary); root.style.setProperty("--swdg-textAccent", textAccent); root.style.setProperty("--swdg-inputBorderColor", inputBorderColor); root.style.setProperty("--swdg-bg", swdgBuildThemeBackground(t.bgMode, bgColor1, bgColor2, parseInt(t.bgAngle || 135, 10), parseInt(t.bgSplit ?? 36, 10))); root.style.setProperty("--swdg-topbar", swdgBuildTopBar(t.topBarMode, topBarColor1, topBarColor2)); const pulseRgb = swdgHexToRgb(button); if (pulseRgb) root.style.setProperty("--swdg-pulseRgb", `${pulseRgb.r}, ${pulseRgb.g}, ${pulseRgb.b}`); const pulse = (t.pulse === true); root.setAttribute("data-swdg-pulse", pulse ? "1" : "0"); root.setAttribute("data-swdg-glow", t.cardGlow === false ? "0" : "1"); root.setAttribute("data-swdg-whats-icon", t.whatsIcon === false ? "0" : "1"); } // ========= IP (public) ========= // Captura IP público via ipify (frontend não tem acesso direto ao IP real). // - Prefetch em background (não atrasa Whats/GTM) // - Cache em memória para reutilizar nas próximas aberturas/envios let SWDG_CACHED_IP = ""; let SWDG_IP_PROMISE = null; function swdgPrefetchIP() { if (SWDG_CACHED_IP) return Promise.resolve(SWDG_CACHED_IP); if (SWDG_IP_PROMISE) return SWDG_IP_PROMISE; SWDG_IP_PROMISE = fetch("https://api.ipify.org?format=json", { cache: "no-store", credentials: "omit" }) .then((r) => (r && r.ok ? r.json() : null)) .then((j) => (j && j.ip ? String(j.ip) : "")) .catch(() => "") .then((ip) => { SWDG_CACHED_IP = ip || ""; return SWDG_CACHED_IP; }) .finally(() => { // mantém promise para evitar múltiplas chamadas em curto intervalo // (não reseta aqui) }); return SWDG_IP_PROMISE; } // ========= GEO/DDD (sugestão opcional) ========= // IMPORTANTE: nunca força nada. Apenas sugere e o usuário escolhe. let SWDG_GEO_CACHE = null; // { country, region, city } let SWDG_GEO_PROMISE = null; let SWDG_SUGGESTED_DDD = ""; // "41", "11", etc. let SWDG_DDD_HINT_DISMISSED = false; // por sessão (memória) function swdgStripAccents(str) { try { return String(str || "").normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } catch (_) { return String(str || ""); } } function swdgPrefetchGeo() { if (SWDG_GEO_CACHE) return Promise.resolve(SWDG_GEO_CACHE); if (SWDG_GEO_PROMISE) return SWDG_GEO_PROMISE; SWDG_GEO_PROMISE = swdgPrefetchIP() .then((ip) => { // ipapi.co aceita /json/ (auto) ou /{ip}/json/ const url = ip ? ("https://ipapi.co/" + encodeURIComponent(ip) + "/json/") : "https://ipapi.co/json/"; return fetch(url, { cache: "no-store", credentials: "omit" }).then(r => (r && r.ok ? r.json() : null)).catch(() => null); }) .then((j) => { if (!j) return null; const country = (j.country || j.country_code || "").toString().toUpperCase(); const region = (j.region_code || j.region || j.state || "").toString(); const city = (j.city || "").toString(); return { country, region, city }; }) .catch(() => null) .then((geo) => { SWDG_GEO_CACHE = geo; return geo; }); return SWDG_GEO_PROMISE; } function swdgComputeSuggestedDDD(geo) { if (!geo || geo.country !== "BR") return ""; // 1) Mapa por cidade (mais preciso) const cityKey = swdgStripAccents((geo.city || "").trim().toLowerCase()); const cityDDD = { "curitiba": "41", "sao paulo": "11", "saopaulo": "11", "rio de janeiro": "21", "riodejaneiro": "21", "porto alegre": "51", "portoalegre": "51", "ijui": "55", "ijuí": "55", "santa maria": "55", "santamaria": "55", "uruguaiana": "55", "santa rosa": "55", "santarosa": "55", "passo fundo": "54", "passofundo": "54" }; if (cityKey && cityDDD[cityKey]) return cityDDD[cityKey]; // 2) Fallback por UF (chute conservador, capital) — sempre opcional const uf = (geo.region || "").toString().trim().toUpperCase(); const ufDefault = { "PR": "41", "SP": "11", "RJ": "21", "RS": "51", "SC": "48", "MG": "31", "BA": "71", "DF": "61", "GO": "62", "PE": "81", "CE": "85" }; return ufDefault[uf] || ""; } function swdgPrefetchSuggestedDDD() { if (SWDG_SUGGESTED_DDD) return Promise.resolve(SWDG_SUGGESTED_DDD); return swdgPrefetchGeo().then((geo) => { SWDG_SUGGESTED_DDD = swdgComputeSuggestedDDD(geo) || ""; return SWDG_SUGGESTED_DDD; }); } // tenta pegar o IP já no carregamento (fire-and-forget) try { swdgPrefetchIP(); swdgPrefetchGeo(); swdgPrefetchSuggestedDDD(); } catch (_) { } function findThisScript() { const scripts = $$("script[data-swdg-client]"); return scripts[scripts.length - 1] || null; } function qsAny(keys) { try { const url = String(location.href || ""); const search = String(location.search || ""); const params = new URLSearchParams(search); // case-insensitive lookup (URLSearchParams is case-sensitive) const lowerMap = {}; for (const [k, v] of params.entries()) lowerMap[String(k).toLowerCase()] = v; for (const k of keys) { const kk = String(k).trim(); if (!kk) continue; let v = params.get(kk); if (v == null) v = lowerMap[kk.toLowerCase()]; if (v && String(v).trim() !== "") return String(v).trim(); } // fallback: sometimes params appear after # or in full URL copies for (const k of keys) { const kk = String(k).trim(); if (!kk) continue; const re1 = new RegExp(`[?&#]${kk}=([^&#]+)`, "i"); const m = url.match(re1); if (m && m[1]) { try { return decodeURIComponent(m[1].replace(/\+/g, "%20")).trim(); } catch { return String(m[1]).trim(); } } } } catch (_) { } return ""; } // ========= Atribuição inicial da visita ========= // Preserva a origem externa e os principais parâmetros de campanha durante // a navegação interna. Os parâmetros da URL atual continuam tendo prioridade. const SWDG_ATTRIBUTION_STORAGE_KEY = "swdg_attribution_v1"; const SWDG_ATTRIBUTION_SESSION_MS = 30 * 60 * 1000; // 30 min de inatividade function swdgNormalizeAttributionHost(value) { try { return String(value || "").trim().toLowerCase().replace(/^www\./, "").replace(/\.$/, ""); } catch (_) { return ""; } } function swdgExternalReferrer() { try { const raw = String(document.referrer || "").trim(); if (!raw) return ""; const refHost = swdgNormalizeAttributionHost(new URL(raw).hostname); const curHost = swdgNormalizeAttributionHost(location.hostname); if (!refHost || !curHost || refHost === curHost) return ""; return raw; } catch (_) { return ""; } } function swdgReadAttribution() { try { const raw = localStorage.getItem(SWDG_ATTRIBUTION_STORAGE_KEY); if (!raw) return null; const data = JSON.parse(raw); if (!data || typeof data !== "object") return null; return data; } catch (_) { return null; } } function swdgWriteAttribution(data) { try { localStorage.setItem(SWDG_ATTRIBUTION_STORAGE_KEY, JSON.stringify(data)); } catch (_) { } return data; } function swdgCaptureInitialAttribution() { const now = Date.now(); const externalReferrer = swdgExternalReferrer(); const current = { utm_source: qsAny(["utm_source"]), utm_medium: qsAny(["utm_medium"]), utm_campaign: qsAny(["utm_campaign", "campaign", "campanha"]), utm_term: qsAny(["utm_term", "keyword", "termo", "palavra"]), utm_content: qsAny(["utm_content"]), gclid: qsAny(["gclid"]), gbraid: qsAny(["gbraid"]), wbraid: qsAny(["wbraid"]), fbclid: qsAny(["fbclid"]) }; const hasCampaignSignal = Object.values(current).some(v => String(v || "").trim() !== ""); let saved = swdgReadAttribution(); const lastSeen = saved && Number(saved.last_seen || 0); const expired = !lastSeen || (now - lastSeen > SWDG_ATTRIBUTION_SESSION_MS); // Nova entrada externa ou nova campanha explícita inicia uma atribuição nova. if (!saved || expired || externalReferrer || hasCampaignSignal) { saved = { referrer: externalReferrer || "", landing_url: String(location.href || ""), first_seen: now, last_seen: now, ...current }; return swdgWriteAttribution(saved); } // Navegação interna: mantém a origem inicial e só atualiza a atividade. saved.last_seen = now; return swdgWriteAttribution(saved); } let SWDG_ATTRIBUTION = swdgCaptureInitialAttribution(); function swdgAttributionReferrer() { try { const data = SWDG_ATTRIBUTION || swdgReadAttribution(); if (data && String(data.referrer || "").trim()) return String(data.referrer).trim(); return swdgExternalReferrer(); } catch (_) { return ""; } } function swdgAttributionValue(keys) { const live = qsAny(keys); if (live) return live; try { const data = SWDG_ATTRIBUTION || swdgReadAttribution() || {}; const aliases = { campaign: "utm_campaign", campanha: "utm_campaign", keyword: "utm_term", termo: "utm_term", palavra: "utm_term" }; for (const key of keys || []) { const k = String(key || "").trim().toLowerCase(); const mapped = aliases[k] || k; const value = data[mapped]; if (value != null && String(value).trim() !== "") return String(value).trim(); } } catch (_) { } return ""; } function swdgNormalizeCourseParamValue(value) { try { let v = String(value || "").trim(); if (!v) return ""; v = v.replace(/[\r\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim(); if (!v) return ""; if (String(v).includes("────")) return ""; if (/^[a-z0-9]+(?:[-_][a-z0-9]+)+$/i.test(v)) { v = v .replace(/[-_]+/g, " ") .replace(/\s{2,}/g, " ") .trim() .replace(/\b\w/g, (m) => m.toUpperCase()); } return v; } catch (_) { return ""; } } function swdgGetCourseFromUrl() { const raw = qsAny(["curso_nome", "cursolista", "curso", "cursointeresse"]); return swdgNormalizeCourseParamValue(raw); } function deviceLabel() { return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent) ? "Mobile" : "Desktop"; } function nowBR() { const d = new Date(); const p = (n) => String(n).padStart(2, "0"); return `${p(d.getDate())}/${p(d.getMonth() + 1)}/${d.getFullYear()} ${p(d.getHours())}:${p( d.getMinutes() )}:${p(d.getSeconds())}`; } function digitsOnly(s) { return String(s || "").replace(/\D/g, ""); } // Normaliza telefone vindo de autofill (ex.: +55DD9XXXXXXXX) e evita truncar o final. function normalizePhoneDigits(raw) { let d = digitsOnly(raw); // Autofill costuma vir em formato internacional: +55 + DDD + número (13 dígitos) // Mantemos apenas DDD + número (11 dígitos) if (d.length > 11 && d.startsWith("55")) d = d.slice(2); // Alguns teclados colocam um 0 na frente if (d.length > 11 && d.startsWith("0")) d = d.slice(1); // Se ainda vier maior, pega os últimos 11 (mais seguro que cortar o final) if (d.length > 11) d = d.slice(-11); return d; } function applyPhoneMask(input) { let v = normalizePhoneDigits(input.value).slice(0, 11); if (v.length <= 2) input.value = v ? `(${v}` : ""; else if (v.length <= 7) input.value = `(${v.slice(0, 2)}) ${v.slice(2)}`; else input.value = `(${v.slice(0, 2)}) ${v.slice(2, 7)}-${v.slice(7)}`; } function isValidFullBRPhoneMasked(v) { const s = String(v || "").trim(); if (!/^\(\d{2}\)\s\d{5}-\d{4}$/.test(s)) return false; // Regra Whats Brasil: DDD (2) + celular (9 + 8 dígitos) = 11 const d = digitsOnly(s); if (d.length !== 11) return false; // Todo celular BR (Whats) começa com 9 após o DDD → bloqueia casos tipo (55) 47999-9065 if (d[2] !== "9") return false; return true; } function isValidEmail(v) { const s = String(v || "").trim(); if (!s) return false; // validação simples (suficiente p/ captura de lead) return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(s); } // Nome: letras + espaço + hífen + apóstrofo (permite espaço ao vivo) function sanitizeNameLive(raw) { let s = String(raw || ""); s = s.replace(/[^A-Za-zÀ-ÖØ-öø-ÿ\s'-]/g, ""); s = s.replace(/\s{2,}/g, " "); return s; } function sanitizeNameFinal(raw) { return sanitizeNameLive(raw).replace(/\s+/g, " ").trim(); } function buildInfo() { const pairs = [ ["utm_source", swdgAttributionValue(["utm_source"])], ["utm_medium", swdgAttributionValue(["utm_medium"])], ["utm_campaign", swdgAttributionValue(["utm_campaign", "campaign", "campanha"])], ["utm_term", swdgAttributionValue(["utm_term", "keyword", "termo", "palavra"])], ["utm_content", swdgAttributionValue(["utm_content"])], ["gclid", swdgAttributionValue(["gclid"])], ["wbraid", swdgAttributionValue(["wbraid"])], ["gbraid", swdgAttributionValue(["gbraid"])], ["fbclid", swdgAttributionValue(["fbclid"])], ]; return pairs.map(([k, v]) => `${k}=${v || ""}`).join(" | ") + ` | ua=${navigator.userAgent}`; } async function safeJsonFetch(url) { const res = await fetch(url, { cache: "no-store", credentials: "omit" }); if (!res.ok) throw new Error(`Config HTTP ${res.status}`); return await res.json(); } // ========= Bootstrap: script e config ========= const scriptEl = findThisScript(); if (!scriptEl) return; const clientId = (scriptEl.getAttribute("data-swdg-client") || "").trim(); if (!clientId) return; const scriptSrc = scriptEl.src; const baseUrl = scriptSrc.substring(0, scriptSrc.lastIndexOf("/") + 1); const configUrl = `${baseUrl}config/${encodeURIComponent(clientId)}.json`; function swdgNormalizeAllowedDomainRule(value) { let rule = String(value || "").trim().toLowerCase(); if (!rule) return null; const wildcard = rule.startsWith("*."); if (wildcard) rule = rule.slice(2); try { if (rule.includes("://")) rule = new URL(rule).hostname.toLowerCase(); else rule = rule.split("/")[0].split(":")[0].replace(/^\.+|\.+$/g, ""); } catch (_) { return null; } return rule ? { host: rule, wildcard } : null; } function swdgCurrentHostAllowed(config) { const rules = Array.isArray(config && config.allowedDomains) ? config.allowedDomains : []; if (!rules.length) return true; // vazio = sem restrição const host = String(window.location.hostname || "").trim().toLowerCase().replace(/\.$/, ""); if (!host) return false; return rules.some((value) => { const rule = swdgNormalizeAllowedDomainRule(value); if (!rule || !rule.host) return false; return rule.wildcard ? (host === rule.host || host.endsWith("." + rule.host)) : host === rule.host; }); } let CONFIG = null; // Modo de teste do roteamento: ?teste=true ou ?teste=1 // Simula exatamente o próximo vendedor, mas não avança fila, não contabiliza e não grava histórico. function swdgWhatsappRouterTestMode() { try { const value = String(new URLSearchParams(window.location.search).get('teste') || '').trim().toLowerCase(); return value === 'true' || value === '1'; } catch (_) { return false; } } let SWDG_LAST_ROUTE = null; async function swdgResolveWhatsappRoute() { const fallback = String((CONFIG && CONFIG.whatsDestino) || '').replace(/\D+/g, ''); const routing = CONFIG && CONFIG.whatsappRouting; if (!routing || routing.enabled !== true) { return { whatsapp: fallback, seller_id: '', seller_name: '', mode: 'fixed', routed: false, reason: 'disabled' }; } try { const response = await fetch(baseUrl + 'whatsapp-router.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ clientId, testMode: swdgWhatsappRouterTestMode() }), cache: 'no-store', credentials: 'omit' }); const data = await response.json().catch(() => null); if (!response.ok || !data || data.ok !== true || !data.whatsapp) throw new Error('router unavailable'); SWDG_LAST_ROUTE = data; return data; } catch (_) { const safe = { whatsapp: fallback, seller_id: '', seller_name: '', mode: 'fixed', routed: false, reason: 'fallback_error' }; SWDG_LAST_ROUTE = safe; return safe; } } let mounted = false; let SWDG_OPEN_MODE = "default"; // "default" | "entry" const SWDG_QR_STATE = { token: "", timer: null, prepareTimer: null, fingerprint: "", busy: false, leadData: null, }; // ========= UI ========= const STYLE_ID = "swdg-popup-style-v1"; const ROOT_ID = "swdg-popup-root-v1"; let SWDG_POPUP_HOST = null; let SWDG_POPUP_SHADOW = null; function getPopupHost() { return SWDG_POPUP_HOST || document.getElementById(ROOT_ID); } function getPopupShadow() { const host = getPopupHost(); return SWDG_POPUP_SHADOW || (host && host.shadowRoot) || null; } function getPopupRoot() { const shadow = getPopupShadow(); return shadow ? shadow.getElementById(ROOT_ID) : null; } function injectStyles(scope) { if (!scope) return; if (scope.getElementById && scope.getElementById(STYLE_ID)) return; const css = ` :host{ all: initial; font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; color: initial; } :host, :host *{ box-sizing: border-box; } /* ====== PREMIUM SKIN (substitua o CSS do injectStyles por este) ====== */ .swdg-overlay{ position:fixed; inset:0; display:none; align-items:center; justify-content:center; z-index:999999; font-family:Inter,system-ui,Arial; padding:16px; } .swdg-overlay[aria-hidden="false"]{ display:flex; } .swdg-overlay::before{ content:""; position:absolute; inset:0; background: radial-gradient(circle at 20% 10%, rgba(255,255,255,.10), transparent 34%), radial-gradient(circle at 80% 0%, rgba(255,212,0,.08), transparent 28%), rgba(3,8,16,.68); backdrop-filter: blur(14px) saturate(120%); -webkit-backdrop-filter: blur(14px) saturate(120%); } /* animação suave */ .swdg-modal{ position:relative; width:min(440px, calc(100vw - 32px)); max-width:calc(100vw - 32px); box-sizing:border-box; border-radius:22px; overflow:visible; max-height:92vh; display:flex; flex-direction:column; margin:0 auto; border:1px solid rgba(255,255,255,.10); background: radial-gradient(circle at 14% 0%, rgba(255,255,255,.10), transparent 32%), radial-gradient(circle at 86% 8%, rgba(255,255,255,.06), transparent 36%), var(--swdg-bg, linear-gradient(180deg, var(--swdg-primaryDarker, #062033) 0%, var(--swdg-primaryDark, #072235) 100% )); backdrop-filter:blur(20px) saturate(130%); -webkit-backdrop-filter:blur(20px) saturate(130%); box-shadow: 0 36px 100px rgba(0,0,0,.62), 0 10px 28px rgba(0,0,0,.28), 0 0 0 1px rgba(255,255,255,.03); transform: translateY(8px) scale(.982); opacity:0; transition: transform .22s ease, opacity .22s ease, box-shadow .22s ease; } .swdg-overlay[aria-hidden="false"] .swdg-modal{ transform: translateY(0) scale(1); opacity:1; } #swdg-popup-root-v1[data-swdg-glow="1"] .swdg-modal{ box-shadow: 0 36px 100px rgba(0,0,0,.62), 0 10px 28px rgba(0,0,0,.28), 0 0 38px rgba(255,255,255,.06); } /* barra “premium” no topo */ .swdg-modal::before{ content:""; position:absolute; left:14px; right:14px; top:0; height:3px; background: var(--swdg-topbar, linear-gradient(90deg, rgba(255,212,0,0), rgba(255,212,0,.95), rgba(255,255,255,.35), rgba(255,212,0,0))); opacity:.95; border-radius:999px; } .swdg-modal::after{ content:""; position:absolute; inset:1px; border-radius:23px; pointer-events:none; box-shadow: inset 0 1px 0 rgba(255,255,255,.08); } .swdg-close{ position:absolute; top:14px; right:14px; width:40px; height:40px; border-radius:13px; display:flex; align-items:center; justify-content:center; line-height:1; border:1px solid rgba(255,255,255,.16); background:linear-gradient(180deg, rgba(255,255,255,.12), rgba(255,255,255,.06)); color: var(--swdg-textOnPrimary, #fff); font-size:22px; cursor:pointer; box-shadow: 0 10px 22px rgba(0,0,0,.18); transition: transform .12s ease, background .18s ease, border-color .18s ease, box-shadow .18s ease; } .swdg-close:hover{ background:linear-gradient(180deg, rgba(255,255,255,.18), rgba(255,255,255,.09)); border-color:rgba(255,255,255,.28); box-shadow: 0 14px 28px rgba(0,0,0,.24); } .swdg-close:active{ transform: scale(.96); } /* ========================= BADGE (imagem/selo) — JSON: badge{enabled,mode,src,alt,position,size} ========================= */ .swdg-badge{ position:absolute; width: var(--swdg-badge-size, 156px); height: var(--swdg-badge-size, 156px); z-index: 6; pointer-events:none; filter: drop-shadow(0 18px 30px rgba(0,0,0,.42)); } .swdg-badge img{ width:100%; height:100%; display:block; object-fit:contain; } .swdg-badge--top-left{ top: var(--swdg-badge-offset, -18px); left: var(--swdg-badge-offset, -18px); } .swdg-badge--top-right{ top: var(--swdg-badge-offset, -18px); right: var(--swdg-badge-offset, -18px); } .swdg-badge--bottom-left{ bottom: var(--swdg-badge-offset, -18px); left: var(--swdg-badge-offset, -18px); } .swdg-badge--bottom-right{ bottom: var(--swdg-badge-offset, -18px); right: var(--swdg-badge-offset, -18px); } @media (max-width:520px){ .swdg-badge{ width: min(var(--swdg-badge-size, 156px), 142px); height: min(var(--swdg-badge-size, 156px), 142px); } .swdg-badge--top-left, .swdg-badge--top-right, .swdg-badge--bottom-left, .swdg-badge--bottom-right{ --swdg-badge-offset: -12px; } } /* ========================= FASE 2 — Reset interno + header premium consistente ========================= */ :host{ color-scheme: light; } #swdg-popup-root-v1, #swdg-popup-root-v1 *, #swdg-popup-root-v1 *::before, #swdg-popup-root-v1 *::after{ box-sizing:border-box; } #swdg-popup-root-v1{ font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; line-height:1.35; text-size-adjust:100%; -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale; } #swdg-popup-root-v1 h1, #swdg-popup-root-v1 h2, #swdg-popup-root-v1 h3, #swdg-popup-root-v1 h4, #swdg-popup-root-v1 h5, #swdg-popup-root-v1 h6, #swdg-popup-root-v1 p, #swdg-popup-root-v1 figure, #swdg-popup-root-v1 blockquote{ margin:0; } #swdg-popup-root-v1 img, #swdg-popup-root-v1 svg{ display:block; max-width:100%; height:auto; } #swdg-popup-root-v1 button, #swdg-popup-root-v1 input, #swdg-popup-root-v1 select, #swdg-popup-root-v1 textarea{ font:inherit; letter-spacing:normal; } /* header mais “marca”, com estrutura previsível */ .swdg-header{ padding:26px 22px 18px; text-align:center; background: linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,0)); border-bottom: 1px solid rgba(255,255,255,.08); } .swdg-headerBlock{ width:100%; display:flex; flex-direction:column; align-items:center; justify-content:flex-start; gap:9px; } .swdg-logoRow{ width:100%; display:flex; justify-content:center; align-items:center; padding:0 48px; margin:0; min-height:90px; } .swdg-logoImg{ height:84px !important; max-height:124px !important; width:auto !important; max-width:min(230px, 74vw) !important; object-fit:contain !important; filter: drop-shadow(0 12px 26px rgba(0,0,0,.26)); margin:0 !important; } .swdg-headings{ width:100%; max-width:430px; display:flex; flex-direction:column; align-items:center; justify-content:flex-start; gap:5px; text-align:center; } .swdg-brandName, .swdg-sub, .swdg-title{ display:block; width:100%; text-align:center; margin:0; float:none !important; text-wrap:balance; } .swdg-brandName{ color: var(--swdg-textOnPrimary, #fff); font-weight:900; font-size:21px; line-height:1.04; letter-spacing:.18px; text-shadow: 0 2px 12px rgba(0,0,0,.18); } .swdg-sub{ color: var(--swdg-textAccent, #ffd84d) !important; font-weight:800; font-size:12.5px; line-height:1.2; letter-spacing:.6px; text-transform:uppercase; text-shadow: 0 2px 10px rgba(0,0,0,.15); } .swdg-title{ color: var(--swdg-textOnPrimary, #fff); font-size:19px; font-weight:850; line-height:1.13; letter-spacing:.18px; max-width:410px; text-shadow: 0 4px 16px rgba(0,0,0,.18); } /* blindagem extra contra CSS global do tema / page builder */ #swdg-popup-root-v1 .swdg-header{ text-align:center !important; } #swdg-popup-root-v1 .swdg-headerBlock, #swdg-popup-root-v1 .swdg-logoRow, #swdg-popup-root-v1 .swdg-headings{ display:flex !important; flex-direction:column !important; justify-content:flex-start !important; align-items:center !important; align-content:center !important; text-align:center !important; width:100% !important; } #swdg-popup-root-v1 .swdg-logoRow{ padding-left:52px !important; padding-right:52px !important; gap:0 !important; } #swdg-popup-root-v1 .swdg-logoRow > *{ margin-left:auto !important; margin-right:auto !important; } #swdg-popup-root-v1 .swdg-logoImg{ display:block !important; float:none !important; } #swdg-popup-root-v1 .swdg-brandName, #swdg-popup-root-v1 .swdg-sub, #swdg-popup-root-v1 .swdg-title{ margin-left:auto !important; margin-right:auto !important; } #swdg-popup-root-v1 .swdg-header h1, #swdg-popup-root-v1 .swdg-header h2, #swdg-popup-root-v1 .swdg-header h3, #swdg-popup-root-v1 .swdg-header p, #swdg-popup-root-v1 .swdg-header div{ text-align:center !important; } /* form mais claro (como seu exemplo) */ .swdg-form{ padding:18px 20px 20px; display:grid; gap:13px; /* mantém o fundo contínuo do modal (sem “bloco” separado aqui) */ background: transparent; flex:1 1 auto; overflow:auto; -webkit-overflow-scrolling: touch; } .swdg-header{ flex:0 0 auto; } .swdg-form input[type="text"], .swdg-form input[type="tel"], .swdg-form input[type="email"], .swdg-form select{ width:100%; min-height:50px; padding:14px 14px; border-radius:15px; border:1.5px solid color-mix(in srgb, var(--swdg-inputBorderColor, var(--swdg-textAccent, #ffd84d)) 72%, rgba(11,25,38,.18) 28%); background: linear-gradient(180deg, rgba(255,255,255,.985), rgba(255,255,255,.955)); outline:none; font-size:14px; font-weight:600; color:#0b2233; box-shadow: 0 14px 30px rgba(0,0,0,.14); transition: transform .12s ease, border-color .18s ease, box-shadow .18s ease, background .18s ease; } .swdg-form input:focus, .swdg-form select:focus{ border-color: color-mix(in srgb, var(--swdg-inputBorderColor, var(--swdg-textAccent, #ffd84d)) 72%, white 28%); box-shadow: 0 18px 40px rgba(0,0,0,.20), 0 0 0 4px color-mix(in srgb, var(--swdg-inputBorderColor, var(--swdg-textAccent, #ffd84d)) 18%, transparent); transform: translateY(-1px); background: rgba(255,255,255,1); } .swdg-form input::placeholder{ color:rgba(11,34,51,.46); font-weight:500; } /* consent mais elegante */ /* DDD hint (sugestão opcional) */ .swdg-ddd-hint{ margin-top: -25px; padding: 10px 12px; border-radius: 12px; border: 1px solid rgba(255,255,255,.10); background: rgba(255,255,255,.06); color: rgba(255,255,255,.92); font-size: 11px; line-height: 1.25; display: flex; align-items: center; justify-content: space-between; gap: 10px; } .swdg-ddd-hint-text{ flex: 1; } .swdg-ddd-hint-actions{ display:flex; gap:8px; } .swdg-ddd-btn{ appearance:none; border: 1px solid rgba(255,255,255,.14); background: rgba(0,0,0,.18); color: rgba(255,255,255,.92); border-radius: 10px; padding: 8px 10px; font-weight: 700; font-size: 12px; cursor: pointer; transition: transform .12s ease, filter .12s ease; } .swdg-ddd-btn:hover{ filter: brightness(1.08); transform: translateY(-1px); } .swdg-ddd-btn:active{ transform: translateY(0px) scale(.99); } .swdg-consent{ display:flex; align-items:center; gap:12px; padding:11px 13px; border-radius:15px; background: linear-gradient(180deg, rgba(255,255,255,.085), rgba(255,255,255,.05)); border:1px solid rgba(255,255,255,.11); box-shadow: inset 0 1px 0 rgba(255,255,255,.04); color: rgba(255,255,255,.92); font-size:12px; line-height:1.3; cursor:pointer; user-select:none; } .swdg-consent > span:not(.swdg-checkWrap){ flex:1 1 auto; min-width:0; } .swdg-checkWrap{ position:relative; width:46px; height:26px; flex:0 0 46px; margin-left:auto; } .swdg-checkWrap input{ position:absolute; inset:0; opacity:0; cursor:pointer; z-index:2; } .swdg-checkUi{ position:absolute; inset:0; border-radius:999px; background: rgba(255,255,255,.12); border:1px solid rgba(255,255,255,.22); box-shadow: inset 0 1px 2px rgba(0,0,0,.22); transition: background .22s ease, border-color .22s ease, box-shadow .22s ease; } .swdg-checkUi::after{ content:""; position:absolute; left:3px; top:3px; width:18px; height:18px; border-radius:50%; background: #ffffff; box-shadow: 0 2px 8px rgba(0,0,0,.28); transition: transform .22s ease; } .swdg-checkWrap input:checked + .swdg-checkUi{ background: linear-gradient(180deg, #25d366, #18b955); border-color: rgba(37,211,102,.95); box-shadow: 0 0 0 3px rgba(37,211,102,.12), inset 0 1px 0 rgba(255,255,255,.16); } .swdg-checkWrap input:checked + .swdg-checkUi::after{ transform: translateX(20px); } /* CTA mais “premium” */ .swdg-btn{ margin-top:8px; min-height:50px; padding:16px 18px; border-radius:17px; border:0; cursor:pointer; position:relative; overflow:hidden; background: linear-gradient(180deg, var(--swdg-button, var(--swdg-button, #22e36a)) 0%, var(--swdg-button, var(--swdg-button, #22e36a)) 18%, var(--swdg-buttonHover, var(--swdg-buttonHover, #10b455)) 100%); color: var(--swdg-textOnPrimary, #fff); font-weight:900; letter-spacing:.72px; font-size:14px; text-transform:uppercase; box-shadow: 0 22px 50px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.18); transition: transform .12s ease, filter .18s ease, box-shadow .18s ease; display:inline-flex; align-items:center; justify-content:center; gap:10px; } .swdg-btn::before{ content:""; position:absolute; inset:0 auto 0 -22%; width:42%; background: linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,.16), rgba(255,255,255,0)); transform: skewX(-22deg); opacity:.75; } .swdg-btn .swdg-btn-icon{ width:18px; height:18px; display:inline-block; flex:0 0 18px; } #swdg-popup-root-v1[data-swdg-whats-icon="0"] .swdg-btn .swdg-btn-icon{ display:none; } .swdg-btn:hover{ filter: brightness(1.03); transform: translateY(-1px); box-shadow: 0 26px 58px rgba(0,0,0,.36), inset 0 1px 0 rgba(255,255,255,.2); } .swdg-btn:active{ transform: translateY(0) scale(.988); } .swdg-btn:focus-visible{ outline:none; box-shadow: 0 0 0 4px color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 18%, transparent), 0 22px 50px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.18); } /* ===== FLOW BUTTON STYLES ===== */ .swdg-flow-btn-primary{ background: linear-gradient(180deg, var(--swdg-button, #22e36a) 0%, var(--swdg-button, #22e36a) 18%, var(--swdg-buttonHover, #10b455) 100%) !important; color: var(--swdg-textOnPrimary, #fff) !important; border: 1px solid rgba(255,255,255,.08) !important; box-shadow: 0 20px 46px rgba(0,0,0,.34), inset 0 1px 0 rgba(255,255,255,.18) !important; } .swdg-flow-btn-primary:hover{ filter: brightness(1.04) !important; transform: translateY(-1px); } .swdg-btn-secondary{ background: linear-gradient(180deg, rgba(255,255,255,.11) 0%, rgba(255,255,255,.08) 100%) !important; border: 1px solid rgba(255,255,255,.18) !important; color: rgba(255,255,255,.92) !important; box-shadow: inset 0 1px 0 rgba(255,255,255,.08), 0 10px 24px rgba(0,0,0,.18) !important; } .swdg-btn-secondary::before{ opacity: .28 !important; } .swdg-btn-secondary:hover{ filter: brightness(1.02) !important; box-shadow: inset 0 1px 0 rgba(255,255,255,.10), 0 14px 28px rgba(0,0,0,.22) !important; } .swdg-flow-actions .swdg-btn + .swdg-btn{ margin-top: 12px; } /* ===== FLOW PREMIUM SAFE ===== */ .swdg-btn-ghost{ appearance:none; border:none; background:transparent !important; box-shadow:none !important; color:rgba(255,255,255,.82) !important; font-weight:700; font-size:14px; letter-spacing:.01em; padding:6px 4px 0; margin:8px auto 0; min-height:auto; width:auto; display:inline-flex; align-items:center; justify-content:center; gap:6px; text-transform:none; } .swdg-btn-ghost::before{ display:none !important; } .swdg-btn-ghost:hover{ transform:none !important; filter:none !important; box-shadow:none !important; color:#fff !important; opacity:.96; } .swdg-btn-ghost:focus-visible{ outline:none; box-shadow:none !important; text-decoration:underline; } .swdg-card{ animation:swdgFlowFade .20s ease; } @keyframes swdgFlowFade{ from{ opacity:.0; transform:translateY(6px); } to{ opacity:1; transform:translateY(0); } } .swdg-card .swdg-title{ font-size: clamp(28px, 4.2vw, 34px); line-height:1.12; letter-spacing:-.02em; } .swdg-card .swdg-sub{ opacity:.92; max-width: 34ch; margin-left:auto; margin-right:auto; } /* ===== FLOW PREMIUM PLUS ===== */ #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-headings .swdg-brandName, #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-headings .swdg-sub, #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-headings .swdg-title{ display:none !important; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-header{ padding-bottom:8px; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-headerBlock{ gap:8px; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-logoRow{ margin-bottom:0; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-logoImg{ max-height:54px; width:auto; object-fit:contain; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-form{ gap:12px; } .swdg-flow-step{ display:grid; gap:12px; animation:swdgFlowPremiumIn .24s ease; } @keyframes swdgFlowPremiumIn{ from{ opacity:0; transform:translateY(8px); } to{ opacity:1; transform:translateY(0); } } .swdg-flow-meta{ display:grid; gap:10px; } .swdg-flow-progress{ display:grid; gap:7px; } .swdg-flow-progressTop{ display:flex; align-items:center; justify-content:space-between; gap:10px; } .swdg-flow-kicker{ color: color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 82%, white 18%); font-size:11px; font-weight:900; text-transform:uppercase; letter-spacing:.08em; } .swdg-flow-stepCount{ color:rgba(255,255,255,.72); font-size:11px; font-weight:700; } .swdg-flow-progressBar{ position:relative; height:6px; border-radius:999px; overflow:hidden; background:rgba(255,255,255,.08); border:1px solid rgba(255,255,255,.08); } .swdg-flow-progressFill{ position:absolute; inset:0 auto 0 0; border-radius:999px; background:linear-gradient(90deg, color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 88%, white 12%), color-mix(in srgb, var(--swdg-button, #25d366) 78%, white 22%)); box-shadow:0 0 20px color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 22%, transparent); } .swdg-flow-question{ margin:2px 0 0; color:var(--swdg-textOnPrimary, #fff); font-size:clamp(24px, 4.2vw, 24px); font-weight:900; line-height:1.08; letter-spacing:-.03em; text-align:center; text-wrap:balance; } .swdg-flow-copy{ color:rgba(255,255,255,.82); font-size:13px; line-height:1.6; text-align:center; max-width:100%; margin:-10px auto 14px; text-wrap:normal; } .swdg-flow-actions{ display:grid; gap:10px; margin-top:4px; } .swdg-flow-actions .swdg-btn{ width:100%; margin-top:0; } .swdg-btn-ghost{ margin:2px auto 0; font-size:12.5px; color:rgba(255,255,255,.72) !important; } .swdg-btn-ghost:hover{ color:#fff !important; } #whatsPopupForm.swdg-flow-form-active{ gap:12px; } #whatsPopupForm.swdg-flow-form-active input, #whatsPopupForm.swdg-flow-form-active select{ min-height:50px; border-radius:16px; border:1px solid rgba(255,255,255,.16); background:linear-gradient(180deg, rgba(255,255,255,.18), rgba(255,255,255,.10)); backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); box-shadow:inset 0 1px 0 rgba(255,255,255,.10), 0 10px 24px rgba(0,0,0,.10); } #whatsPopupForm.swdg-flow-form-active input::placeholder{ color:rgba(255,255,255,.66); } #whatsPopupForm.swdg-flow-form-active select{ color:var(--swdg-textOnPrimary, #fff); } #whatsPopupForm.swdg-flow-form-active select option{ color:#111; } #whatsPopupForm.swdg-flow-form-active input:focus, #whatsPopupForm.swdg-flow-form-active select:focus{ border-color:color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 70%, white 30%); box-shadow:0 0 0 4px color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 18%, transparent), inset 0 1px 0 rgba(255,255,255,.15), 0 14px 28px rgba(0,0,0,.14); } #whatsPopupForm.swdg-flow-form-active .swdg-consent{ border:1px solid rgba(255,255,255,.10); background:linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,.06)); border-radius:16px; padding:12px 14px; } #whatsPopupForm.swdg-flow-form-active .swdg-btn{ width:100%; } .swdg-status{ color: var(--swdg-textOnPrimary, #fff); font-size:12.5px; text-align:center; min-height:18px; margin-top:2px; opacity:.92; } /* Continuação discreta no celular — habilitada individualmente no JSON */ .swdg-qr-option{ display:grid; grid-template-columns:126px minmax(0,1fr); align-items:center; gap:14px; padding:13px; border-radius:17px; border:1px solid rgba(255,255,255,.13); background:linear-gradient(180deg,rgba(255,255,255,.10),rgba(255,255,255,.055)); box-shadow:inset 0 1px 0 rgba(255,255,255,.06); } .swdg-qr-option[hidden]{display:none!important} .swdg-qr-canvasWrap{ width:126px;height:126px;padding:6px;border-radius:14px;background:#fff; display:grid;place-items:center;box-shadow:0 12px 30px rgba(0,0,0,.18); } .swdg-qr-canvas{display:block;width:114px!important;height:114px!important} .swdg-qr-copy{min-width:0;color:var(--swdg-textOnPrimary,#fff)} .swdg-qr-copy small{display:block;margin-bottom:4px;font-size:9px;font-weight:900;letter-spacing:.75px;text-transform:uppercase;color:var(--swdg-textAccent,#ffd84d)} .swdg-qr-copy strong{display:block;font-size:14px;line-height:1.2;margin-bottom:5px} .swdg-qr-copy span{display:block;font-size:11px;line-height:1.4;opacity:.84} .swdg-qr-loading{font-size:10px!important;margin-top:6px;opacity:.72!important} @media(max-width:520px){.swdg-qr-option{display:none!important}} .swdg-error{ border:2px solid rgba(255, 80, 80, .95) !important; } @media (max-width:520px){ .swdg-modal{ width:min(520px, 96vw); max-height:92vh; display:flex; flex-direction:column; border-radius:20px; } .swdg-modal::after{ border-radius:19px; } .swdg-header{ padding:20px 15px 13px; } .swdg-headerBlock{ gap:7px; } .swdg-logoRow{ min-height:76px; padding:0 38px; } .swdg-logoImg{ height:70px !important; max-height:70px !important; } .swdg-headings{ max-width:100%; gap:4px; } .swdg-brandName{ font-size:18px; line-height:1.08; } .swdg-sub{ font-size:10.5px; line-height:1.18; letter-spacing:.5px; } .swdg-title{ font-size:16.5px; line-height:1.1; max-width:320px; } .swdg-form{ padding:12px 14px 16px; gap:10px; overflow:auto; -webkit-overflow-scrolling: touch; } .swdg-form input[type="text"], .swdg-form input[type="tel"], .swdg-form input[type="email"], .swdg-form select{ min-height:48px; padding:13px 12px; border-radius:14px; font-size:14px; box-shadow: 0 10px 22px rgba(0,0,0,.12); } .swdg-consent{ padding:10px 10px; border-radius:14px; gap:10px; font-size:11px; } .swdg-btn{ margin-top:3px; min-height:50px; padding:14px 14px; border-radius:16px; font-size:13px; letter-spacing:.56px; } .swdg-close{ top:10px; right:10px; width:36px; height:36px; border-radius:12px; font-size:20px; } /* ===== FLOW PREMIUM PLUS ===== */ #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-headings .swdg-brandName, #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-headings .swdg-sub, #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-headings .swdg-title{ display:none !important; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-header{ padding-bottom:8px; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-headerBlock{ gap:8px; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-logoRow{ margin-bottom:0; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-logoImg{ max-height:54px; width:auto; object-fit:contain; } #swdg-popup-root-v1[data-swdg-ui-mode="flow"] .swdg-form{ gap:12px; } .swdg-flow-step{ display:grid; gap:12px; animation:swdgFlowPremiumIn .24s ease; } @keyframes swdgFlowPremiumIn{ from{ opacity:0; transform:translateY(8px); } to{ opacity:1; transform:translateY(0); } } .swdg-flow-meta{ display:grid; gap:10px; } .swdg-flow-progress{ display:grid; gap:7px; } .swdg-flow-progressTop{ display:flex; align-items:center; justify-content:space-between; gap:10px; } .swdg-flow-kicker{ color: color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 82%, white 18%); font-size:11px; font-weight:900; text-transform:uppercase; letter-spacing:.08em; } .swdg-flow-stepCount{ color:rgba(255,255,255,.72); font-size:11px; font-weight:700; } .swdg-flow-progressBar{ position:relative; height:6px; border-radius:999px; overflow:hidden; background:rgba(255,255,255,.08); border:1px solid rgba(255,255,255,.08); } .swdg-flow-progressFill{ position:absolute; inset:0 auto 0 0; border-radius:999px; background:linear-gradient(90deg, color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 88%, white 12%), color-mix(in srgb, var(--swdg-button, #25d366) 78%, white 22%)); box-shadow:0 0 20px color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 22%, transparent); } .swdg-flow-question{ margin:2px 0 0; color:var(--swdg-textOnPrimary, #fff); font-size:clamp(24px, 4.2vw, 24px); font-weight:900; line-height:1.08; letter-spacing:-.03em; text-align:center; text-wrap:balance; } .swdg-flow-copy{ color:rgba(255,255,255,.82); font-size:12.5px; line-height:1.6; text-align:center; max-width:100%; margin:-10px auto 14px; text-wrap:normal; } .swdg-flow-actions{ display:grid; gap:10px; margin-top:4px; } .swdg-flow-actions .swdg-btn{ width:100%; margin-top:0; } .swdg-btn-ghost{ margin:2px auto 0; font-size:12.5px; color:rgba(255,255,255,.72) !important; } .swdg-btn-ghost:hover{ color:#fff !important; } #whatsPopupForm.swdg-flow-form-active{ gap:12px; } #whatsPopupForm.swdg-flow-form-active input, #whatsPopupForm.swdg-flow-form-active select{ min-height:50px; border-radius:16px; border:1px solid rgba(255,255,255,.16); background:linear-gradient(180deg, rgba(255,255,255,.18), rgba(255,255,255,.10)); backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); box-shadow:inset 0 1px 0 rgba(255,255,255,.10), 0 10px 24px rgba(0,0,0,.10); } #whatsPopupForm.swdg-flow-form-active input::placeholder{ color:rgba(255,255,255,.66); } #whatsPopupForm.swdg-flow-form-active select{ color:var(--swdg-textOnPrimary, #fff); } #whatsPopupForm.swdg-flow-form-active select option{ color:#111; } #whatsPopupForm.swdg-flow-form-active input:focus, #whatsPopupForm.swdg-flow-form-active select:focus{ border-color:color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 70%, white 30%); box-shadow:0 0 0 4px color-mix(in srgb, var(--swdg-textAccent, #ffd84d) 18%, transparent), inset 0 1px 0 rgba(255,255,255,.15), 0 14px 28px rgba(0,0,0,.14); } #whatsPopupForm.swdg-flow-form-active .swdg-consent{ border:1px solid rgba(255,255,255,.10); background:linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,.06)); border-radius:16px; padding:12px 14px; } #whatsPopupForm.swdg-flow-form-active .swdg-btn{ width:100%; } .swdg-status{ font-size:10px; } } @media (max-width:360px){ .swdg-header{ padding:16px 12px 10px; } .swdg-logoRow{ min-height:66px; padding:0 40px; } .swdg-logoImg{ height:60px !important; max-height:60px !important; } .swdg-brandName{ font-size:17px; } .swdg-sub{ font-size:10px; } .swdg-title{ font-size:15px; } .swdg-form{ padding:10px 12px 12px; gap:9px; } .swdg-form input[type="text"], .swdg-form input[type="tel"], .swdg-form input[type="email"], .swdg-form select{ padding:12px 12px; font-size:13.5px; } .swdg-btn{ padding:13px 12px; font-size:12.5px; } } .swdg-devNotice{ margin-top:10px; padding:10px 12px; border-radius:12px; border:1px solid rgba(255,212,0,.35); background:rgba(255,212,0,.10); color:rgba(255,255,255,.92); font-size:12px; line-height:1.25; } /* ====== Pulse (opcional) ====== */ #swdg-popup-root-v1[data-swdg-pulse="1"] .swdg-btn{ animation: swdgPulse 1.6s ease-out infinite; } @keyframes swdgPulse{ 0%{ box-shadow: 0 0 0 0 rgba(var(--swdg-pulseRgb, 34,227,106), .55); } 70%{ box-shadow: 0 0 0 14px rgba(var(--swdg-pulseRgb, 34,227,106), 0); } 100%{ box-shadow: 0 0 0 0 rgba(var(--swdg-pulseRgb, 34,227,106), 0); } } #swdg-popup-root-v1[data-swdg-pulse="1"] .swdg-btn{ /* sincroniza a cor do pulse com o botão */ --_pulse: var(--swdg-button, var(--swdg-button, #22e36a)); } #swdg-popup-root-v1[data-swdg-pulse="1"] .swdg-btn{ /* substitui o rgba fixo acima em browsers modernos via shadow color */ } /* ====== Theme extras ====== */ #swdg-popup-root-v1 input:focus, #swdg-popup-root-v1 select:focus{ outline:none; border-color: rgba(255,212,0,.72); box-shadow: 0 0 0 4px rgba(255,216,77,.14), 0 18px 40px rgba(0,0,0,.20); } #swdg-popup-root-v1 input:focus-visible, #swdg-popup-root-v1 select:focus-visible, #swdg-popup-root-v1 button:focus-visible{ outline:none; } /* ====== Mobile refinado (consolidado na Fase 2) ====== */ /* Quando o teclado abre no iOS, evita “estourar” */ @supports (-webkit-touch-callout: none){ @media (max-width:520px){ .swdg-modal{ max-height:88vh; } } } /* ========================= THEME-PROOF (Elementor/WP) - impede tema de "sumir" com texto do `); const value = await new Promise((resolve) => { const btn = box.querySelector("#" + CSS.escape(btnId)); const inp = box.querySelector("#" + CSS.escape(inputId)); if (inp && typeof meta.onInput === "function") { inp.addEventListener("input", meta.onInput, { passive: true }); } const go = () => { const raw = String(inp && inp.value || ""); const formatted = typeof meta.format === "function" ? meta.format(raw) : String(raw || "").trim(); const valid = typeof meta.validate === "function" ? meta.validate(formatted) : true; if (!valid) { showInlineError(box, meta.error || "Preencha este campo 🙂"); return; } if (inp) inp.value = formatted; clearInlineError(box); resolve(formatted); }; if (inp) inp.addEventListener("keydown", (e) => { if (e.key === "Enter") go(); }); if (btn) btn.addEventListener("click", go); if (inp) setTimeout(() => { try { inp.focus(); } catch (_) { } }, 10); }); vars[meta.key] = value; if (meta.key === "whats" || meta.key === "whatsapp") { vars.whats = value; vars.whats_formatado = value; vars.whats_digits = normDigits(value); } appendMsg("out", escapeHtml(value)); swdgFlowSaveSession("", { consent: true, vars: { ...vars } }, "chat"); } currentIndex = getExplicitNextIndex(step, defaultNextIndex); continue; } if (type === "redirect") { await renderRedirectStep(step, varsForTemplate); currentIndex = getExplicitNextIndex(step, defaultNextIndex); continue; } if (type === "cta") { const t = await showTyping(cfg); t.remove(); const label = step.label || "Iniciar atendimento no WhatsApp"; const tpl = step.messageTemplate || ""; const fallbackMsg = fillTemplate(tpl, varsForTemplate); const chatQualification = swdgBuildCommercialQualification({ nome: vars.nome || "", curso: vars.curso || vars.curso_nome || "" }, vars); const msg = (chatQualification && chatQualification.commercial && chatQualification.whatsapp) ? chatQualification.whatsapp : fallbackMsg; vars.__cta_message = msg; if (chatQualification && chatQualification.commercial && chatQualification.observacao) { vars.observacao = chatQualification.observacao; } const explicitDestino = step.whatsDestino || step.phone || step.whatsapp || ""; let destino = String(explicitDestino || "").replace(/\D+/g, ""); let chatRoute = null; if (!destino) { chatRoute = await swdgResolveWhatsappRoute(); destino = String((chatRoute && chatRoute.whatsapp) || (cfg && cfg.whatsDestino) || "").replace(/\D+/g, ""); if (chatRoute && chatRoute.seller_name) { vars.vendedor_id = chatRoute.seller_id || ""; vars.vendedor_nome = chatRoute.seller_name || ""; vars.vendedor_whatsapp = destino; vars.roteamento_modo = chatRoute.mode || "fixed"; } } const wa = destino ? `https://wa.me/${destino}?text=${encodeURIComponent(msg)}` : ""; const ctaIntro = fillTemplate(step.text || "Perfeito. Clique no botão abaixo para iniciar o atendimento no WhatsApp com tudo preenchido:", varsForTemplate); const ctaWrap = document.createElement("div"); ctaWrap.innerHTML = `
${mdLiteToHtml(ctaIntro)}
` + (wa ? `
` : ""); const ctaLog = document.getElementById("swdg_chat_log"); const ctaMsgDiv = document.createElement("div"); ctaMsgDiv.className = "swdg_msg in"; ctaMsgDiv.appendChild(ctaWrap); if (ctaLog) { ctaLog.appendChild(ctaMsgDiv); ctaLog.scrollTop = ctaLog.scrollHeight; } requestAnimationFrame(() => ctaMsgDiv.classList.add("show")); if (wa) { const ctaBtn = ctaWrap.querySelector(".swdg_cta_btn"); if (ctaBtn) { await new Promise((resolve) => { ctaBtn.addEventListener("click", async (e) => { e.stopPropagation(); ctaBtn.disabled = true; try { if (!vars.__webhook_sent) { vars.__webhook_sent = true; await sendChatWebhook(cfg, vars); } } catch (_) { } try { swdgFlowClearSession("chat"); } catch (_) { } try { swdgOpenWhatsPreferApp(wa); } catch (_) { location.href = wa; } resolve(); }, { once: true }); }); } } currentIndex = getExplicitNextIndex(step, defaultNextIndex); continue; } if (type === "submit") { if (String(step.to || "").toLowerCase() === "webhook") { const hasCta = flow.some(s => s && s.type === "cta"); if (!hasCta && !vars.__webhook_sent) { vars.__webhook_sent = true; await sendChatWebhook(cfg, vars); } } currentIndex = getExplicitNextIndex(step, defaultNextIndex); continue; } currentIndex = defaultNextIndex; } } const SWDG_UNIFIED_LEAD_EVENT = "swdg_lead_enviado"; function swdgNormalizePhoneForAds(value) { // Conversoes otimizadas do Google Ads esperam telefone em E.164. // Esta normalizacao afeta somente o user_data enviado ao dataLayer; // o telefone original usado por webhook, dashboard, e-mail e WhatsApp permanece intacto. const digits = (typeof digitsOnly !== "undefined") ? digitsOnly(value || "") : String(value || "").replace(/\D+/g, ""); if (!digits) return ""; let normalized = digits; if (!normalized.startsWith("55") && normalized.length === 11) { normalized = "55" + normalized; } // Para numeros brasileiros validos, retorna +55DDDNUMERO (E.164). if (normalized.startsWith("55") && normalized.length >= 12) { return "+" + normalized; } return normalized; } function swdgPushLeadEvent(payload) { try { const safe = payload || {}; const phone = swdgNormalizePhoneForAds(safe.whats || safe.phone_number || ""); const firstName = String(safe.nome || safe.first_name || "").trim(); if (!phone || phone.length < 12) return false; const mode = String(safe.mode || safe.popup_mode || "default").trim() || "default"; const source = String(safe.source || "popup_system").trim() || "popup_system"; const dedupeKey = [phone, mode, String(location.pathname || "")].join("|"); const now = Date.now(); window.__SWDG_LEAD_EVENT_CACHE__ = window.__SWDG_LEAD_EVENT_CACHE__ || {}; const lastSent = Number(window.__SWDG_LEAD_EVENT_CACHE__[dedupeKey] || 0); if (lastSent && (now - lastSent) < 10000) return false; window.__SWDG_LEAD_EVENT_CACHE__[dedupeKey] = now; window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: SWDG_UNIFIED_LEAD_EVENT, user_data: { phone_number: phone, first_name: firstName || undefined }, clientId, popup_mode: mode, lead_source: source, origem_site: location.hostname, page_url: location.href }); return true; } catch (_) { return false; } } async function swdgSendWebhook(cfg, data, mode = "default", options = null) { try { const wh = cfg && cfg.webhook ? cfg.webhook : null; if (!(wh && wh.enabled === true && wh.url)) return; const bhCtx = (typeof swdgGetBusinessContext !== "undefined") ? swdgGetBusinessContext(cfg) : null; const notify = (cfg && cfg.notifyEmail) ? cfg.notifyEmail : null; const notify_enabled = (notify && typeof notify.enabled === "boolean") ? notify.enabled : true; const email_to_prod = (notify && notify.toProd) ? String(notify.toProd) : ""; const email_to_test = (notify && notify.toTest) ? String(notify.toTest) : ""; const email_bcc = (notify && notify.bcc) ? String(notify.bcc) : ""; const email_test_mode = swdgIsUrlTestMode(); // ?teste=true NÃO muda o fluxo para "Caso Teste" no n8n. // Ele continua como produção e altera somente o roteamento do e-mail. const notify_effective_enabled = notify_enabled; let email_bcc_effective = email_bcc; let email_to = ((notify_effective_enabled ? email_to_prod : email_to_test) || email_to_test || email_to_prod || ""); if (email_test_mode) { // O Gmail exige um destinatário em "Para". Para não enviar ao principal // do cliente, promovemos o primeiro BCC para "Para" e mantemos os // demais como BCC. Se não houver BCC, usamos o e-mail de teste. const bccList = String(email_bcc || "") .split(/[;,]/) .map(v => v.trim()) .filter(Boolean); if (bccList.length) { email_to = bccList.shift(); email_bcc_effective = bccList.join(", " ); } else { email_to = email_to_test || ""; email_bcc_effective = ""; } } // Fallbacks para cliques const gclid = data.origem_clique || (typeof swdgAttributionValue !== "undefined" ? swdgAttributionValue(["gclid"]) : ""); const gbraid = data.origem_clique_tipo === "gbraid" ? data.origem_clique : (typeof swdgAttributionValue !== "undefined" ? swdgAttributionValue(["gbraid"]) : ""); const wbraid = data.origem_clique_tipo === "wbraid" ? data.origem_clique : (typeof swdgAttributionValue !== "undefined" ? swdgAttributionValue(["wbraid"]) : ""); let final_clique = data.origem_clique || gclid || gbraid || wbraid || ""; let final_tipo = data.origem_clique_tipo || (gclid ? "gclid" : (gbraid ? "gbraid" : (wbraid ? "wbraid" : ""))); const extraData = Object.fromEntries( Object.entries(data || {}).filter(([k]) => !String(k || "").startsWith("__")) ); const rawFlow = (data && data.flow && typeof data.flow === "object") ? data.flow : ((data && data.flow_answers && typeof data.flow_answers === "object") ? data.flow_answers : (data || {})); const compactFlow = Object.fromEntries( Object.entries(rawFlow || {}).filter(([k]) => { const key = String(k || ""); return !key.startsWith("__") && key !== "observacao"; }) ); const payload = { clientId, notify_enabled: notify_effective_enabled, email_test_mode, email_to, email_bcc: email_bcc_effective, business_context: (bhCtx && bhCtx.mode) ? bhCtx.mode : "open", ip_publico: SWDG_CACHED_IP, ip_fonte: SWDG_CACHED_IP ? "ipify" : "", ...extraData, nome: data.nome || "", whats: (typeof digitsOnly !== "undefined" ? digitsOnly(data.whats || "") : String(data.whats || "").replace(/\D/g, "")), whats_formatado: data.whats || "", email: data.email || "", accepted: true, origem_site: location.hostname, page_url: location.href, referrer: swdgAttributionReferrer(), device: (typeof deviceLabel !== "undefined" ? deviceLabel() : ""), datahora: (typeof nowBR !== "undefined" ? nowBR() : ""), campanha: data.campanha || (typeof swdgAttributionValue !== "undefined" ? swdgAttributionValue(["utm_campaign", "campaign", "campanha", "g_campaignid", "gad_campaignid", "gad_campaign_id"]) : ""), palavra: data.palavra || (typeof swdgAttributionValue !== "undefined" ? swdgAttributionValue(["utm_term", "keyword", "palavra"]) : ""), cidade: data.cidade || (typeof qsAny !== "undefined" ? qsAny(["locality", "cidade", "city"]) : ""), polo: (cfg && cfg.polo) ? String(cfg.polo) : "", popup_mode: mode, // O botão é o caminho padrão e mantém este campo vazio. // No servidor, somente a leitura efetiva do QR altera para "qr_code". forma_abertura_whatsapp: "", origem_clique: final_clique, origem_clique_tipo: final_tipo, info: (typeof buildInfo !== "undefined" ? buildInfo() : ""), flow: compactFlow, quero_desconto: !!data.quero_desconto, ir_whats_agora: !!data.ir_whats_agora }; if (options && options.payloadOnly === true) return payload; swdgPushLeadEvent({ nome: payload.nome, whats: payload.whats, mode: mode, source: "webhook", }); return fetch(wh.url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), keepalive: true, }).catch(() => { }); } catch (err) { console.warn("SWDG Webhook Error:", err); } } async function sendChatWebhook(cfg, vars) { return swdgSendWebhook(cfg, vars, "chat"); } function swdgQrEnabled() { return !!(CONFIG && CONFIG.whatsappQr && CONFIG.whatsappQr.enabled === true); } function swdgQrDesktop() { try { return !/Android|iPhone|iPad|iPod|IEMobile|Opera Mini/i.test(navigator.userAgent || "") && window.matchMedia("(min-width: 521px)").matches; } catch (_) { return false; } } function swdgQrEndpoint() { return baseUrl + "qr-handoff.php"; } function swdgQrStopPolling() { if (SWDG_QR_STATE.timer) clearInterval(SWDG_QR_STATE.timer); SWDG_QR_STATE.timer = null; } function swdgQrHide(root) { const card = root && $("#swdg_qr_option", root); if (card) card.hidden = true; } function swdgQrCancelPending() { if (SWDG_QR_STATE.prepareTimer) clearTimeout(SWDG_QR_STATE.prepareTimer); SWDG_QR_STATE.prepareTimer = null; swdgQrStopPolling(); const token = SWDG_QR_STATE.token; SWDG_QR_STATE.token = ""; SWDG_QR_STATE.fingerprint = ""; SWDG_QR_STATE.busy = false; SWDG_QR_STATE.leadData = null; if (!token) return; try { fetch(swdgQrEndpoint(), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "cancel", token }), keepalive: true, }).catch(() => {}); } catch (_) {} } function swdgQrLoadLibrary() { if (window.QRCode && typeof window.QRCode.toCanvas === "function") return Promise.resolve(window.QRCode); if (window.__SWDG_QR_LIBRARY_PROMISE__) return window.__SWDG_QR_LIBRARY_PROMISE__; window.__SWDG_QR_LIBRARY_PROMISE__ = new Promise((resolve, reject) => { const script = document.createElement("script"); script.src = baseUrl + "qrcode.min.js"; script.async = true; script.onload = () => (window.QRCode ? resolve(window.QRCode) : reject(new Error("QR indisponível"))); script.onerror = () => reject(new Error("QR indisponível")); document.head.appendChild(script); }); return window.__SWDG_QR_LIBRARY_PROMISE__; } function swdgQrCurrentData(root) { if (!root || !swdgQrEnabled() || !swdgQrDesktop()) return null; const elNome = $("#swdg_nome", root); const elWhats = $("#swdg_whats", root); const elCurso = $("#swdg_curso", root); const elEmail = $("#swdg_email", root); const elAcc = $("#swdg_acc", root); if (!elNome || !elWhats || !elCurso || !elAcc) return null; const nome = sanitizeNameFinal(elNome.value); const whats = String(elWhats.value || "").trim(); const curso = String(elCurso.value || "").trim(); const email = elEmail ? String(elEmail.value || "").trim() : ""; const hasCourses = Array.isArray(CONFIG.courses) && CONFIG.courses.length > 0; const courseRequired = (CONFIG.courseRequired !== false) && hasCourses; const emailCfg = CONFIG.email || {}; const emailOk = emailCfg.enabled !== true || (emailCfg.required !== true && email === "") || isValidEmail(email); const entryCfg = CONFIG.entryPopup || {}; const isEntry = SWDG_OPEN_MODE === "entry" && entryCfg.enabled === true; const desconto = $("#swdg_desconto", root); const goWhats = $("#swdg_goWhats", root); const descontoOk = !(isEntry && entryCfg.requireDiscountCheckbox === true) || !!(desconto && desconto.checked); if (nome.length < 2 || !isValidFullBRPhoneMasked(whats) || !elAcc.checked || !emailOk || !descontoOk) return null; if (courseRequired && (!curso || curso.includes("────"))) return null; const gclid = swdgAttributionValue(["gclid"]), gbraid = swdgAttributionValue(["gbraid"]), wbraid = swdgAttributionValue(["wbraid"]); const origem_clique = gclid || gbraid || wbraid || ""; const origem_clique_tipo = gclid ? "gclid" : (gbraid ? "gbraid" : (wbraid ? "wbraid" : "")); const qrIsCommercialFlow = !!( (root && root.getAttribute && root.getAttribute("data-swdg-ui-mode") === "flow") || (SWDG_FLOW_DATA && Object.keys(SWDG_FLOW_DATA).length > 0) ); const qrQualification = qrIsCommercialFlow ? swdgBuildCommercialQualification({ nome, curso }, SWDG_FLOW_DATA) : null; return { nome, whats, curso, email, campanha: swdgAttributionValue(["utm_campaign", "campaign", "campanha"]), palavra: swdgAttributionValue(["utm_term", "keyword", "termo", "palavra"]), cidade: qsAny(["loc_fisica", "locality", "loc", "cidade", "city"]), origem_clique, origem_clique_tipo, visitor_id: (window.SWDG_VISITOR && window.SWDG_VISITOR.vid) ? String(window.SWDG_VISITOR.vid) : "", is_returning: !!(window.SWDG_VISITOR && window.SWDG_VISITOR.isReturning === true), visit_count: (window.SWDG_VISITOR && window.SWDG_VISITOR.visitCount != null) ? Number(window.SWDG_VISITOR.visitCount) : 0, within_window: !!(window.SWDG_VISITOR && window.SWDG_VISITOR.withinWindow === true), flow_step: SWDG_FLOW_CURRENT_STEP || "", flow_answers: { ...SWDG_FLOW_DATA }, ...(qrQualification && qrQualification.commercial && qrQualification.observacao ? { observacao: qrQualification.observacao } : {}), __commercial_qualification: !!(qrQualification && qrQualification.commercial), quero_desconto: !!(desconto && desconto.checked), ir_whats_agora: !!(goWhats && goWhats.checked), }; } function swdgQrToast() { const old = document.getElementById("swdg_qr_success_toast"); if (old) old.remove(); const toast = document.createElement("div"); toast.id = "swdg_qr_success_toast"; toast.textContent = "WhatsApp aberto no celular com sucesso."; toast.style.cssText = "position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:1000001;padding:13px 18px;border-radius:14px;background:#132238;color:#fff;font:600 13px Inter,Arial,sans-serif;box-shadow:0 16px 42px rgba(0,0,0,.28);border:1px solid rgba(255,255,255,.13)"; document.body.appendChild(toast); setTimeout(() => toast.remove(), 4500); } function swdgQrStartPolling(root, token) { swdgQrStopPolling(); SWDG_QR_STATE.timer = setInterval(async () => { try { const response = await fetch(swdgQrEndpoint(), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "status", token }), cache: "no-store", }); if (!response.ok) return; const result = await response.json(); if (result.status !== "qr_scanned") return; swdgQrStopPolling(); try { const lead = SWDG_QR_STATE.leadData || {}; swdgPushLeadEvent({ nome: lead.nome, whats: lead.whats, mode: SWDG_OPEN_MODE, source: "qr_code" }); window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "swdg_whatsapp_qr", clientId, popup_mode: SWDG_OPEN_MODE }); } catch (_) {} SWDG_QR_STATE.token = ""; closePopup(); swdgQrToast(); } catch (_) {} }, 1500); } async function swdgQrPrepare(root, data) { if (!data || SWDG_QR_STATE.busy) return; const fingerprint = JSON.stringify([data.nome, data.whats, data.curso, data.email, data.origem_clique, data.flow_answers]); if (SWDG_QR_STATE.token && SWDG_QR_STATE.fingerprint === fingerprint) return; if (SWDG_QR_STATE.token) swdgQrCancelPending(); SWDG_QR_STATE.busy = true; const card = $("#swdg_qr_option", root); const loading = $("#swdg_qr_loading", root); try { if (card) card.hidden = false; if (loading) loading.textContent = "Preparando QR seguro…"; const payload = await swdgSendWebhook(CONFIG, data, SWDG_OPEN_MODE, { payloadOnly: true }); if (!payload) throw new Error("Webhook não configurado"); const message = swdgBuildWhatsMessageFromLead(data); const response = await fetch(swdgQrEndpoint(), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "prepare", clientId, payload, message, testMode: swdgWhatsappRouterTestMode() }), cache: "no-store", }); const result = await response.json(); if (!response.ok || !result.ok) throw new Error(result.error || "QR indisponível"); const latestData = swdgQrCurrentData(root); const latestFingerprint = latestData ? JSON.stringify([latestData.nome, latestData.whats, latestData.curso, latestData.email, latestData.origem_clique, latestData.flow_answers]) : ""; if (!latestData || latestFingerprint !== fingerprint) { fetch(swdgQrEndpoint(), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "cancel", token: result.token }), keepalive: true, }).catch(() => {}); throw new Error("Dados alterados durante a preparação"); } await swdgQrLoadLibrary(); const canvas = $("#swdg_qr_canvas", root); if (!canvas) throw new Error("QR indisponível"); await window.QRCode.toCanvas(canvas, result.handoffUrl, { width: 114, margin: 1, errorCorrectionLevel: "M", color: { dark: "#10243b", light: "#ffffff" }, }); SWDG_QR_STATE.token = result.token; SWDG_QR_STATE.fingerprint = fingerprint; SWDG_QR_STATE.leadData = data; if (loading) loading.textContent = "QR válido por 30 minutos."; swdgQrStartPolling(root, result.token); } catch (_) { swdgQrHide(root); } finally { SWDG_QR_STATE.busy = false; const latest = swdgQrCurrentData(root); if (latest) { const latestFingerprint = JSON.stringify([latest.nome, latest.whats, latest.curso, latest.email, latest.origem_clique, latest.flow_answers]); if (latestFingerprint !== fingerprint) swdgQrSchedule(root); } } } function swdgQrSchedule(root) { if (SWDG_QR_STATE.prepareTimer) clearTimeout(SWDG_QR_STATE.prepareTimer); SWDG_QR_STATE.prepareTimer = setTimeout(() => { const data = swdgQrCurrentData(root); if (!data) { swdgQrHide(root); if (SWDG_QR_STATE.token) swdgQrCancelPending(); return; } swdgQrPrepare(root, data); }, 350); } function swdgQrBindForm(root) { const form = root && $("#whatsPopupForm", root); if (!form || form.dataset.swdgQrBound === "1") return; form.dataset.swdgQrBound = "1"; form.addEventListener("input", () => swdgQrSchedule(root)); form.addEventListener("change", () => swdgQrSchedule(root)); swdgQrSchedule(root); } // ========= Badge (imagem/selo) ========= function swdgMountBadge(root, cfg) { try { const b = cfg && cfg.badge ? cfg.badge : null; if (!b || b.enabled !== true) return; const mode = String(b.mode || "image").toLowerCase(); const src = String(b.src || "").trim(); if (mode !== "image" || !src) return; const modal = root.querySelector(".swdg-modal"); if (!modal) return; // evita duplicar if (modal.querySelector(".swdg-badge")) return; const pos = String(b.position || "top-left").toLowerCase(); const sizeKey = String(b.size || "lg").toLowerCase(); const sizeMap = { xs: 88, sm: 108, md: 128, lg: 156, xl: 184 }; const px = sizeMap[sizeKey] || 156; const wrap = document.createElement("div"); wrap.className = "swdg-badge swdg-badge--" + (["top-left", "top-right", "bottom-left", "bottom-right"].includes(pos) ? pos : "top-left"); wrap.style.setProperty("--swdg-badge-size", px + "px"); wrap.style.setProperty("--swdg-badge-offset", "-18px"); const img = document.createElement("img"); img.src = src; img.alt = String(b.alt || "").trim() || "Selo"; img.loading = "lazy"; wrap.appendChild(img); modal.appendChild(wrap); } catch (_) { } } const SWDG_FLOW_V22_CSS = ` .swdg-flow-intro-consent{ display:flex !important; align-items:flex-start !important; gap:10px !important; margin: 10px 0 4px !important; padding: 12px 14px !important; border-radius: 16px !important; border:1px solid rgba(255,255,255,.12) !important; background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.045)) !important; color: rgba(245,250,255,.92) !important; font-size: 14px !important; line-height: 1.45 !important; } .swdg-flow-intro-consent > span:last-child{ color: rgba(245,250,255,.92) !important; } .swdg-flow-intro-consent.is-error{ border-color: rgba(255,120,120,.55) !important; background: linear-gradient(180deg, rgba(255,100,100,.18), rgba(255,100,100,.10)) !important; box-shadow: 0 0 0 3px rgba(255,100,100,.10) !important; } .swdg-flow-consent-alert{ margin-top: 8px !important; font-size: 12px !important; line-height: 1.35 !important; color: rgba(255,214,214,.96) !important; display:none; } .swdg-flow-consent-alert.is-visible{ display:block !important; } .swdg-btn-ghost{ background: transparent !important; border: 0 !important; box-shadow: none !important; color: rgba(255,255,255,.82) !important; padding: 0 !important; min-height: auto !important; height: auto !important; margin: 14px auto 0 !important; display:block !important; width: auto !important; } .swdg-btn-ghost:hover{ background: transparent !important; color: #fff !important; transform: none !important; box-shadow: none !important; } #whatsPopupForm.swdg-flow-form-active{ display:flex !important; flex-direction:column !important; gap: 0 !important; } #whatsPopupForm.swdg-flow-form-active .swdg-flow-form-shell{ display:flex !important; flex-direction:column !important; gap: 14px !important; } #whatsPopupForm.swdg-flow-form-active .swdg-flow-form-shell > *{ margin: 0 !important; } #whatsPopupForm.swdg-flow-form-active input, #whatsPopupForm.swdg-flow-form-active textarea{ color: rgba(245,250,255,.96) !important; -webkit-text-fill-color: rgba(245,250,255,.96) !important; caret-color: rgba(245,250,255,.96) !important; background: linear-gradient(180deg, rgba(255,255,255,.15), rgba(255,255,255,.09)) !important; border-color: rgba(255,255,255,.20) !important; font-size: 15px !important; } #whatsPopupForm.swdg-flow-form-active input::placeholder, #whatsPopupForm.swdg-flow-form-active textarea::placeholder{ color: rgba(245,250,255,.56) !important; -webkit-text-fill-color: rgba(245,250,255,.56) !important; } #whatsPopupForm.swdg-flow-form-active input:focus, #whatsPopupForm.swdg-flow-form-active textarea:focus{ color: rgba(255,255,255,.98) !important; -webkit-text-fill-color: rgba(255,255,255,.98) !important; } #whatsPopupForm.swdg-flow-form-active .swdg-liveConfirm{ margin-top: 0 !important; } #whatsPopupForm.swdg-flow-form-active .swdg-btn[type="submit"]{ margin-top: 6px !important; } @media (max-width:560px){ .swdg-flow-intro-consent{ font-size: 13px !important; padding: 11px 12px !important; } } /* ========================================================= VISUAL CLEAN — sólido, minimalista e com contraste calculado As variáveis são definidas por swdgApplyTheme(). ========================================================= */ :host([data-swdg-visual="clean"]) .swdg-overlay::before{ background:rgba(3,8,16,.52) !important; backdrop-filter:blur(5px) !important; -webkit-backdrop-filter:blur(5px) !important; } :host([data-swdg-visual="clean"]) .swdg-modal{ background:var(--swdg-clean-bg,#f5f7fb) !important; border:1px solid var(--swdg-clean-border,#cbd5e1) !important; border-radius:20px !important; backdrop-filter:none !important; -webkit-backdrop-filter:none !important; box-shadow:0 24px 70px rgba(2,8,23,.28),0 4px 16px rgba(2,8,23,.12) !important; } :host([data-swdg-visual="clean"]) .swdg-modal::before{ left:18px !important; right:18px !important; top:-1px !important; height:3px !important; border-radius:999px !important; background:var(--swdg-textAccent,#2563eb) !important; box-shadow:0 1px 5px color-mix(in srgb, var(--swdg-textAccent,#2563eb) 28%, transparent) !important; } :host([data-swdg-visual="clean"]) .swdg-modal::after{ box-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-header{ background:transparent !important; border-bottom:1px solid var(--swdg-clean-border,#cbd5e1) !important; } :host([data-swdg-visual="clean"]) .swdg-logoImg{ filter:none !important; } :host([data-swdg-visual="clean"]) .swdg-brandName, :host([data-swdg-visual="clean"]) .swdg-title, :host([data-swdg-visual="clean"]) .swdg-flow-question{ color:var(--swdg-clean-text,#111827) !important; text-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-sub, :host([data-swdg-visual="clean"]) .swdg-flow-kicker, :host([data-swdg-visual="clean"]) .swdg-qr-copy small{ color:var(--swdg-textAccent,#2563eb) !important; text-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-flow-copy, :host([data-swdg-visual="clean"]) .swdg-flow-stepCount, :host([data-swdg-visual="clean"]) .swdg-status, :host([data-swdg-visual="clean"]) .swdg-qr-copy{ color:var(--swdg-clean-muted,var(--swdg-clean-text,#111827)) !important; -webkit-text-fill-color:currentColor !important; } :host([data-swdg-visual="clean"]) .swdg-close{ background:var(--swdg-clean-surface,#fff) !important; color:var(--swdg-clean-text,#111827) !important; border-color:var(--swdg-clean-border,#cbd5e1) !important; box-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-close:hover{ filter:brightness(.97) !important; box-shadow:0 6px 18px rgba(2,8,23,.10) !important; } :host([data-swdg-visual="clean"]) .swdg-form input[type="text"], :host([data-swdg-visual="clean"]) .swdg-form input[type="tel"], :host([data-swdg-visual="clean"]) .swdg-form input[type="email"], :host([data-swdg-visual="clean"]) .swdg-form textarea, :host([data-swdg-visual="clean"]) .swdg-form select, :host([data-swdg-visual="clean"]) #whatsPopupForm.swdg-flow-form-active input, :host([data-swdg-visual="clean"]) #whatsPopupForm.swdg-flow-form-active textarea, :host([data-swdg-visual="clean"]) #whatsPopupForm.swdg-flow-form-active select{ background:var(--swdg-clean-input-bg,#fff) !important; background-image:none !important; color:var(--swdg-clean-input-text,#111827) !important; -webkit-text-fill-color:var(--swdg-clean-input-text,#111827) !important; caret-color:var(--swdg-clean-input-text,#111827) !important; border:1px solid var(--swdg-clean-border,#cbd5e1) !important; border-radius:13px !important; box-shadow:none !important; text-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-form select option{ background:var(--swdg-clean-input-bg,#fff) !important; color:var(--swdg-clean-input-text,#111827) !important; } :host([data-swdg-visual="clean"]) .swdg-form input::placeholder, :host([data-swdg-visual="clean"]) .swdg-form textarea::placeholder, :host([data-swdg-visual="clean"]) #whatsPopupForm.swdg-flow-form-active input::placeholder, :host([data-swdg-visual="clean"]) #whatsPopupForm.swdg-flow-form-active textarea::placeholder{ color:var(--swdg-clean-placeholder,#64748b) !important; -webkit-text-fill-color:var(--swdg-clean-placeholder,#64748b) !important; opacity:1 !important; } :host([data-swdg-visual="clean"]) .swdg-form input:focus, :host([data-swdg-visual="clean"]) .swdg-form textarea:focus, :host([data-swdg-visual="clean"]) .swdg-form select:focus{ border-color:var(--swdg-textAccent,#2563eb) !important; box-shadow:0 0 0 3px color-mix(in srgb,var(--swdg-textAccent,#2563eb) 20%,transparent) !important; transform:none !important; } :host([data-swdg-visual="clean"]) .swdg-consent, :host([data-swdg-visual="clean"]) .swdg-flow-intro-consent, :host([data-swdg-visual="clean"]) .swdg-qr-option, :host([data-swdg-visual="clean"]) .swdg-ddd-hint{ background:var(--swdg-clean-surface,#fff) !important; color:var(--swdg-clean-text,#111827) !important; border-color:var(--swdg-clean-border,#cbd5e1) !important; box-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-flow-intro-consent > span:last-child{ color:var(--swdg-clean-text,#111827) !important; } :host([data-swdg-visual="clean"]) .swdg-checkUi{ background:var(--swdg-clean-input-bg,#fff) !important; border-color:var(--swdg-clean-border,#cbd5e1) !important; } :host([data-swdg-visual="clean"]) .swdg-checkWrap input:checked + .swdg-checkUi{ background:var(--swdg-textAccent,#2563eb) !important; border-color:var(--swdg-textAccent,#2563eb) !important; box-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-flow-progressBar{ background:var(--swdg-clean-surface,#fff) !important; border-color:var(--swdg-clean-border,#cbd5e1) !important; } :host([data-swdg-visual="clean"]) .swdg-flow-progressFill{ background:var(--swdg-textAccent,#2563eb) !important; box-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-btn, :host([data-swdg-visual="clean"]) .swdg-flow-btn-primary{ background:var(--swdg-button,#2563eb) !important; color:var(--swdg-buttonText,#fff) !important; -webkit-text-fill-color:var(--swdg-buttonText,#fff) !important; border:1px solid color-mix(in srgb,var(--swdg-button,#2563eb) 76%,var(--swdg-clean-text,#111827) 24%) !important; border-radius:14px !important; box-shadow:0 9px 22px rgba(2,8,23,.15) !important; } :host([data-swdg-visual="clean"]) .swdg-btn::before{ display:none !important; } :host([data-swdg-visual="clean"]) .swdg-btn:hover, :host([data-swdg-visual="clean"]) .swdg-flow-btn-primary:hover{ background:var(--swdg-buttonHover,var(--swdg-button,#2563eb)) !important; filter:none !important; box-shadow:0 12px 26px rgba(2,8,23,.18) !important; } :host([data-swdg-visual="clean"]) .swdg-btn-secondary{ background:var(--swdg-clean-surface,#fff) !important; color:var(--swdg-clean-text,#111827) !important; -webkit-text-fill-color:var(--swdg-clean-text,#111827) !important; border-color:var(--swdg-clean-border,#cbd5e1) !important; box-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-btn-ghost, :host([data-swdg-visual="clean"]) .swdg-btn-ghost:hover{ background:transparent !important; color:var(--swdg-clean-muted,var(--swdg-clean-text,#111827)) !important; -webkit-text-fill-color:currentColor !important; border:0 !important; box-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-qr-canvasWrap{ border:1px solid var(--swdg-clean-border,#cbd5e1) !important; box-shadow:none !important; } /* Autofill do Clean: fundo e texto sempre seguem as variáveis calculadas. */ :host([data-swdg-visual="clean"]) .swdg-form input:-webkit-autofill, :host([data-swdg-visual="clean"]) .swdg-form input:-webkit-autofill:hover, :host([data-swdg-visual="clean"]) .swdg-form input:-webkit-autofill:focus, :host([data-swdg-visual="clean"]) .swdg-form input:-webkit-autofill:active, :host([data-swdg-visual="clean"]) #whatsPopupForm.swdg-flow-form-active input:-webkit-autofill, :host([data-swdg-visual="clean"]) #whatsPopupForm.swdg-flow-form-active input:-webkit-autofill:hover, :host([data-swdg-visual="clean"]) #whatsPopupForm.swdg-flow-form-active input:-webkit-autofill:focus, :host([data-swdg-visual="clean"]) #whatsPopupForm.swdg-flow-form-active input:-webkit-autofill:active{ color:var(--swdg-clean-input-text,#111827) !important; -webkit-text-fill-color:var(--swdg-clean-input-text,#111827) !important; caret-color:var(--swdg-clean-input-text,#111827) !important; background-color:var(--swdg-clean-input-bg,#fff) !important; -webkit-box-shadow:0 0 0 1000px var(--swdg-clean-input-bg,#fff) inset !important; box-shadow:0 0 0 1000px var(--swdg-clean-input-bg,#fff) inset !important; border-color:var(--swdg-clean-border,#cbd5e1) !important; text-shadow:none !important; } :host([data-swdg-visual="clean"]) .swdg-form input:autofill{ color:var(--swdg-clean-input-text,#111827) !important; -webkit-text-fill-color:var(--swdg-clean-input-text,#111827) !important; caret-color:var(--swdg-clean-input-text,#111827) !important; background:var(--swdg-clean-input-bg,#fff) !important; } `; function mountUI() { if (mounted) return; // [IP] tenta aquecer o cache do IP o quanto antes try { swdgPrefetchIP(); } catch (_) { } const host = document.createElement("div"); host.id = ROOT_ID; host.setAttribute("data-swdg-host", "1"); const shadow = host.attachShadow({ mode: "open" }); const root = document.createElement("div"); root.id = ROOT_ID; SWDG_POPUP_HOST = host; SWDG_POPUP_SHADOW = shadow; injectStyles(shadow); try { const __flowV22Style = document.createElement('style'); __flowV22Style.id = 'swdg-flow-v22-shadow'; __flowV22Style.textContent = SWDG_FLOW_V22_CSS; shadow.appendChild(__flowV22Style); } catch (_) { } // Theme via JSON (opcional) try { swdgApplyTheme(host, CONFIG); } catch (_) { } // ✅ FORM REAL com names form_fields[...] (GTM/planilha reconhece) root.innerHTML = ` `; if (!SWDG_FLOW_ORIGINAL_FORM_HTML) { const __f = $("#whatsPopupForm", root); if (__f) SWDG_FLOW_ORIGINAL_FORM_HTML = __f.innerHTML; } // Badge (opcional via JSON) try { swdgMountBadge(root, CONFIG); } catch (_) { } shadow.appendChild(root); document.body.appendChild(host); mounted = true; // Fechar const overlay = $(".swdg-overlay", root); const closeBtn = $(".swdg-close", root); closeBtn.addEventListener("click", () => closePopup()); overlay.addEventListener("click", (e) => { if (e.target === overlay) closePopup(); }); document.addEventListener("keydown", (e) => { if (e.key === "Escape" && overlay.getAttribute("aria-hidden") === "false") closePopup(); }); // Inputs const elNome = $("#swdg_nome", root); const elWhats = $("#swdg_whats", root); const elEmailLive = $("#swdg_email", root); const elLiveConfirm = $("#swdg_liveConfirm", root); const elLiveConfirmValue = $("#swdg_liveConfirm_value", root); const elLiveConfirmStudentLink = $("#swdg_liveConfirm_studentLink", root); function updateLiveConfirm() { if (!elLiveConfirm || !elLiveConfirmValue) return; const nome = sanitizeNameFinal(elNome ? elNome.value : ""); const whats = String(elWhats && elWhats.value ? elWhats.value : "").trim(); const email = String(elEmailLive && elEmailLive.value ? elEmailLive.value : "").trim(); const parts = []; if (nome) parts.push(nome); if (whats) parts.push(whats); if (email) parts.push(email); const alunoDestino = String((CONFIG && (CONFIG.whatsAlunoDestino || CONFIG.studentWhatsDestino || CONFIG.whatsPedagogicoDestino)) || "").replace(/\D+/g, ""); const alunoLabel = String((CONFIG && (CONFIG.whatsAlunoLabel || CONFIG.studentWhatsLabel || CONFIG.whatsPedagogicoLabel)) || "Se você já é aluno, clique aqui.").trim(); if (!parts.length) { elLiveConfirm.style.display = "none"; elLiveConfirmValue.textContent = ""; if (elLiveConfirmStudentLink) { elLiveConfirmStudentLink.hidden = true; elLiveConfirmStudentLink.removeAttribute("href"); } return; } elLiveConfirmValue.textContent = parts.join(" • "); elLiveConfirm.style.display = "block"; if (elLiveConfirmStudentLink) { if (alunoDestino) { const emailLinha = email ? `\nE-mail: ${email}` : ""; const whatsLinha = whats ? `\nWhats: ${whats}` : ""; const nomeLinha = nome ? `\nNome: ${nome}` : ""; const msgAluno = encodeURIComponent(`Olá! Já sou aluno e preciso de atendimento.${nomeLinha}${whatsLinha}${emailLinha}`); elLiveConfirmStudentLink.href = `https://wa.me/${alunoDestino}?text=${msgAluno}`; elLiveConfirmStudentLink.textContent = alunoLabel || "Se você já é aluno, clique aqui."; elLiveConfirmStudentLink.hidden = false; } else { elLiveConfirmStudentLink.hidden = true; elLiveConfirmStudentLink.removeAttribute("href"); } } } elNome.addEventListener("input", () => { const fixed = sanitizeNameLive(elNome.value); if (fixed !== elNome.value) { const pos = elNome.selectionStart || 0; elNome.value = fixed; try { elNome.setSelectionRange(pos, pos); } catch (_) { } } updateLiveConfirm(); }); elWhats.addEventListener("input", () => { applyPhoneMask(elWhats); updateLiveConfirm(); }); elEmailLive && elEmailLive.addEventListener("input", updateLiveConfirm); updateLiveConfirm(); const elDDDHint = $("#swdg_ddd_hint", root); const elDDDHintText = $("#swdg_ddd_hint_text", root); const btnKeep = $("#swdg_ddd_keep", root); const btnSwap = $("#swdg_ddd_swap", root); function hideDDDHints() { if (elDDDHint) elDDDHint.style.display = "none"; } function showDDDHints(currentDDD, suggestedDDD, geo) { if (!elDDDHint || !elDDDHintText || SWDG_DDD_HINT_DISMISSED) return; const city = (geo && geo.city) ? String(geo.city) : ""; const uf = (geo && geo.region) ? String(geo.region) : ""; const loc = (city && uf) ? (city + "/" + uf) : (city || uf || "sua região"); elDDDHintText.textContent = `Você está em ${loc}? O DDD aí costuma ser (${suggestedDDD}).`; btnKeep.textContent = `Manter (${currentDDD})`; btnSwap.textContent = `Trocar para (${suggestedDDD})`; elDDDHint.style.display = "flex"; } function updateDDDHints() { if (SWDG_DDD_HINT_DISMISSED) return; const digits = normalizePhoneDigits(elWhats.value); if (digits.length !== 11) { hideDDDHints(); return; } const currentDDD = digits.slice(0, 2); swdgPrefetchSuggestedDDD().then((suggested) => { if (!suggested || suggested.length !== 2) { hideDDDHints(); return; } if (currentDDD === suggested) { hideDDDHints(); return; } return swdgPrefetchGeo().then((geo) => { showDDDHints(currentDDD, suggested, geo); }); }); } btnKeep && btnKeep.addEventListener("click", () => { SWDG_DDD_HINT_DISMISSED = true; hideDDDHints(); }); btnSwap && btnSwap.addEventListener("click", () => { const suggested = SWDG_SUGGESTED_DDD; if (!suggested || suggested.length !== 2) return; const digits = normalizePhoneDigits(elWhats.value); if (digits.length !== 11) return; const swapped = suggested + digits.slice(2); elWhats.value = swapped; applyPhoneMask(elWhats); SWDG_DDD_HINT_DISMISSED = true; hideDDDHints(); }); elWhats.addEventListener("input", () => updateDDDHints()); // ✅ Blindagem em CAPTURE: no Shadow DOM o listener precisa ficar no próprio form const form = $("#whatsPopupForm", root); form && form.addEventListener("submit", onSubmitCapture, true); swdgQrBindForm(root); } function closePopup() { const root = getPopupRoot(); if (!root) return; $(".swdg-overlay", root).setAttribute("aria-hidden", "true"); swdgQrCancelPending(); try { document.activeElement && document.activeElement.blur && document.activeElement.blur(); } catch (_) { } try { const h = String(location.hash || "").toLowerCase(); if (h === "#swdg-popup" || h.indexOf("#swdg-popup=") === 0 || h === "#swdg-form" || h.indexOf("#swdg-form=") === 0 || h === "#swdg-flow" || h.indexOf("#swdg-flow=") === 0 || h === "#swdg-chat") { history.replaceState({}, "", location.pathname + location.search); } } catch (_) { } } function clearErrors(root) { ["#swdg_nome", "#swdg_whats", "#swdg_email", "#swdg_curso"].forEach((id) => { const el = $(id, root); el && el.classList.remove("swdg-error"); }); } function setError(el) { el && el.classList.add("swdg-error"); } function setField(root, name, value) { const el = root.querySelector(`[name="${name}"]`); if (!el) return; // vazio se vier null/undefined/"undefined"/"-" el.value = (value == null || value === "-" || value === "undefined") ? "" : String(value); } function swdgFlowEscape(v) { return String(v == null ? "" : v) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function swdgResolveFlowStep(flowCfg, stepId) { const steps = Array.isArray(flowCfg && flowCfg.steps) ? flowCfg.steps : []; return steps.find((s) => s && s.id === stepId) || null; } function swdgFlowResolveWhats(value) { const key = String(value || "").trim().toLowerCase(); const direct = String(value || "").replace(/\D+/g, ""); if (direct) return direct; const map = { pedagogico: (CONFIG && (CONFIG.whatsPedagogicoDestino || CONFIG.studentWhatsDestino || CONFIG.whatsAlunoDestino)) || "", financeiro: (CONFIG && (CONFIG.whatsFinanceiroDestino || CONFIG.financeiroWhatsDestino)) || "", comercial: (CONFIG && (CONFIG.whatsDestino || CONFIG.whatsapp || CONFIG.phone)) || "", default: (CONFIG && (CONFIG.whatsDestino || CONFIG.whatsapp || CONFIG.phone)) || "" }; return String(map[key] || map.default || "").replace(/\D+/g, ""); } function swdgAttachCurrentFormEnhancements(root) { const elNome = $("#swdg_nome", root); const elWhats = $("#swdg_whats", root); const elEmailLive = $("#swdg_email", root); const elLiveConfirm = $("#swdg_liveConfirm", root); const elLiveConfirmValue = $("#swdg_liveConfirm_value", root); const elLiveConfirmStudentLink = $("#swdg_liveConfirm_studentLink", root); const emailWrap = $("#swdg_email_wrap", root); const courseWrap = $("#swdg_course_wrap", root); const courseSel = $("#swdg_curso", root); // Reaplica a lógica real do formulário tradicional dentro do Flow const emailCfg = (CONFIG && CONFIG.email) ? CONFIG.email : null; const emailEnabled = !!(emailCfg && emailCfg.enabled === true); if (emailWrap && elEmailLive) { if (emailEnabled) { emailWrap.style.display = "block"; elEmailLive.required = !!emailCfg.required; elEmailLive.placeholder = emailCfg.placeholder || "Seu e-mail (opcional)"; } else { emailWrap.style.display = "none"; elEmailLive.required = false; elEmailLive.value = ""; } } const hasCourses = Array.isArray(CONFIG && CONFIG.courses) && CONFIG.courses.length > 0; const mustChooseCourse = (CONFIG && CONFIG.courseRequired !== false) && hasCourses; const courseFromUrl = swdgGetCourseFromUrl(); if (courseSel) { if (!hasCourses) { if (courseWrap) courseWrap.style.display = "none"; if (courseSel) courseSel.required = false; if (courseSel) { const fallbackCourse = courseFromUrl || ""; const fallbackLabel = fallbackCourse || "Curso (não informado)"; courseSel.innerHTML = ``; courseSel.value = fallbackCourse; } } else { if (courseWrap) courseWrap.style.display = "block"; if (courseSel) courseSel.required = mustChooseCourse; if (courseSel) courseSel.innerHTML = ``; (CONFIG.courses || []).forEach((c) => { const opt = document.createElement("option"); opt.textContent = c; if (String(c).includes("────")) { opt.value = ""; opt.disabled = true; opt.style.fontWeight = "800"; opt.style.backgroundColor = "#f3f3f3"; } else { opt.value = c; } if (courseSel) courseSel.appendChild(opt); }); if (courseSel) courseSel.value = ""; } } if (!elNome || !elWhats) return; function updateLiveConfirm() { if (!elLiveConfirm || !elLiveConfirmValue) return; const nome = sanitizeNameFinal(elNome ? elNome.value : ""); const whats = String(elWhats && elWhats.value ? elWhats.value : "").trim(); const email = String(elEmailLive && elEmailLive.value ? elEmailLive.value : "").trim(); const parts = []; if (nome) parts.push(nome); if (whats) parts.push(whats); if (email) parts.push(email); const alunoDestino = String((CONFIG && (CONFIG.whatsAlunoDestino || CONFIG.studentWhatsDestino || CONFIG.whatsPedagogicoDestino)) || "").replace(/\D+/g, ""); const alunoLabel = String((CONFIG && (CONFIG.whatsAlunoLabel || CONFIG.studentWhatsLabel || CONFIG.whatsPedagogicoLabel)) || "Se você já é aluno, clique aqui.").trim(); if (!parts.length) { elLiveConfirm.style.display = "none"; elLiveConfirmValue.textContent = ""; if (elLiveConfirmStudentLink) { elLiveConfirmStudentLink.hidden = true; elLiveConfirmStudentLink.removeAttribute("href"); } return; } elLiveConfirmValue.textContent = parts.join(" • "); elLiveConfirm.style.display = "block"; if (elLiveConfirmStudentLink) { if (alunoDestino) { const emailLinha = email ? `\nE-mail: ${email}` : ""; const whatsLinha = whats ? `\nWhats: ${whats}` : ""; const nomeLinha = nome ? `\nNome: ${nome}` : ""; const msgAluno = encodeURIComponent(`Olá! Já sou aluno e preciso de atendimento.${nomeLinha}${whatsLinha}${emailLinha}`); elLiveConfirmStudentLink.href = `https://wa.me/${alunoDestino}?text=${msgAluno}`; elLiveConfirmStudentLink.textContent = alunoLabel || "Se você já é aluno, clique aqui."; elLiveConfirmStudentLink.hidden = false; } else { elLiveConfirmStudentLink.hidden = true; elLiveConfirmStudentLink.removeAttribute("href"); } } } if (!elNome.dataset.swdgBound) { elNome.addEventListener("input", () => { const fixed = sanitizeNameLive(elNome.value); if (fixed !== elNome.value) { const pos = elNome.selectionStart || 0; elNome.value = fixed; try { elNome.setSelectionRange(pos, pos); } catch (_) { } } updateLiveConfirm(); }); elNome.dataset.swdgBound = "1"; } if (!elWhats.dataset.swdgBound) { elWhats.addEventListener("input", () => { applyPhoneMask(elWhats); updateLiveConfirm(); }); elWhats.dataset.swdgBound = "1"; } if (elEmailLive && !elEmailLive.dataset.swdgBound) { elEmailLive.addEventListener("input", updateLiveConfirm); elEmailLive.dataset.swdgBound = "1"; } updateLiveConfirm(); swdgQrBindForm(root); } function swdgGetFlowSteps(flowCfg) { return (flowCfg && Array.isArray(flowCfg.steps)) ? flowCfg.steps : []; } function swdgGetFlowStepIndex(flowCfg, stepId) { const steps = swdgGetFlowSteps(flowCfg); return Math.max(0, steps.findIndex((s) => String((s && s.id) || "") === String(stepId || ""))); } function swdgGetFlowProgressMeta(flowCfg, stepId) { const steps = swdgGetFlowSteps(flowCfg); const visualSteps = steps.filter((s) => String((s && s.type) || "") !== "message"); const current = swdgResolveFlowStep(flowCfg, stepId); const currentId = String((current && current.id) || stepId || ""); let idx = visualSteps.findIndex((s) => String((s && s.id) || "") === currentId); if (idx < 0) idx = 0; const total = Math.max(visualSteps.length, 1); const currentNumber = Math.min(idx + 1, total); const percent = Math.max(12, Math.min(100, Math.round((currentNumber / total) * 100))); return { currentNumber, total, percent }; } function swdgFlowPreferredOptionIndex(step, opts) { const list = Array.isArray(opts) ? opts : []; if (!list.length) return 0; if (typeof step.primaryIndex === "number" && list[step.primaryIndex]) return step.primaryIndex; const key = String(step.key || step.id || "").toLowerCase(); const score = (opt) => { const txt = String((opt && (opt.value || opt.label)) || "").toLowerCase(); if (key.includes("ja_e_aluno")) { if (txt.includes("nao") || txt.includes("não")) return 10; if (txt.includes("sim")) return 1; } if (key.includes("modalidade") || key.includes("ead") || key.includes("presencial")) { if (txt.includes("continuar") || txt.includes("prosseguir") || txt.includes("entendo") || txt == 'sim' || txt.startsWith('sim,')) return 10; if (txt.includes("não") || txt.includes("nao")) return 1; } return 0; }; let best = 0, bestScore = -1; list.forEach((opt, idx) => { const s = score(opt); if (s > bestScore) { bestScore = s; best = idx; } }); return best; } function swdgFlowButtonClass(step, btn, idx, preferredIdx) { const mode = String((btn && btn.style) || "").toLowerCase(); if (mode === "primary") return "swdg-btn swdg-flow-btn-primary"; if (mode === "secondary" || mode === "ghost") return "swdg-btn swdg-btn-secondary"; if (typeof preferredIdx === "number") return `swdg-btn ${idx === preferredIdx ? 'swdg-flow-btn-primary' : 'swdg-btn-secondary'}`; return "swdg-btn"; } function swdgRenderFlowMeta(flowCfg, step) { const meta = swdgGetFlowProgressMeta(flowCfg, step && step.id); const kicker = swdgFlowEscape(step && step.kicker ? step.kicker : 'Atendimento via WhatsApp'); return `
${kicker}
Etapa ${meta.currentNumber} de ${meta.total}
`; } function swdgRenderFlowStep(root, flowCfg, stepId) { const form = $("#whatsPopupForm", root); const statusEl = $(".swdg-status", root); if (!form) return; if (statusEl) statusEl.textContent = ""; const step = swdgResolveFlowStep(flowCfg, stepId); if (!step) { form.innerHTML = `
Etapa do fluxo não encontrada.
`; return; } SWDG_FLOW_CURRENT_STEP = step.id || ""; swdgFlowSaveSession(SWDG_FLOW_CURRENT_STEP); form.classList.add("swdg-flow-mode"); const showBack = SWDG_FLOW_HISTORY.length > 0 && step.type !== "intro" && step.type !== "form"; const backHtml = showBack ? `` : ""; const cardOpen = '
'; const cardClose = `${backHtml}
`; const metaHtml = swdgRenderFlowMeta(flowCfg, step); const titleHtml = `

${swdgFlowEscape(step.question || step.title || "Atendimento")}

`; const stepCopyText = (() => { if (step.type === "intro" && swdgVisitorShouldShowWelcome()) return "Que bom ter você de volta 😊"; return String(step.text || ""); })(); const textHtml = stepCopyText ? `
${swdgFlowEscape(stepCopyText)}
` : ""; if (step.type === "form") { form.classList.add("swdg-flow-form-active"); const formIntroText = "Preencha seus dados para continuar no WhatsApp."; const formIntro = `${cardOpen}${metaHtml}

Vamos começar

${swdgFlowEscape(formIntroText)}
`; const originalMarkup = SWDG_FLOW_ORIGINAL_FORM_HTML || form.innerHTML; const hiddenAcceptance = ``; const cleanedMarkup = originalMarkup.replace(/