const questions = [ // SECTION 1: Customer Base Complexity (Index: 0) { id: "complexity_volume", section: 0, type: "slider", text: "How many active customer accounts does your business manage?", desc: "", min: 10, max: 5000, step: 10, default: 150, labels: ["10", "1,000", "2,500", "5,000+"] }, { id: "complexity_gtm", section: 0, type: "radio", text: "What is your average Annual Contract Value (ACV) per account?", desc: "Your average deal size dictates your risk exposure and capabilities needed to protect accounts.", options: [ { label: "Under £5,000 / year", value: 60, acv: 2400 }, { label: "£5,000 - £25,000 / year", value: 80, acv: 18000 }, { label: "£25,000 - £50,000 / year", value: 40, acv: 35000 }, { label: "Over £50,000 / year", value: 50, acv: 75000 } ] }, // SECTION 2: Data Fragmentation (Index: 1) { id: "frag_tools", section: 1, type: "checkbox", text: "Which systems store your customer health and product usage data?", desc: "Select all that apply.", items: [ { id: "crm", label: "CRM (e.g. Salesforce / HubSpot)", icon: "crm" }, { id: "cs", label: "CS Platform (e.g. Gainsight / ChurnZero)", icon: "cs" }, { id: "db", label: "Warehouse / DB (e.g. Snowflake / PostgreSQL)", icon: "db" }, { id: "analytics", label: "Product Analytics (e.g. Mixpanel / Amplitude)", icon: "analytics" }, { id: "helpdesk", label: "Helpdesk (e.g. Zendesk / Intercom)", icon: "helpdesk" }, { id: "spreadsheets", label: "Spreadsheets / Manual Trackers", icon: "spreadsheets" } ] }, // SECTION 3: Risk Prediction & Action (Index: 2) { id: "risk_ident", section: 2, type: "radio", text: "How do you predict risk and what do you do about it?", desc: "Your methods for identifying churn threat and standardising response determine recovery success.", options: [ { label: "Reactive & Ad-hoc", value: 15 }, { label: "Manual Checks & Checklists", value: 45 }, { label: "Rule-Based Triggers & Manual Outreach", value: 75 }, { label: "Predictive Automation & Auto-Playbooks", value: 100 }, { label: "Other", value: 30, isOther: true } ] }, // SECTION 4: Revenue Operations Efficiency (Index: 3) { id: "revops_time", section: 3, type: "radio", text: "How much time do you spend manually compiling health reports weekly?", desc: "", options: [ { label: "Less than 1 hour per week", value: 100, hours: 0.5 }, { label: "1 - 3 hours per week", value: 75, hours: 2.0 }, { label: "3 - 5 hours per week", value: 40, hours: 4.0 }, { label: "5+ hours per week", value: 15, hours: 7.0 } ] } ]; const sections = [ { name: "Customer Base Complexity", weight: 0.10 }, { name: "Data Fragmentation", weight: 0.30 }, { name: "Risk Prediction & Action", weight: 0.35 }, { name: "Revenue Operations Efficiency", weight: 0.25 } ]; let currentStep = 0; let userAnswers = {}; let leadDetails = {}; document.addEventListener("DOMContentLoaded", () => { // Bind Landing CTA -> Opens Lead Capture Screen first const startBtn = document.getElementById("start-audit-btn"); if (startBtn) { startBtn.addEventListener("click", () => { showLeadCaptureScreen(); }); } // Bind Wizard Navigation Buttons const prevBtn = document.getElementById("prev-btn"); if (prevBtn) { prevBtn.addEventListener("click", handlePrev); } const nextBtn = document.getElementById("next-btn"); if (nextBtn) { nextBtn.addEventListener("click", handleNext); } // Bind Lead Form Submission const leadForm = document.getElementById("lead-capture-form"); if (leadForm) { leadForm.addEventListener("submit", handleLeadSubmit); } // Bind Restart Button const restartBtn = document.getElementById("restart-audit-btn"); if (restartBtn) { restartBtn.addEventListener("click", () => { currentStep = 0; userAnswers = {}; leadDetails = {}; const nameInput = document.getElementById("lead-name"); if (nameInput) nameInput.value = ""; const emailInput = document.getElementById("lead-email"); if (emailInput) emailInput.value = ""; const roleInput = document.getElementById("lead-role"); if (roleInput) roleInput.value = ""; const phoneInput = document.getElementById("lead-phone"); if (phoneInput) phoneInput.value = ""; const optinInput = document.getElementById("lead-optin"); if (optinInput) optinInput.checked = false; const resultsView = document.getElementById("results-view"); if (resultsView) resultsView.classList.remove("active"); const landingView = document.getElementById("landing-view"); if (landingView) { landingView.classList.add("active"); } else { const leadView = document.getElementById("lead-view"); if (leadView) leadView.classList.add("active"); } }); } // Fallback: If no landing-view exists on page load, make lead-view active immediately if (!document.getElementById("landing-view")) { const leadView = document.getElementById("lead-view"); if (leadView) leadView.classList.add("active"); } }); function transitionToStep(stepIndex) { const landingView = document.getElementById("landing-view"); if (landingView) { landingView.classList.remove("active"); } const previewContainer = document.getElementById("risqly-landing-preview-container"); if (previewContainer) { previewContainer.style.display = "none"; } const assessmentView = document.getElementById("assessment-view"); if (assessmentView) assessmentView.classList.add("active"); const leadView = document.getElementById("lead-view"); if (leadView) leadView.classList.remove("active"); const resultsView = document.getElementById("results-view"); if (resultsView) resultsView.classList.remove("active"); currentStep = stepIndex; renderQuestion(currentStep); updateProgressBar(); } function renderQuestion(index) { const q = questions[index]; const container = document.getElementById("question-container"); container.innerHTML = ""; const title = document.createElement("h2"); title.className = "risqly-question-title"; title.textContent = q.text; if (q.id === "complexity_volume") { title.style.setProperty("margin-bottom", "18px", "important"); } else if (q.id === "revops_time") { title.style.setProperty("margin-bottom", "18px", "important"); } container.appendChild(title); if (q.desc) { const desc = document.createElement("p"); desc.className = "risqly-question-desc"; desc.textContent = q.desc; container.appendChild(desc); } if (q.type === "radio" || q.type === "binary") { const optionsWrapper = document.createElement("div"); optionsWrapper.className = "risqly-question-options"; q.options.forEach((opt, oIndex) => { const card = document.createElement("div"); card.className = "risqly-option-card"; const isSelected = userAnswers[q.id] === oIndex; if (isSelected) card.classList.add("risqly-selected"); card.innerHTML = `
${opt.label}
${opt.hint ? `
${opt.hint}
` : ''}
`; card.addEventListener("click", (e) => { if (e.target.tagName === "INPUT") return; userAnswers[q.id] = oIndex; renderQuestion(index); updateNavigationButtons(); }); if (opt.isOther && isSelected) { const inputWrapper = document.createElement("div"); inputWrapper.style.marginTop = "10px"; inputWrapper.style.width = "100%"; const otherInput = document.createElement("input"); otherInput.type = "text"; otherInput.className = "form-input"; otherInput.placeholder = "Please specify..."; otherInput.style.width = "100%"; otherInput.style.boxSizing = "border-box"; otherInput.value = userAnswers[q.id + "_other_text"] || ""; otherInput.addEventListener("input", (ev) => { userAnswers[q.id + "_other_text"] = ev.target.value; updateNavigationButtons(); }); inputWrapper.appendChild(otherInput); const infoDiv = card.querySelector(".risqly-option-info"); infoDiv.appendChild(inputWrapper); } optionsWrapper.appendChild(card); }); container.appendChild(optionsWrapper); } else if (q.type === "slider") { const sliderWrapper = document.createElement("div"); sliderWrapper.className = "risqly-slider-container"; sliderWrapper.style.setProperty("margin-top", "60px", "important"); const currentValue = userAnswers[q.id] !== undefined ? userAnswers[q.id] : q.default; userAnswers[q.id] = currentValue; const bubble = document.createElement("div"); bubble.className = "risqly-slider-val-bubble"; bubble.id = "slider-bubble"; bubble.textContent = formatSliderValue(q.id, currentValue); const input = document.createElement("input"); input.type = "range"; input.className = "risqly-range-slider"; input.style.width = "100%"; input.style.boxSizing = "border-box"; if (q.id === "complexity_volume") { input.min = 0; input.max = 300; input.step = 1; input.value = mapValueToSliderPos(currentValue); } else { input.min = q.min; input.max = q.max; input.step = q.step; input.value = currentValue; } if (q.id === "complexity_volume") { updateSliderBackground(input, 0, 300, mapValueToSliderPos(currentValue)); } else { updateSliderBackground(input, q.min, q.max, currentValue); } input.addEventListener("input", (e) => { const rawVal = parseFloat(e.target.value); let val = rawVal; if (q.id === "complexity_volume") { val = mapSliderPosToValue(rawVal); } userAnswers[q.id] = val; bubble.textContent = formatSliderValue(q.id, val); const pct = (rawVal - input.min) / (input.max - input.min); bubble.style.left = `calc(${pct * 100}% - ${(pct - 0.5) * 20}px)`; updateSliderBackground(e.target, input.min, input.max, rawVal); }); sliderWrapper.appendChild(bubble); sliderWrapper.appendChild(input); const labelsRow = document.createElement("div"); labelsRow.className = "risqly-slider-labels"; q.labels.forEach((label, idx) => { const span = document.createElement("span"); span.innerHTML = label; const pct = (idx / (q.labels.length - 1)) * 100; span.style.left = `${pct}%`; labelsRow.appendChild(span); }); sliderWrapper.appendChild(labelsRow); container.appendChild(sliderWrapper); setTimeout(() => { const rawVal = q.id === "complexity_volume" ? mapValueToSliderPos(currentValue) : currentValue; const pct = (rawVal - input.min) / (input.max - input.min); bubble.style.left = `calc(${pct * 100}% - ${(pct - 0.5) * 20}px)`; }, 0); } else if (q.type === "checkbox") { const grid = document.createElement("div"); grid.className = "risqly-tool-grid"; if (!userAnswers[q.id]) userAnswers[q.id] = []; q.items.forEach(item => { const card = document.createElement("div"); card.className = "risqly-tool-card"; const isChecked = userAnswers[q.id].includes(item.id); if (isChecked) card.classList.add("risqly-selected"); card.innerHTML = `
${getToolIconSvg(item.icon)}
${item.label}
`; card.addEventListener("click", () => { let list = [...userAnswers[q.id]]; if (list.includes(item.id)) { list = list.filter(id => id !== item.id); } else { list.push(item.id); } userAnswers[q.id] = list; renderQuestion(index); updateNavigationButtons(); }); grid.appendChild(card); }); container.appendChild(grid); } updateNavigationButtons(); } function updateSliderBackground(slider, min, max, value) { const percentage = ((value - min) / (max - min)) * 100; slider.style.background = `linear-gradient(to right, var(--primary) 0%, var(--primary) ${percentage}%, rgba(0, 0, 0, 0.06) ${percentage}%, rgba(0, 0, 0, 0.06) 100%)`; } function formatSliderValue(id, val) { if (id === "complexity_volume") { return `${val.toLocaleString()} accounts`; } if (id === "frag_unity") { const texts = ["Siloed", "Fragmented", "Partially Unified", "Unified", "Real-Time"]; return texts[val - 1]; } return val; } function mapSliderPosToValue(pos) { const p = pos / 300; if (p <= 1/3) { const pPrime = p * 3; return Math.round(10 + pPrime * (1000 - 10)); } else if (p <= 2/3) { const pPrime = (p - 1/3) * 3; return Math.round(1000 + pPrime * (2500 - 1000)); } else { const pPrime = (p - 2/3) * 3; return Math.round(2500 + pPrime * (5000 - 2500)); } } function mapValueToSliderPos(val) { if (val <= 1000) { const pPrime = (val - 10) / (1000 - 10); const p = pPrime / 3; return p * 300; } else if (val <= 2500) { const pPrime = (val - 1000) / (2500 - 1000); const p = (pPrime / 3) + (1/3); return p * 300; } else { const pPrime = (val - 2500) / (5000 - 2500); const p = (pPrime / 3) + (2/3); return p * 300; } } function getToolIconSvg(key) { const svgs = { crm: ``, cs: ``, db: ``, analytics: ``, helpdesk: ``, spreadsheets: `` }; return svgs[key] || svgs.crm; } function updateProgressBar() { const total = questions.length; const pct = Math.round((currentStep / total) * 100); document.getElementById("progress-fill").style.width = `${pct}%`; document.getElementById("risqly-progress-percent").textContent = `${pct}% Complete`; const currentSectionIdx = questions[currentStep].section; const indicators = document.querySelectorAll(".risqly-step-indicator"); indicators.forEach((ind, iIndex) => { ind.className = "risqly-step-indicator"; if (iIndex === currentSectionIdx) { ind.classList.add("active"); } else if (iIndex < currentSectionIdx) { ind.classList.add("completed"); } }); } function updateNavigationButtons() { const q = questions[currentStep]; const nextBtn = document.getElementById("next-btn"); const prevBtn = document.getElementById("prev-btn"); if (currentStep === 0) { prevBtn.style.visibility = "hidden"; } else { prevBtn.style.visibility = "visible"; } let isAnswered = false; if (q.type === "radio" || q.type === "binary") { isAnswered = userAnswers[q.id] !== undefined; if (isAnswered && q.options && q.options[userAnswers[q.id]] && q.options[userAnswers[q.id]].isOther) { const otherText = userAnswers[q.id + "_other_text"] || ""; isAnswered = otherText.trim().length > 0; } } else if (q.type === "slider") { isAnswered = true; } else if (q.type === "checkbox") { isAnswered = userAnswers[q.id] && userAnswers[q.id].length > 0; } if (isAnswered) { nextBtn.className = "btn btn-primary"; nextBtn.removeAttribute("disabled"); } else { nextBtn.className = "btn risqly-btn-disabled"; nextBtn.setAttribute("disabled", "true"); } if (currentStep === questions.length - 1) { nextBtn.innerHTML = `Calculate `; } else { nextBtn.innerHTML = `Continue `; } } function handlePrev() { if (currentStep > 0) { currentStep--; renderQuestion(currentStep); updateProgressBar(); } } function handleNext() { const q = questions[currentStep]; if (q.type === "radio" || q.type === "binary") { if (userAnswers[q.id] === undefined) return; } else if (q.type === "checkbox") { if (!userAnswers[q.id] || userAnswers[q.id].length === 0) return; } if (currentStep < questions.length - 1) { currentStep++; renderQuestion(currentStep); updateProgressBar(); } else { // Last step complete, transition directly to Results Dashboard showResultsDashboardScreen(); } } function showLeadCaptureScreen() { const landingView = document.getElementById("landing-view"); if (landingView) landingView.classList.remove("active"); const assessmentView = document.getElementById("assessment-view"); if (assessmentView) assessmentView.classList.remove("active"); const resultsView = document.getElementById("results-view"); if (resultsView) resultsView.classList.remove("active"); const leadView = document.getElementById("lead-view"); if (leadView) leadView.classList.add("active"); } function getReadableAnswer(question) { const value = userAnswers[question.id]; if (value === undefined || value === null) { return "Not answered"; } if (question.type === "slider") { if (question.labels && question.labels.length > 0) { if (question.min === 1 && question.max === question.labels.length) { const label = question.labels[value - 1] || ""; return `${value} (${label.replace(/
/g, " ")})`; } } return value.toString(); } if (question.type === "checkbox") { if (Array.isArray(value)) { return value.map(id => { const item = question.items.find(i => i.id === id); return item ? item.label : id; }).join(", "); } return ""; } if (question.type === "radio" || question.type === "binary") { const option = question.options[value]; if (option && option.isOther) { return "Other: " + (userAnswers[question.id + "_other_text"] || "Not specified"); } return option ? option.label : `Option ${value}`; } return value.toString(); } function submitFormToWebflow(formName, fields) { const siteId = document.documentElement.getAttribute("data-wf-site"); if (!siteId) { console.log(`[Local Staging] Simulated Webflow form submission: "${formName}"`, fields); return; } let formId = ""; let idPrefix = ""; if (formName === "Risqly Audit Lead") { formId = "wf-audit-form"; idPrefix = "wf-audit-"; } else if (formName === "Risqly Beta Request") { formId = "wf-beta-form"; idPrefix = "wf-beta-"; } const form = document.getElementById(formId); if (!form) { console.warn(`[Webflow Submit] Hidden form not found for: ${formName}`); return; } // Populate hidden input elements for (const [key, value] of Object.entries(fields)) { let inputIdKey = key.toLowerCase(); if (inputIdKey === "cs-tool") inputIdKey = "cs"; if (inputIdKey === "overall-score") inputIdKey = "score"; if (inputIdKey === "maturity-tier") inputIdKey = "tier"; const inputId = idPrefix + inputIdKey; let input = document.getElementById(inputId); if (!input) { input = document.createElement("input"); input.type = "hidden"; input.id = inputId; form.appendChild(input); } input.name = key; input.setAttribute("data-name", key); input.value = value !== undefined && value !== null ? value : ""; } // Find or dynamically create a hidden submit button const submitBtnId = idPrefix + "submit-btn"; let submitBtn = document.getElementById(submitBtnId); if (!submitBtn) { submitBtn = document.createElement("input"); submitBtn.type = "submit"; submitBtn.id = submitBtnId; submitBtn.style.display = "none"; form.appendChild(submitBtn); } // Directly trigger Webflow's form handler via jQuery submit, or fall back to native dispatchEvent. // This triggers Webflow's native form listener to capture and send it via AJAX. try { if (window.jQuery) { window.jQuery(form).submit(); console.log(`Webflow native form submission triggered via jQuery submit(): "${formName}"`); } else { const event = new Event("submit", { bubbles: true, cancelable: true }); form.dispatchEvent(event); console.log(`Webflow native form submission triggered via dispatchEvent: "${formName}"`); } } catch (err) { console.warn(`Webflow submit event triggering failed, falling back to click:`, err); try { submitBtn.click(); } catch (clickErr) { console.error("Webflow native form submission click fallback failed:", clickErr); } } } const ZAPIER_WEBHOOK_URL = "https://hook.eu1.make.com/8ldca5j95llbhon64xbpnqhrdp4pgm62"; function buildPDFElement(scores) { const dateStr = new Date().toLocaleDateString("en-GB", { day: "numeric", month: "long", year: "numeric" }); const company = leadDetails.company || "Your Company"; const name = leadDetails.name || ""; const overallScore = scores.overallScore || 0; const tier = scores.tier || "Reactive"; const tierDesc = scores.tierDesc || ""; const cats = scores.categories || {}; const logoWhiteUrl = "https://cdn.prod.website-files.com/691eff3fa46175f70dca8dd8/6a1aee0ed891929be360995f_Untitled%20(450%20x%20250%20px)%20(1).png"; const logoDarkUrl = "https://cdn.prod.website-files.com/691eff3fa46175f70dca8dd8/691effd3a43669d5b9e18633_Risqly-Logo.png"; function scoreColor(s) { return s >= 70 ? "#C6ECD3" : s >= 30 ? "#fbefaf" : "#D80027"; } function fmt(v) { if (v >= 1000000) return "£" + (v / 1000000).toFixed(1) + "M"; if (v >= 1000) return "£" + Math.round(v / 1000) + "K"; return "£" + (v || 0); } const catRows = [ ["Customer Base Complexity", cats.complexity || 0], ["Data Fragmentation", cats.fragmentation || 0], ["Risk Prediction & Action", cats.riskPrediction || 0], ["Revenue Operations Efficiency", cats.revops || 0] ].map(([label, score]) => ` ${label}
${score}/100 `).join(""); const answerRows = questions.map(q => ` ${q.text} ${getReadableAnswer(q)} `).join(""); const main = `
Risqly Logo
RETENTION HEALTH AUDIT REPORT
${company}
${dateStr} · Prepared for ${name}
${overallScore}
Overall Retention Health Score
${tier} Maturity
${tierDesc}
${fmt(scores.arrExposure)}
ARR at Risk
${scores.blindSpotPct || 0}%
Retention Blind Spot
${scores.collectiveCSMHours || 0}h
Weekly CSM Overhead

Section Score Breakdown

How your organisation scored across the 4 retention capability dimensions.
${catRows}
Risqly Logo Dark

Your Assessment Answers

A record of your responses to the Retention Health Audit questionnaire.
${answerRows}
Want to improve your score?
Sign up for a 7-day free trial to see how Risqly can help you retain revenue and expand your accounts.
Start 7-Day Free Trial
CONFIDENTIAL · Generated by Risqly Retention Health Audit · ${dateStr}
`; return main; } function generatePDFReport(sendViaEmail = false) { if (typeof html2pdf === "undefined") { alert("PDF generation library is still loading. Please try again in a moment."); return Promise.reject("html2pdf undefined"); } const scores = window.lastComputedScores; if (!scores) { console.warn("No scores for PDF"); return Promise.reject("no scores"); } const sanitizedCompany = (leadDetails.company || "Company").replace(/[^a-z0-9]/gi, "-"); const pdfHtml = buildPDFElement(scores); const opt = { margin: [0, 0, 0, 0], filename: `Risqly-Retention-Audit-${sanitizedCompany}.pdf`, image: { type: "jpeg", quality: 0.98 }, html2canvas: { scale: 2, useCORS: true, logging: false, scrollX: 0, scrollY: 0 }, jsPDF: { unit: "mm", format: "a4", orientation: "portrait" }, pagebreak: { mode: ["css", "legacy"] } }; if (sendViaEmail) { return html2pdf().set(opt).from(pdfHtml, 'string').outputPdf("blob").then(blob => { console.log("PDF Blob size:", blob.size); return blobToBase64(blob); }).then(base64 => { console.log("Base64 length:", base64.length); const payload = { recipient_name: leadDetails.name || "", recipient_email: leadDetails.email || "", company: leadDetails.company || "", role: leadDetails.role || "", crm: leadDetails.crm || "", cs_tool: leadDetails.cs || "", overall_score: scores.overallScore || "", maturity_tier: scores.tier || "", pdf_filename: `Risqly-Retention-Audit-${sanitizedCompany}.pdf`, pdf_base64: base64 }; // Append all 12 questionnaire answers to the payload if (typeof questions !== "undefined" && typeof userAnswers !== "undefined") { questions.forEach(q => { const payloadKey = "q_" + q.id; payload[payloadKey] = getReadableAnswer(q); }); } return fetch(ZAPIER_WEBHOOK_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); }).then(res => { if (res && res.ok) { console.log("Webhook success, PDF sent via email."); } else { console.warn("Webhook status:", res ? res.status : "unknown"); } return res; }).catch(err => { console.error("PDF email dispatch failed:", err); throw err; }); } else { return html2pdf().set(opt).from(pdfHtml, 'string').save() .catch(err => { console.error("PDF generation failed:", err); throw err; }); } } function blobToBase64(blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => { const base64 = reader.result.split(",")[1]; resolve(base64); }; reader.onerror = reject; reader.readAsDataURL(blob); }); } function showEmailSentBanner() { let banner = document.getElementById("risqly-email-banner"); if (!banner) { banner = document.createElement("div"); banner.id = "risqly-email-banner"; banner.style.cssText = [ "position:fixed", "bottom:32px", "left:50%", "transform:translateX(-50%)", "background:linear-gradient(135deg,#7c3aed,#4f46e5)", "color:#fff", "padding:14px 28px", "border-radius:12px", "font-size:15px", "font-weight:600", "box-shadow:0 8px 32px rgba(124,58,237,0.35)", "z-index:99999", "display:flex", "align-items:center", "gap:10px", "transition:opacity 0.4s" ].join(";"); banner.innerHTML = ` Your report has been sent to ${leadDetails.email || "your email"} `; document.body.appendChild(banner); } banner.style.opacity = "1"; banner.style.display = "flex"; setTimeout(() => { banner.style.opacity = "0"; setTimeout(() => { banner.style.display = "none"; }, 400); }, 5000); } // Handle Form Submission at the start of audit function handleLeadSubmit(e) { e.preventDefault(); leadDetails.name = document.getElementById("lead-name").value.trim(); leadDetails.email = document.getElementById("lead-email").value.trim(); leadDetails.role = document.getElementById("lead-role").value.trim(); const phoneInput = document.getElementById("lead-phone"); leadDetails.phone = phoneInput ? phoneInput.value.trim() : ""; // Set default fallbacks for Webflow / webhook fields we no longer ask let extractedCompany = "Your Company"; if (leadDetails.email && leadDetails.email.includes("@")) { const domain = leadDetails.email.split("@")[1].trim().toLowerCase(); const domainName = domain.split(".")[0]; const commonDomains = ["gmail", "outlook", "hotmail", "yahoo", "icloud", "mail", "aol", "zoho", "proton", "protonmail"]; if (!commonDomains.includes(domainName)) { extractedCompany = domainName.charAt(0).toUpperCase() + domainName.slice(1); } } leadDetails.company = extractedCompany; leadDetails.crm = "None"; leadDetails.cs = "None"; if (!leadDetails.name || !leadDetails.email || !leadDetails.role) { alert("Please complete all required fields."); return; } // transition to step 0 of the questionnaire transitionToStep(0); } // Show Results Dashboard Screen and send webhook/PDF function showResultsDashboardScreen() { const assessmentView = document.getElementById("assessment-view"); if (assessmentView) assessmentView.classList.remove("active"); const resultsView = document.getElementById("results-view"); if (resultsView) resultsView.classList.add("active"); const scores = calculateScores(); window.lastComputedScores = scores; renderResultsDashboard(scores); // Submit to Webflow's native form processor const formFields = { "Name": leadDetails.name, "Email": leadDetails.email, "Company": leadDetails.company, "Role": leadDetails.role, "Phone": leadDetails.phone, "CRM": leadDetails.crm, "CS-Tool": leadDetails.cs, "Maturity-Tier": scores.tier, "Overall-Score": scores.overallScore }; // Add all questionnaire answers if (typeof questions !== "undefined" && typeof userAnswers !== "undefined") { questions.forEach(q => { const fieldKey = "Q-" + q.id.split("_").map(word => { if (word.toUpperCase() === "GTM") return "GTM"; if (word.toUpperCase() === "CRM") return "CRM"; if (word.toUpperCase() === "CS") return "CS"; return word.charAt(0).toUpperCase() + word.slice(1); }).join("-"); formFields[fieldKey] = getReadableAnswer(q); }); } // Generate and email the PDF report. Once complete, submit to Webflow. generatePDFReport(true) .then(() => { submitFormToWebflow("Risqly Audit Lead", formFields); }) .catch(err => { console.error("Error during PDF flow, proceeding with Webflow submit anyway:", err); submitFormToWebflow("Risqly Audit Lead", formFields); }); } function calculateScores() { // 1. Customer Complexity Subscore const gtmIdx = userAnswers["complexity_gtm"]; const gtmScore = questions[1].options[gtmIdx].value; const averageACV = questions[1].options[gtmIdx].acv || 15000; const complexityScore = gtmScore; // 2. Data Fragmentation Subscore const tools = userAnswers["frag_tools"] || []; let toolsScore = 100; if (tools.includes("spreadsheets")) toolsScore -= 30; if (!tools.includes("cs")) toolsScore -= 20; if (tools.length >= 4) toolsScore -= 15; toolsScore = Math.max(10, toolsScore); const fragmentationScore = toolsScore; // 3. Risk Prediction & Action Subscore const riskIdx = userAnswers["risk_ident"]; const riskScore = questions[3].options[riskIdx].value; // 4. RevOps Efficiency Subscore const timeIdx = userAnswers["revops_time"]; const timeScore = questions[4].options[timeIdx].value; const manualHours = questions[4].options[timeIdx].hours || 4; const revopsScore = timeScore; // Overall Weighted Score (out of 100) const overallScore = Math.round( (complexityScore * sections[0].weight) + (fragmentationScore * sections[1].weight) + (riskScore * sections[2].weight) + (revopsScore * sections[3].weight) ); // Compute Maturity Tiers based on red (0-44), yellow (45-69), green (70+) boundaries let tier = "Reactive"; let tierDesc = "Risqly can help you combine fragmented data sources into a single retention view, giving you visibility of how much revenue is at risk and what to do next."; let tierClass = "reactive"; if (overallScore >= 70) { tier = "Predictive"; tierDesc = "Risqly can unlock greater visibility of expansion opportunities across your customer base, helping you move from retention to revenue growth."; tierClass = "predictive"; } else if (overallScore >= 45) { tier = "Emerging"; tierDesc = "Risqly can help mature your retention systems so you can consolidate fragmented data, quantify revenue at risk, and build a clear action plan."; tierClass = "emerging"; } const contracts = userAnswers["complexity_volume"] || 150; const estimatedArr = contracts * averageACV; const riskPct = 100 - overallScore; const rawRiskExposureVal = estimatedArr * (riskPct / 100) * 0.18; const arrExposure = Math.round(rawRiskExposureVal); const blindSpotPct = Math.round(100 - overallScore); const numCSMs = Math.max(1, Math.round(contracts / 65)); const collectiveCSMHours = numCSMs * manualHours; return { overallScore, categories: { complexity: Math.round(complexityScore), fragmentation: Math.round(fragmentationScore), riskPrediction: Math.round(riskScore), revops: Math.round(revopsScore) }, tier, tierDesc, tierClass, estimatedArr, arrExposure, blindSpotPct, collectiveCSMHours, numCSMs, manualHoursPerCsm: manualHours }; } function renderResultsDashboard(scores) { // Update Header details const leadMetaEl = document.getElementById("results-lead-meta"); if (leadMetaEl) { let metaText = `${leadDetails.name} • ${leadDetails.role}`; if (leadDetails.email) metaText += ` • ${leadDetails.email}`; if (leadDetails.phone) metaText += ` • ${leadDetails.phone}`; leadMetaEl.textContent = metaText; } const timestampEl = document.getElementById("results-timestamp"); if (timestampEl) { timestampEl.textContent = new Date().toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' }); } // 1. Set Gauge Score const scoreValEl = document.getElementById("dashboard-score-val"); if (scoreValEl) scoreValEl.textContent = scores.overallScore; const circle = document.getElementById("dashboard-gauge-fill"); if (circle) { const offset = 377 * (1 - (scores.overallScore / 100)); circle.getBoundingClientRect(); circle.style.strokeDashoffset = offset; // Set color dynamically: 0-29 Red (#D80027), 30-69 Yellow (#fbefaf), 70+ Green (#C6ECD3) let gaugeColor = "#C6ECD3"; // Green default let textColor = "#198754"; // Readable dark green for text if (scores.overallScore < 30) { gaugeColor = "#D80027"; // Red textColor = "#D80027"; // Readable red for text } else if (scores.overallScore < 70) { gaugeColor = "#fbefaf"; // Yellow textColor = "#b58900"; // Readable dark gold/yellow for text } circle.style.stroke = gaugeColor; const resultsMaturityTitle = document.getElementById("results-maturity-title"); if (resultsMaturityTitle) { resultsMaturityTitle.style.color = textColor; } } // Update dynamic values inside results page text const resultsMaturityTitle = document.getElementById("results-maturity-title"); if (resultsMaturityTitle) { resultsMaturityTitle.textContent = `Maturity Tier: ${scores.tier}`; } const resultsMaturityDesc = document.getElementById("results-maturity-desc"); if (resultsMaturityDesc) { resultsMaturityDesc.textContent = scores.tierDesc; } } function renderCategoryProgress(id, score, avg, rec) { const container = document.getElementById(id); if (!container) return; const scoreLabel = container.querySelector(".cat-score"); if (scoreLabel) scoreLabel.textContent = `${score}/100`; const fill = container.querySelector(".cat-bar-fill"); if (fill) { fill.getBoundingClientRect(); fill.style.width = `${score}%`; if (score < 40) { fill.style.background = "#D80027"; } else if (score < 70) { fill.style.background = "#fbefaf"; } else { fill.style.background = "#C6ECD3"; } } const mark = container.querySelector(".cat-bar-benchmark"); if (mark) { mark.style.left = `${avg}%`; } const recEl = container.querySelector(".cat-recommendation"); if (recEl) { recEl.textContent = rec; } }