/* Stumble CRO Bundle — Built 2026-04-11T06:40:23.215593 */ window.__STUMBLE_CRO_CONFIG={"global":{"frequency_cap_days":5,"mobile_enabled":true,"app_store_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794","ab_testing":{"enabled":true,"cookie_days":30,"min_sample_size":100}},"banners":[{"id":"exit-popup-homepage","type":"exit-intent","pages":["/"],"enabled":true,"delay_seconds":10,"frequency_cap_days":14,"privacy_text":"Free \u00b7 Anonymous \u00b7 iOS","variants":[{"variant_id":"A-company","weight":25,"headline":"You don't have to do this alone.","body":"Stumble is a free, anonymous app for the hardest days after a breakup, loss, or life change. Download it and the next hard moment has company.","cta_text":"Download Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"variant_id":"B-first-place","weight":25,"headline":"The first place to go after a breakup.","body":"No sign-up. No judgment. Just tools and people who get it \u2014 whenever you need them.","cta_text":"Get The App","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"variant_id":"C-rating","weight":25,"headline":"4.86 stars from people exactly where you are.","body":"Stumble is free, anonymous, and built with mental health professionals. Take it with you.","cta_text":"Download Free","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"variant_id":"D-pocket","weight":25,"headline":"Keep Stumble in your pocket for 3 AM.","body":"The hardest moments don't happen on a schedule. Stumble is always there \u2014 free and anonymous.","cta_text":"Install Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"}]},{"id":"exit-popup-breakup-topics","type":"exit-intent","pages":["/breakup-recovery","/breakup-recovery-2","/heartbreak-peer-support","/anxiety-after-breakup","/toxic-relationship-recovery"],"enabled":true,"delay_seconds":8,"frequency_cap_days":10,"privacy_text":"Free \u00b7 Anonymous \u00b7 Built with mental health professionals","variants":[{"variant_id":"A-take-with-you","weight":50,"headline":"Take this support with you.","body":"Everything you just read is inside the Stumble app \u2014 plus a private journal, guided exercises, and anonymous peer support. Free to download.","cta_text":"Download Stumble Free","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"variant_id":"B-3am","weight":50,"headline":"Breakup spirals don't keep office hours.","body":"Stumble is awake at 3 AM with you. Free, anonymous, and built for exactly this.","cta_text":"Get Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"}]},{"id":"exit-popup-divorce","type":"exit-intent","pages":["/divorce-support","/divorce-mental-health-app","/life-transitions"],"enabled":true,"delay_seconds":8,"frequency_cap_days":10,"privacy_text":"Free \u00b7 Anonymous \u00b7 iOS","headline":"Divorce is a beginning, not just an ending.","body":"Stumble is a free, anonymous app for the long middle \u2014 with tools, check-ins, and people who've been through it.","cta_text":"Download Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"id":"exit-popup-loneliness","type":"exit-intent","pages":["/loneliness-support","/loneliness-support-2","/ai-companion-loneliness","/ai-companion","/anonymous-community"],"enabled":true,"delay_seconds":8,"frequency_cap_days":10,"privacy_text":"Free \u00b7 Anonymous \u00b7 No sign-up required","headline":"Loneliness lies. Stumble reminds you you're not the only one.","body":"Free, anonymous peer support and guided exercises \u2014 in your pocket, whenever you need them.","cta_text":"Download Free","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"id":"exit-popup-alternatives","type":"exit-intent","pages":["/cerebral-alternative","/calm-alternative","/7-cups-alternative","/replika-alternative","/woebot-alternative","/talkspace-alternative","/wysa-alternative","/betterhelp","/headspace","/mend"],"enabled":true,"delay_seconds":6,"frequency_cap_days":10,"privacy_text":"Free \u00b7 Anonymous \u00b7 No waitlist","variants":[{"variant_id":"A-no-price-tag","weight":50,"headline":"Can't afford therapy right now? Stumble is free.","body":"Real peer support and guided tools \u2014 no waitlist, no $200/hour sessions, no credit card.","cta_text":"Download Stumble Free","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"variant_id":"B-built-for-this","weight":50,"headline":"Built specifically for heartbreak \u2014 not everything.","body":"General mental health apps are great, but Stumble was built from a breakup, for breakups. Free and anonymous.","cta_text":"Get The App","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"}]},{"id":"exit-popup-journaling","type":"exit-intent","pages":["/journaling","/anonymous-journaling-app","/mood-tracking"],"enabled":true,"delay_seconds":8,"frequency_cap_days":10,"privacy_text":"Free \u00b7 Anonymous \u00b7 No account required","headline":"Writing it out helps. Doing it anonymously helps more.","body":"Stumble's anonymous journal and mood tracking live in your pocket \u2014 no account, no pressure.","cta_text":"Download Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"id":"sticky-bar-global","type":"sticky-bar","pages":["/*"],"exclude_pages":["/contactus","/support","/privacy","/terms"],"enabled":true,"delay_seconds":5,"frequency_cap_days":5,"variants":[{"variant_id":"A-first-place","weight":25,"headline":"The first place to go after a breakup \u2014 free, anonymous, always with you.","cta_text":"Download Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794","bg_color":"#1a1a1a","cta_bg_color":"#fff","cta_text_color":"#1a1a1a","position":"bottom"},{"variant_id":"B-3am","weight":25,"headline":"Heartbreak doesn't keep office hours. Stumble is free and always there.","cta_text":"Get The App","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794","bg_color":"#4F46E5","cta_bg_color":"#fff","cta_text_color":"#4F46E5","position":"bottom"},{"variant_id":"C-community","weight":25,"headline":"You're not the only one. Stumble connects you anonymously.","cta_text":"Download Free","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794","bg_color":"#059669","cta_bg_color":"#fff","cta_text_color":"#059669","position":"bottom"},{"variant_id":"D-pocket","weight":25,"headline":"Keep peer support in your pocket. Free \u00b7 Anonymous \u00b7 iOS.","cta_text":"Install Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794","bg_color":"#0f172a","cta_bg_color":"#fcd34d","cta_text_color":"#0f172a","position":"bottom"}]},{"id":"inline-cta-blog","type":"inline-cta","pages":["/2024/*","/2025/*","/2026/*","/blog/*"],"enabled":true,"placement":"after-h2-2","variants":[{"variant_id":"A-take-this-with-you","weight":25,"headline":"Take this with you in your pocket","body":"Stumble is a free, anonymous iOS app that turns what you're reading into tools you can actually use \u2014 journaling, grounding exercises, and peer support.","cta_text":"Download Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"variant_id":"B-not-alone","weight":25,"headline":"You're not the only one feeling this","body":"Stumble connects you anonymously with people going through the same thing. Free, no sign-up, always in your pocket.","cta_text":"Get Stumble Free","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"variant_id":"C-3am","weight":25,"headline":"For the 3 AM version of this moment","body":"Articles help during the day. Stumble helps at 3 AM \u2014 anonymous, free, and always awake.","cta_text":"Download The App","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"variant_id":"D-build-for-this","weight":25,"headline":"Stumble was built for exactly this","body":"Founded after a breakup in 2023, built with mental health professionals. Free, anonymous, iOS. Take the next hard day with company.","cta_text":"Install Stumble Free","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"}]},{"id":"inline-cta-topic-pages","type":"inline-cta","pages":["/breakup-recovery","/breakup-recovery-2","/heartbreak-peer-support","/anxiety-after-breakup","/toxic-relationship-recovery","/divorce-support","/divorce-mental-health-app","/life-transitions","/loneliness-support","/loneliness-support-2","/ai-companion","/ai-companion-loneliness","/anonymous-community","/anonymous-journaling-app","/journaling","/mood-tracking"],"enabled":true,"placement":"after-h2-2","headline":"Stumble turns all of this into something you can hold","body":"A free, anonymous iOS app for breakups, loneliness, and life transitions. No sign-up, no judgment, always there.","cta_text":"Download Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"id":"inline-cta-alternatives","type":"inline-cta","pages":["/cerebral-alternative","/calm-alternative","/7-cups-alternative","/replika-alternative","/woebot-alternative","/talkspace-alternative","/wysa-alternative","/betterhelp","/headspace","/mend"],"enabled":true,"placement":"after-h2-2","headline":"Ready to try Stumble? It's free.","body":"No waitlists. No monthly subscription. No $200/hour sessions. Just an anonymous app built specifically for heartbreak, in your pocket when you need it.","cta_text":"Download Stumble Free","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794"},{"id":"scroll-popup-blog","type":"scroll-popup","pages":["/2024/*","/2025/*","/2026/*","/blog/*"],"enabled":true,"scroll_percent":55,"delay_seconds":2,"frequency_cap_days":7,"privacy_text":"Free \u00b7 Anonymous \u00b7 No sign-up required","cta_text":"Download Stumble","cta_url":"https://apps.apple.com/us/app/stumble-llc/id6758137794","headline_template":"Struggling with {feature}? Stumble is in your pocket.","body_template":"{angle}. Free, anonymous, and built for exactly this.","fallback_headline":"Healing is easier with company.","fallback_body":"Stumble is a free, anonymous app for heartbreak, loneliness, and life transitions. Take it with you for the next hard day.","variants":[{"variant_id":"A-contextual-empathetic","weight":50},{"variant_id":"B-contextual-direct","weight":50,"headline_template":"{feature}, in your pocket \u2014 free.","body_template":"{angle}. Download Stumble and take it with you.","cta_text":"Get Stumble Free"}]},{"id":"form-optimizer-contact","type":"form-optimizer","pages":["/contactus","/contact*","/support"],"form_config":{"social_proof_text":"Stumble is free and anonymous \u2014 no account needed to get started","cta_text":"Send Message","inline_validation":true,"privacy_text":"We reply from a real human at Stumble, usually within a day."},"enabled":true},{"id":"form-optimizer-global","type":"form-optimizer","pages":["/*"],"form_config":{"social_proof_text":"Join thousands healing with Stumble \u2014 free and anonymous","inline_validation":true},"enabled":true}]}; /* ── cro-loader.js ── */ /** * CRO Loader — Main entry point for Stumble CRO system * Loads banner config, runs A/B variant selection, and activates modules per page. * Deploy this single script to WordPress; it handles everything else. */ (function () { "use strict"; const CRO_VERSION = "2.0.0"; const COOKIE_PREFIX = "stumble_cro_"; const CONFIG_ATTR = "data-cro-config"; // ── Kill Switch ── function isKilled() { if (window.__STUMBLE_CRO_ENABLED === false) return true; var params = new URLSearchParams(window.location.search); var croParam = params.get("cro"); if (croParam === "off") { document.cookie = COOKIE_PREFIX + "disabled=1;path=/;SameSite=Lax"; return true; } if (croParam === "on") { document.cookie = COOKIE_PREFIX + "disabled=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/"; return false; } if (document.cookie.indexOf(COOKIE_PREFIX + "disabled=1") !== -1) return true; return false; } // ── Utility Functions ── function getCookie(name) { var match = document.cookie.match( new RegExp("(^| )" + COOKIE_PREFIX + name + "=([^;]+)") ); return match ? match[2] : null; } function setCookie(name, value, days) { var d = new Date(); d.setTime(d.getTime() + days * 86400000); document.cookie = COOKIE_PREFIX + name + "=" + value + ";expires=" + d.toUTCString() + ";path=/;SameSite=Lax"; } function matchesPage(patterns) { var path = window.location.pathname; return patterns.some(function (pattern) { if (pattern.endsWith("*")) { return path.startsWith(pattern.slice(0, -1)); } return path === pattern || path === pattern + "/"; }); } function isMobile() { return window.innerWidth < 768; } function trackEvent(action, label, variantId) { var params = { event_category: "CRO", event_label: label, }; if (variantId) params.cro_variant = variantId; if (window.gtag) { window.gtag("event", action, params); } if (window.dataLayer) { window.dataLayer.push({ event: "cro_event", cro_action: action, cro_label: label, cro_variant: variantId || null, }); } } // ── A/B Testing Infrastructure ── function hashString(str) { // djb2 hash — deterministic, fast, good distribution var hash = 5381; for (var i = 0; i < str.length; i++) { hash = ((hash << 5) + hash) + str.charCodeAt(i); hash = hash & hash; // Convert to 32-bit integer } return Math.abs(hash); } function getVisitorId() { var vid = getCookie("vid"); if (vid) return vid; vid = Date.now().toString(36) + Math.random().toString(36).substr(2, 9); setCookie("vid", vid, 365); return vid; } function selectVariant(banner, visitorId) { var variants = banner.variants; if (!variants || variants.length === 0) return null; if (variants.length === 1) return variants[0]; // Calculate total weight var totalWeight = 0; for (var i = 0; i < variants.length; i++) { totalWeight += (variants[i].weight || 1); } // Deterministic bucket based on visitor + banner ID var bucket = hashString(visitorId + "|" + banner.id) % totalWeight; // Walk cumulative weights to find the selected variant var cumulative = 0; for (var j = 0; j < variants.length; j++) { cumulative += (variants[j].weight || 1); if (bucket < cumulative) return variants[j]; } return variants[variants.length - 1]; } function resolveVariant(banner, variant) { if (!variant) return banner; // Copy all banner fields except variants array var resolved = {}; for (var key in banner) { if (key !== "variants") resolved[key] = banner[key]; } // Overlay variant fields (non-null/undefined values only) var variantFields = [ "headline", "body", "cta_text", "cta_url", "image_url", "bg_color", "cta_bg_color", "cta_text_color", "position", "success_headline", "success_body", "privacy_text", "headline_template", "body_template", "fallback_headline", "fallback_body", "form_config" ]; for (var i = 0; i < variantFields.length; i++) { var f = variantFields[i]; if (variant[f] !== undefined && variant[f] !== null) { // Deep merge for form_config if (f === "form_config" && resolved.form_config && typeof variant[f] === "object") { var merged = {}; for (var k in resolved.form_config) merged[k] = resolved.form_config[k]; for (var k2 in variant[f]) merged[k2] = variant[f][k2]; resolved.form_config = merged; } else { resolved[f] = variant[f]; } } } resolved._variant_id = variant.variant_id || null; return resolved; } // ── CSS Injection ── function injectStyles() { if (document.getElementById("stumble-cro-styles")) return; var style = document.createElement("style"); style.id = "stumble-cro-styles"; style.textContent = [ // Overlay ".zcro-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.55);z-index:99998;opacity:0;transition:opacity .3s ease}", ".zcro-overlay.active{opacity:1}", // Popup ".zcro-popup{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) scale(.9);background:#fff;border-radius:12px;padding:40px;max-width:480px;width:90%;z-index:99999;opacity:0;transition:all .3s ease;box-shadow:0 20px 60px rgba(0,0,0,.3)}", ".zcro-popup.active{opacity:1;transform:translate(-50%,-50%) scale(1)}", ".zcro-popup-close{position:absolute;top:12px;right:16px;background:none;border:none;font-size:24px;cursor:pointer;color:#666;line-height:1}", ".zcro-popup h2{margin:0 0 12px;font-size:24px;color:#1a1a1a;line-height:1.3}", ".zcro-popup p{margin:0 0 20px;color:#555;font-size:16px;line-height:1.5}", ".zcro-popup form{display:flex;flex-direction:column;gap:12px}", ".zcro-popup input{padding:12px 16px;border:1px solid #ddd;border-radius:8px;font-size:15px;outline:none;transition:border-color .2s}", ".zcro-popup input:focus{border-color:#4F46E5}", ".zcro-popup button[type=submit]{padding:14px;background:#4F46E5;color:#fff;border:none;border-radius:8px;font-size:16px;font-weight:600;cursor:pointer;transition:background .2s}", ".zcro-popup button[type=submit]:hover{background:#4338CA}", ".zcro-popup .zcro-privacy{font-size:12px;color:#999;text-align:center;margin-top:8px}", ".zcro-popup img.zcro-hero{width:100%;max-height:200px;object-fit:cover;border-radius:8px;margin-bottom:16px}", // Sticky Bar ".zcro-sticky{position:fixed;left:0;width:100%;z-index:99997;padding:12px 20px;display:flex;align-items:center;justify-content:center;gap:16px;font-size:15px;transition:transform .3s ease}", ".zcro-sticky-top{top:0;transform:translateY(-100%)}", ".zcro-sticky-bottom{bottom:0;transform:translateY(100%)}", ".zcro-sticky.active.zcro-sticky-top{transform:translateY(0)}", ".zcro-sticky.active.zcro-sticky-bottom{transform:translateY(0)}", ".zcro-sticky-text{color:#fff;font-weight:500}", ".zcro-sticky-cta{padding:8px 20px;border-radius:6px;text-decoration:none;font-weight:600;font-size:14px;white-space:nowrap;transition:opacity .2s}", ".zcro-sticky-cta:hover{opacity:.9}", ".zcro-sticky-close{background:none;border:none;color:rgba(255,255,255,.7);font-size:20px;cursor:pointer;padding:0 4px;line-height:1}", ".zcro-sticky-close:hover{color:#fff}", // Inline CTA ".zcro-inline{margin:32px 0;padding:28px 32px;border-radius:12px;border:2px solid #E0E7FF;background:linear-gradient(135deg,#EEF2FF,#F5F3FF);text-align:center}", ".zcro-inline h3{margin:0 0 8px;font-size:20px;color:#1a1a1a}", ".zcro-inline p{margin:0 0 16px;color:#555;font-size:15px}", ".zcro-inline a{display:inline-block;padding:12px 28px;background:#4F46E5;color:#fff;border-radius:8px;text-decoration:none;font-weight:600;font-size:15px;transition:background .2s}", ".zcro-inline a:hover{background:#4338CA}", ".zcro-inline img.zcro-hero{max-width:100%;border-radius:8px;margin-bottom:12px}", // Mobile adjustments "@media(max-width:767px){", ".zcro-popup{padding:28px 20px;max-width:95%}", ".zcro-popup h2{font-size:20px}", ".zcro-sticky{flex-wrap:wrap;gap:8px;padding:10px 16px;font-size:13px}", ".zcro-inline{padding:20px 16px}", "}", ].join("\n"); document.head.appendChild(style); } // ── Module Registry ── var modules = {}; function registerModule(name, initFn) { modules[name] = initFn; } // ── Config Loading ── function loadConfig() { var scriptTag = document.querySelector("script[" + CONFIG_ATTR + "]"); if (scriptTag) { try { return JSON.parse(scriptTag.getAttribute(CONFIG_ATTR)); } catch (e) { console.warn("[CRO] Invalid inline config:", e); } } if (window.__STUMBLE_CRO_CONFIG) { return window.__STUMBLE_CRO_CONFIG; } return null; } // ── Init ── function init() { if (isKilled()) { console.log("[CRO] Disabled via admin toggle or ?cro=off"); return; } var config = loadConfig(); if (!config) { console.warn("[CRO] No config found."); return; } injectStyles(); var globalConfig = config.global || {}; var banners = config.banners || []; var visitorId = getVisitorId(); banners.forEach(function (banner) { if (!banner.enabled) return; if (!matchesPage(banner.pages || ["*"])) return; if (globalConfig.mobile_enabled === false && isMobile()) return; // Check frequency cap var capKey = "dismissed_" + banner.id; var dismissed = getCookie(capKey); if (dismissed) return; var capDays = banner.frequency_cap_days || globalConfig.frequency_cap_days || 3; // ── A/B Variant Selection ── var selectedVariant = selectVariant(banner, visitorId); var resolvedBanner = resolveVariant(banner, selectedVariant); var moduleName = resolvedBanner.type; if (modules[moduleName]) { modules[moduleName](resolvedBanner, capDays); } else { console.warn("[CRO] Unknown module:", moduleName); } }); console.log("[CRO] Initialized v" + CRO_VERSION + " | visitor: " + visitorId.substr(0, 8)); } // Expose for modules window.__STUMBLE_CRO = { registerModule: registerModule, getCookie: getCookie, setCookie: setCookie, trackEvent: trackEvent, isMobile: isMobile, getVisitorId: getVisitorId, init: init, }; // Auto-init on DOM ready — defer via setTimeout(0) so all module IIFEs // in the concatenated bundle have a chance to register themselves before // init() iterates the banner list. function scheduleInit() { setTimeout(init, 0); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", scheduleInit); } else { scheduleInit(); } })(); /* ── exit-intent.js ── */ /** * Exit-Intent Popup Module * Detects when a visitor is about to leave and shows a lead capture popup. * Desktop: mouse leaves viewport. Mobile: scroll up + pause pattern. */ (function () { "use strict"; var CRO = window.__STUMBLE_CRO; if (!CRO) return; CRO.registerModule("exit-intent", function (banner, capDays) { var triggered = false; var overlay = null; var popup = null; function createPopup() { // Overlay overlay = document.createElement("div"); overlay.className = "zcro-overlay"; overlay.addEventListener("click", dismiss); // Popup container popup = document.createElement("div"); popup.className = "zcro-popup"; popup.setAttribute("role", "dialog"); popup.setAttribute("aria-label", banner.headline || "Special offer"); // Close button var closeBtn = document.createElement("button"); closeBtn.className = "zcro-popup-close"; closeBtn.innerHTML = "×"; closeBtn.setAttribute("aria-label", "Close"); closeBtn.addEventListener("click", dismiss); // Headline var h2 = document.createElement("h2"); h2.textContent = banner.headline || "Wait! Before you go..."; // Body text var p = document.createElement("p"); p.textContent = banner.body || ""; popup.appendChild(closeBtn); // Hero image (optional) if (banner.image_url) { var img = document.createElement("img"); img.className = "zcro-hero"; img.src = banner.image_url; img.alt = banner.headline || "Offer"; popup.appendChild(img); } popup.appendChild(h2); if (banner.body) popup.appendChild(p); // Form or CTA link if (banner.form_fields && banner.form_fields.length > 0) { var form = createForm(banner); popup.appendChild(form); } else if (banner.cta_url) { var ctaLink = document.createElement("a"); ctaLink.href = banner.cta_url; ctaLink.textContent = banner.cta_text || "Learn More"; ctaLink.style.cssText = "display:block;padding:14px;background:#4F46E5;color:#fff;border:none;border-radius:8px;font-size:16px;font-weight:600;text-align:center;text-decoration:none;cursor:pointer;transition:background .2s"; if (/apps\.apple\.com|play\.google\.com/.test(ctaLink.href)) { ctaLink.target = "_blank"; ctaLink.rel = "noopener"; } ctaLink.addEventListener("click", function () { CRO.trackEvent("cro_popup_cta_click", banner.id, banner._variant_id); CRO.trackEvent("cro_app_install_click", banner.id, banner._variant_id); CRO.setCookie("dismissed_" + banner.id, "installed", capDays * 10); }); popup.appendChild(ctaLink); } // Privacy note var privacy = document.createElement("div"); privacy.className = "zcro-privacy"; privacy.textContent = banner.privacy_text || "We respect your privacy. Unsubscribe anytime."; popup.appendChild(privacy); document.body.appendChild(overlay); document.body.appendChild(popup); // Animate in requestAnimationFrame(function () { overlay.classList.add("active"); popup.classList.add("active"); }); CRO.trackEvent("cro_popup_shown", banner.id, banner._variant_id); } function createForm(banner) { var form = document.createElement("form"); form.addEventListener("submit", function (e) { e.preventDefault(); handleSubmit(form, banner); }); var fieldMap = { email: { type: "email", placeholder: "you@company.com", required: true }, name: { type: "text", placeholder: "Your name", required: false }, company: { type: "text", placeholder: "Company name", required: false }, phone: { type: "tel", placeholder: "Phone number", required: false }, }; banner.form_fields.forEach(function (fieldName) { var config = fieldMap[fieldName] || { type: "text", placeholder: fieldName, required: false, }; var input = document.createElement("input"); input.type = config.type; input.name = fieldName; input.placeholder = config.placeholder; if (config.required) input.required = true; form.appendChild(input); }); var submitBtn = document.createElement("button"); submitBtn.type = "submit"; submitBtn.textContent = banner.cta_text || "Get Access"; form.appendChild(submitBtn); return form; } function handleSubmit(form, banner) { var data = {}; var inputs = form.querySelectorAll("input"); inputs.forEach(function (inp) { data[inp.name] = inp.value; }); CRO.trackEvent("cro_popup_submit", banner.id, banner._variant_id); // Send to configured endpoint or fallback var endpoint = banner.submit_url || "/wp-json/cro/v1/lead"; fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ banner_id: banner.id, variant_id: banner._variant_id || null, page: window.location.pathname, fields: data, timestamp: new Date().toISOString(), }), }) .then(function () { showSuccess(banner); }) .catch(function () { // Still show success to user; log error showSuccess(banner); console.error("[CRO] Lead submission failed for", banner.id); }); } function showSuccess(banner) { if (!popup) return; popup.innerHTML = '
' + '
' + "

" + (banner.success_headline || "You're all set!") + "

" + "

" + (banner.success_body || "Check your inbox for your free resource.") + "

"; CRO.setCookie("dismissed_" + banner.id, "submitted", capDays * 10); setTimeout(dismiss, 3000); } function dismiss() { if (overlay) { overlay.classList.remove("active"); popup.classList.remove("active"); setTimeout(function () { if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay); if (popup && popup.parentNode) popup.parentNode.removeChild(popup); }, 300); } CRO.setCookie("dismissed_" + banner.id, "1", capDays); CRO.trackEvent("cro_popup_dismissed", banner.id, banner._variant_id); } // ── Exit-Intent Detection ── // Desktop: mouse leaves viewport top function onMouseOut(e) { if (triggered) return; if (e.clientY < 5 && e.relatedTarget === null) { triggered = true; createPopup(); document.removeEventListener("mouseout", onMouseOut); } } // Mobile: rapid scroll up after being on page > 10s var mobileTimeout = null; var lastScrollY = 0; var pageLoadTime = Date.now(); function onMobileScroll() { if (triggered) return; if (Date.now() - pageLoadTime < 10000) return; var currentY = window.scrollY; if (currentY < lastScrollY - 100 && currentY < 200) { // User scrolled up significantly toward top clearTimeout(mobileTimeout); mobileTimeout = setTimeout(function () { triggered = true; createPopup(); window.removeEventListener("scroll", onMobileScroll); }, 500); } lastScrollY = currentY; } // Activate detection var delay = (banner.delay_seconds || 5) * 1000; setTimeout(function () { if (CRO.isMobile()) { window.addEventListener("scroll", onMobileScroll, { passive: true }); } else { document.addEventListener("mouseout", onMouseOut); } }, delay); // ESC key closes popup document.addEventListener("keydown", function (e) { if (e.key === "Escape" && popup && popup.classList.contains("active")) { dismiss(); } }); }); })(); /* ── sticky-bar.js ── */ /** * Sticky Bar Module * Persistent top or bottom bar with CTA. Dismissible with cookie memory. */ (function () { "use strict"; var CRO = window.__STUMBLE_CRO; if (!CRO) return; CRO.registerModule("sticky-bar", function (banner, capDays) { var position = banner.position || "top"; // "top" or "bottom" var bgColor = banner.bg_color || "#4F46E5"; var ctaBgColor = banner.cta_bg_color || "#fff"; var ctaTextColor = banner.cta_text_color || "#4F46E5"; var bar = document.createElement("div"); bar.className = "zcro-sticky zcro-sticky-" + position; bar.style.background = bgColor; // Text var textSpan = document.createElement("span"); textSpan.className = "zcro-sticky-text"; textSpan.textContent = banner.headline || ""; bar.appendChild(textSpan); // CTA button if (banner.cta_url && banner.cta_text) { var ctaLink = document.createElement("a"); ctaLink.className = "zcro-sticky-cta"; ctaLink.href = banner.cta_url; ctaLink.textContent = banner.cta_text; ctaLink.style.background = ctaBgColor; ctaLink.style.color = ctaTextColor; if (/apps\.apple\.com|play\.google\.com/.test(ctaLink.href)) { ctaLink.target = "_blank"; ctaLink.rel = "noopener"; } ctaLink.addEventListener("click", function () { CRO.trackEvent("cro_sticky_cta_click", banner.id, banner._variant_id); CRO.trackEvent("cro_app_install_click", banner.id, banner._variant_id); CRO.setCookie("dismissed_" + banner.id, "installed", capDays * 10); }); bar.appendChild(ctaLink); } // Close button var closeBtn = document.createElement("button"); closeBtn.className = "zcro-sticky-close"; closeBtn.innerHTML = "×"; closeBtn.setAttribute("aria-label", "Dismiss"); closeBtn.addEventListener("click", function () { bar.classList.remove("active"); setTimeout(function () { if (bar.parentNode) bar.parentNode.removeChild(bar); }, 300); CRO.setCookie("dismissed_" + banner.id, "1", capDays); CRO.trackEvent("cro_sticky_dismissed", banner.id, banner._variant_id); }); bar.appendChild(closeBtn); document.body.appendChild(bar); // Show after delay var delay = (banner.delay_seconds || 2) * 1000; setTimeout(function () { bar.classList.add("active"); CRO.trackEvent("cro_sticky_shown", banner.id, banner._variant_id); }, delay); // Optional: countdown timer if (banner.countdown_minutes) { var countdownEl = document.createElement("span"); countdownEl.style.cssText = "font-weight:700;margin-left:4px"; textSpan.appendChild(countdownEl); var endTime = Date.now() + banner.countdown_minutes * 60000; function updateCountdown() { var remaining = Math.max(0, endTime - Date.now()); var mins = Math.floor(remaining / 60000); var secs = Math.floor((remaining % 60000) / 1000); countdownEl.textContent = " " + mins + ":" + (secs < 10 ? "0" : "") + secs; if (remaining > 0) { requestAnimationFrame(updateCountdown); } } updateCountdown(); } }); })(); /* ── inline-cta.js ── */ /** * Inline CTA Module * Injects CTA banners into page content at strategic positions. */ (function () { "use strict"; var CRO = window.__STUMBLE_CRO; if (!CRO) return; CRO.registerModule("inline-cta", function (banner) { var placement = banner.placement || "after-h2-2"; // after 2nd H2 by default var imageHtml = banner.image_url ? '' + (banner.headline || '') + '' : ''; var ctaHtml = '
' + imageHtml + "

" + (banner.headline || "Ready to get started?") + "

" + (banner.body ? "

" + banner.body + "

" : "") + '' + (banner.cta_text || "Get Started Free") + "
"; function inject() { var target = findTarget(placement); if (!target) { console.warn("[CRO] Inline CTA target not found for placement:", placement); return; } var container = document.createElement("div"); container.innerHTML = ctaHtml; var ctaEl = container.firstChild; target.parentNode.insertBefore(ctaEl, target.nextSibling); // Track CTA link clicks var link = ctaEl.querySelector("a"); if (link) { if (/apps\.apple\.com|play\.google\.com/.test(link.href)) { link.target = "_blank"; link.rel = "noopener"; } link.addEventListener("click", function () { CRO.trackEvent("cro_inline_cta_click", banner.id, banner._variant_id); CRO.trackEvent("cro_app_install_click", banner.id, banner._variant_id); }); } // Track visibility if (window.IntersectionObserver) { var observer = new IntersectionObserver( function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { CRO.trackEvent("cro_inline_cta_seen", banner.id, banner._variant_id); observer.disconnect(); } }); }, { threshold: 0.5 } ); observer.observe(ctaEl); } } function findTarget(placement) { // "after-h2-N" — insert after the Nth H2 in the content area var match = placement.match(/^after-h2-(\d+)$/); if (match) { var n = parseInt(match[1], 10); // Prefer semantic content containers; fall back to theme/page-builder // wrappers. joinstumble.com uses the Agntix theme which wraps body // copy in `.tp-page-content`, so probe that explicitly. var contentArea = document.querySelector(".entry-content") || document.querySelector("article") || document.querySelector(".post-content") || document.querySelector(".tp-page-content") || document.querySelector("main") || document.querySelector(".elementor") || document.body; if (!contentArea) return null; // Filter out H2s inside the footer / header / nav — we only want // body-copy headings. var h2s = Array.prototype.filter.call( contentArea.querySelectorAll("h2"), function (h) { return !h.closest("footer, header, nav, .tp-footer-area, .tp-copyright-area"); } ); return h2s[n - 1] || h2s[h2s.length - 1] || null; } // "after-paragraph-N" — insert after Nth paragraph match = placement.match(/^after-paragraph-(\d+)$/); if (match) { var pn = parseInt(match[1], 10); var content = document.querySelector(".entry-content") || document.querySelector("article") || document.querySelector("main"); if (!content) return null; var paragraphs = content.querySelectorAll("p"); return paragraphs[pn - 1] || null; } // "scroll-50" — inject when scrolled 50% (insert at midpoint of content) if (placement === "scroll-50") { var mainContent = document.querySelector(".entry-content") || document.querySelector("article") || document.querySelector("main"); if (!mainContent) return null; var children = mainContent.children; var midIndex = Math.floor(children.length / 2); return children[midIndex] || null; } // CSS selector return document.querySelector(placement); } // Inject after DOM is ready and content is loaded if (document.readyState === "complete") { inject(); } else { window.addEventListener("load", inject); } CRO.trackEvent("cro_inline_cta_loaded", banner.id, banner._variant_id); }); })(); /* ── scroll-popup.js ── */ /** * Scroll Popup Module * Shows a contextual popup at a configurable scroll depth on blog posts. * Reads the page title and maps it to relevant Stumble topics for personalized messaging. */ (function () { "use strict"; var CRO = window.__STUMBLE_CRO; if (!CRO) return; CRO.registerModule("scroll-popup", function (banner, capDays) { var triggered = false; var overlay = null; var popup = null; var scrollThreshold = banner.scroll_percent || 50; // ── Contextual Mapping ── // Maps keywords found in the blog post title to Stumble topic angles var featureMap = [ { keywords: ["no contact", "block", "stop contacting", "stop texting"], feature: "no-contact support", hook: "No contact is hard. You shouldn't have to white-knuckle it alone.", angle: "Stumble gives you a place to vent when the urge to text them hits" }, { keywords: ["breakup", "break up", "dumped", "ended", "ex "], feature: "breakup recovery", hook: "You don't have to figure this out by yourself", angle: "Stumble was built for exactly this moment — raw, honest breakup support" }, { keywords: ["divorce", "separation", "separated", "marriage end"], feature: "divorce recovery", hook: "Divorce changes everything. Stumble helps you find your footing.", angle: "Connect anonymously with others navigating the same life transition" }, { keywords: ["heartbreak", "heart broken", "broken heart"], feature: "heartbreak recovery", hook: "Heartbreak is a full-body experience. You deserve real support.", angle: "Stumble turns the 3 AM spiral into a community that gets it" }, { keywords: ["loneliness", "lonely", "alone", "isolated"], feature: "loneliness support", hook: "Loneliness lies. Stumble reminds you you're not the only one.", angle: "Anonymous peer support from people who understand — any time, any day" }, { keywords: ["anxiety", "anxious", "panic", "spiral"], feature: "anxiety after breakup", hook: "Breakup anxiety is real. Stumble gives you tools to calm the spiral.", angle: "Journaling prompts, grounding exercises, and peers who get it" }, { keywords: ["depress", "hopeless", "dark"], feature: "emotional support", hook: "You don't have to push through this alone", angle: "Stumble is free, anonymous support for the hardest days" }, { keywords: ["toxic", "narcissist", "abusive", "manipulat"], feature: "toxic relationship recovery", hook: "Leaving a toxic relationship is brave. Healing from it is braver.", angle: "Stumble's community has been exactly where you are — and they made it out" }, { keywords: ["grief", "grieving", "loss", "mourning"], feature: "grief support", hook: "Grief doesn't follow a timeline. Neither does Stumble.", angle: "Anonymous space to sit with the loss without judgment or fixing" }, { keywords: ["journal", "writing", "write it out"], feature: "anonymous journaling", hook: "Writing it down actually helps", angle: "Stumble's anonymous journal travels with you, no account required" }, { keywords: ["therapy", "therapist", "counsel", "betterhelp", "talkspace"], feature: "peer support", hook: "Can't afford therapy right now? Stumble is free.", angle: "Real peer support and guided tools — no waitlist, no $200/hour sessions" }, { keywords: ["sleep", "insomnia", "can't sleep", "cant sleep"], feature: "sleep support", hook: "3 AM hits different after a breakup. Stumble is awake too.", angle: "Guided exercises and a community that's up when you are" }, { keywords: ["move on", "moving on", "get over", "let go"], feature: "moving on", hook: "There's no roadmap for moving on. Stumble walks it with you.", angle: "Small daily steps, honest check-ins, and people cheering you forward" }, { keywords: ["self worth", "self-worth", "self esteem", "confidence"], feature: "rebuilding self-worth", hook: "Rebuilding yourself after a breakup takes tools, not just time", angle: "Stumble's daily prompts help you reconnect with who you are" }, { keywords: ["friend", "friendship"], feature: "community", hook: "When your friends don't get it, Stumble does", angle: "A whole community who's been exactly where you are" } ]; function getPageContext() { // Get the blog post title var h1 = document.querySelector("h1"); var title = h1 ? h1.textContent.trim() : ""; var metaTitle = document.title || ""; var fullText = (title + " " + metaTitle).toLowerCase(); // Find the best matching feature var bestMatch = null; var bestScore = 0; for (var i = 0; i < featureMap.length; i++) { var entry = featureMap[i]; var score = 0; for (var j = 0; j < entry.keywords.length; j++) { if (fullText.indexOf(entry.keywords[j]) !== -1) { score += entry.keywords[j].length; // Longer matches score higher } } if (score > bestScore) { bestScore = score; bestMatch = entry; } } return { title: title, match: bestMatch, isContextual: bestScore > 0 }; } function buildContextualCopy(context, banner) { if (context.isContextual && context.match) { return { headline: banner.headline_template ? banner.headline_template.replace("{feature}", context.match.feature) : context.match.hook, body: banner.body_template ? banner.body_template.replace("{angle}", context.match.angle) : context.match.angle + ". Download Stumble free.", cta_text: banner.cta_text || "Download Stumble", cta_url: banner.cta_url || "https://apps.apple.com/us/app/stumble-llc/id6758137794" }; } // Fallback: generic message return { headline: banner.fallback_headline || "Healing is easier with company", body: banner.fallback_body || "Stumble is a free, anonymous app for heartbreak, loneliness, and life transitions. Download it and you don't have to do the next hard day alone.", cta_text: banner.cta_text || "Download Stumble", cta_url: banner.cta_url || "https://apps.apple.com/us/app/stumble-llc/id6758137794" }; } function createPopup(copy) { // Overlay overlay = document.createElement("div"); overlay.className = "zcro-overlay"; overlay.addEventListener("click", dismiss); // Popup popup = document.createElement("div"); popup.className = "zcro-popup"; popup.setAttribute("role", "dialog"); // Close button var closeBtn = document.createElement("button"); closeBtn.className = "zcro-popup-close"; closeBtn.innerHTML = "×"; closeBtn.setAttribute("aria-label", "Close"); closeBtn.addEventListener("click", dismiss); // Optional image if (banner.image_url) { var img = document.createElement("img"); img.className = "zcro-hero"; img.src = banner.image_url; img.alt = copy.headline; popup.appendChild(img); } // Headline var h2 = document.createElement("h2"); h2.textContent = copy.headline; // Body var p = document.createElement("p"); p.textContent = copy.body; popup.appendChild(closeBtn); popup.appendChild(h2); popup.appendChild(p); // Direct CTA link — always app store, click = success var ctaLink = document.createElement("a"); ctaLink.href = copy.cta_url; ctaLink.target = "_blank"; ctaLink.rel = "noopener"; ctaLink.textContent = copy.cta_text; ctaLink.style.cssText = "display:block;padding:14px;background:#4F46E5;color:#fff;border:none;border-radius:8px;font-size:16px;font-weight:600;text-align:center;text-decoration:none"; ctaLink.addEventListener("click", function () { CRO.trackEvent("cro_app_install_click", banner.id, banner._variant_id); CRO.trackEvent("cro_scroll_popup_cta_click", banner.id, banner._variant_id); CRO.setCookie("dismissed_" + banner.id, "installed", capDays * 10); }); popup.appendChild(ctaLink); // Privacy / reassurance text var privacy = document.createElement("div"); privacy.className = "zcro-privacy"; privacy.textContent = banner.privacy_text || "Free · Anonymous · No sign-up required"; popup.appendChild(privacy); document.body.appendChild(overlay); document.body.appendChild(popup); requestAnimationFrame(function () { overlay.classList.add("active"); popup.classList.add("active"); }); CRO.trackEvent("cro_scroll_popup_shown", banner.id, banner._variant_id); } function dismiss() { if (overlay) { overlay.classList.remove("active"); popup.classList.remove("active"); setTimeout(function () { if (overlay && overlay.parentNode) overlay.parentNode.removeChild(overlay); if (popup && popup.parentNode) popup.parentNode.removeChild(popup); }, 300); } CRO.setCookie("dismissed_" + banner.id, "1", capDays); CRO.trackEvent("cro_scroll_popup_dismissed", banner.id, banner._variant_id); } // ── Scroll Detection ── function onScroll() { if (triggered) return; var scrollTop = window.scrollY || document.documentElement.scrollTop; var docHeight = document.documentElement.scrollHeight - window.innerHeight; if (docHeight <= 0) return; var scrollPercent = (scrollTop / docHeight) * 100; if (scrollPercent >= scrollThreshold) { triggered = true; window.removeEventListener("scroll", onScroll); var context = getPageContext(); var copy = buildContextualCopy(context, banner); createPopup(copy); } } // Start listening after delay var delay = (banner.delay_seconds || 3) * 1000; setTimeout(function () { window.addEventListener("scroll", onScroll, { passive: true }); }, delay); // ESC to close document.addEventListener("keydown", function (e) { if (e.key === "Escape" && popup && popup.classList.contains("active")) { dismiss(); } }); }); })(); /* ── form-optimizer.js ── */ /** * Form Optimizer Module * Enhances existing forms on the page: social proof near submit, * inline validation, progressive disclosure, and CTA copy improvements. */ (function () { "use strict"; var CRO = window.__STUMBLE_CRO; if (!CRO) return; CRO.registerModule("form-optimizer", function (banner) { var config = banner.form_config || {}; function enhance() { var forms = findTargetForms(banner); if (forms.length === 0) { console.warn("[CRO] Form optimizer: no matching forms found"); return; } forms.forEach(function (form, i) { // Add social proof near submit button if (config.social_proof_text) { addSocialProof(form, config.social_proof_text); } // Improve submit button copy if (config.cta_text) { improveSubmitButton(form, config.cta_text); } // Add inline validation if (config.inline_validation !== false) { addInlineValidation(form); } // Add privacy reassurance if (config.privacy_text) { addPrivacyText(form, config.privacy_text); } // Track form interactions trackFormInteractions(form, banner.id + "_form_" + i); }); CRO.trackEvent("cro_form_optimizer_applied", banner.id, banner._variant_id); } function findTargetForms(banner) { if (banner.form_selector) { return Array.from(document.querySelectorAll(banner.form_selector)); } // Auto-detect lead capture / contact forms var allForms = Array.from(document.querySelectorAll("form")); return allForms.filter(function (form) { var hasEmail = form.querySelector('input[type="email"], input[name*="email"]'); var isSearch = form.querySelector('input[type="search"]') || form.getAttribute("role") === "search"; return hasEmail && !isSearch; }); } function isDarkBackground(el) { // Walk up from the element's PARENT to find the section/container background var node = el.parentElement || el; while (node && node !== document.body) { var bg = window.getComputedStyle(node).backgroundColor; if (bg && bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent") { var match = bg.match(/\d+/g); if (match) { var r = parseInt(match[0]); var g = parseInt(match[1]); var b = parseInt(match[2]); // Luminance check: dark if < 128 return (r * 299 + g * 587 + b * 114) / 1000 < 128; } } node = node.parentElement; } return false; } function addSocialProof(form, text) { var submitBtn = form.querySelector('button[type="submit"]') || form.querySelector('input[type="submit"]') || form.querySelector("button:last-of-type"); if (!submitBtn) return; // Check if we already added it if (form.querySelector(".zcro-social-proof")) return; var onDark = isDarkBackground(submitBtn); var textColor = onDark ? "rgba(255,255,255,.85)" : "#666"; var proof = document.createElement("div"); proof.className = "zcro-social-proof"; proof.style.cssText = "font-size:13px;color:" + textColor + ";text-align:center;margin-top:8px;display:flex;align-items:center;justify-content:center;gap:6px"; proof.innerHTML = '' + "" + text + ""; submitBtn.parentNode.insertBefore(proof, submitBtn.nextSibling); } function improveSubmitButton(form, newText) { var btn = form.querySelector('button[type="submit"]') || form.querySelector('input[type="submit"]'); if (!btn) return; var currentText = (btn.value || btn.textContent || "").trim().toLowerCase(); if (["submit", "send", "go", "ok"].indexOf(currentText) === -1) return; // Update text if (btn.tagName === "INPUT") { btn.value = newText; } else { btn.textContent = newText; } // Style based on background: white button on dark bg, keep original on light bg var onDark = isDarkBackground(btn); if (onDark) { btn.setAttribute("style", (btn.getAttribute("style") || "") + ";background:#fff !important;color:#1a1a1a !important;border:none !important" + ";padding:14px 32px !important;border-radius:8px !important" + ";font-size:16px !important;font-weight:600 !important;cursor:pointer !important"); } else { btn.style.fontWeight = "600"; } } function addInlineValidation(form) { var emailInputs = form.querySelectorAll('input[type="email"]'); emailInputs.forEach(function (input) { input.addEventListener("blur", function () { var val = input.value.trim(); if (!val) return; // Basic email validation var valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val); // Common typo detection var typoSuggestion = detectTypo(val); input.style.borderColor = valid ? "#22C55E" : "#EF4444"; // Remove existing message var existing = input.parentNode.querySelector(".zcro-validation-msg"); if (existing) existing.remove(); if (!valid) { var msg = document.createElement("div"); msg.className = "zcro-validation-msg"; msg.style.cssText = "font-size:12px;color:#EF4444;margin-top:4px"; msg.textContent = "Please enter a valid email address"; input.parentNode.insertBefore(msg, input.nextSibling); } else if (typoSuggestion) { var hint = document.createElement("div"); hint.className = "zcro-validation-msg"; hint.style.cssText = "font-size:12px;color:#F59E0B;margin-top:4px;cursor:pointer"; hint.textContent = "Did you mean " + typoSuggestion + "?"; hint.addEventListener("click", function () { input.value = typoSuggestion; hint.remove(); input.style.borderColor = "#22C55E"; }); input.parentNode.insertBefore(hint, input.nextSibling); } }); // Clear validation on focus input.addEventListener("focus", function () { input.style.borderColor = ""; var msg = input.parentNode.querySelector(".zcro-validation-msg"); if (msg) msg.remove(); }); }); } function detectTypo(email) { var parts = email.split("@"); if (parts.length !== 2) return null; var domain = parts[1].toLowerCase(); var corrections = { "gmial.com": "gmail.com", "gmai.com": "gmail.com", "gmal.com": "gmail.com", "gamil.com": "gmail.com", "gnail.com": "gmail.com", "gmail.co": "gmail.com", "outlok.com": "outlook.com", "outllok.com": "outlook.com", "hotmal.com": "hotmail.com", "hotmai.com": "hotmail.com", "yahooo.com": "yahoo.com", "yaho.com": "yahoo.com", }; if (corrections[domain]) { return parts[0] + "@" + corrections[domain]; } return null; } function addPrivacyText(form, text) { var submitBtn = form.querySelector('button[type="submit"]') || form.querySelector('input[type="submit"]'); if (!submitBtn) return; // Check if we already added it if (form.querySelector(".zcro-privacy-text")) return; var onDark = isDarkBackground(submitBtn); var textColor = onDark ? "rgba(255,255,255,.7)" : "#999"; var el = document.createElement("div"); el.className = "zcro-privacy-text"; el.style.cssText = "font-size:11px;color:" + textColor + ";text-align:center;margin-top:6px"; el.innerHTML = "🔒 " + text; // Insert after social proof if it exists, otherwise after submit var socialProof = form.querySelector(".zcro-social-proof"); var insertAfter = socialProof || submitBtn; insertAfter.parentNode.insertBefore(el, insertAfter.nextSibling); } function trackFormInteractions(form, formId) { var tracked = false; // Track first field focus var inputs = form.querySelectorAll("input, select, textarea"); inputs.forEach(function (input) { input.addEventListener( "focus", function () { if (!tracked) { tracked = true; CRO.trackEvent("cro_form_started", formId, banner._variant_id); } }, { once: true } ); }); // Track form submission form.addEventListener("submit", function () { CRO.trackEvent("cro_form_submitted", formId, banner._variant_id); }); } // Run after DOM is ready if (document.readyState === "complete") { enhance(); } else { window.addEventListener("load", enhance); } }); })();