diff --git a/app.js b/app.js index 2c60ebe..eea282d 100644 --- a/app.js +++ b/app.js @@ -20,6 +20,124 @@ if (signupEl) signupEl.textContent = text; })(); +// ── Benefit Calculator ─────────────────────────────────── +(function initCalculator() { + const overlay = document.getElementById('calcOverlay'); + const openBtn = document.getElementById('openCalc'); + const closeBtn = document.getElementById('calcClose'); + const submitBtn = document.getElementById('calcSubmit'); + const recalcBtn = document.getElementById('calcRecalc'); + const calcForm = document.getElementById('calcForm'); + const calcRes = document.getElementById('calcResults'); + const calcErr = document.getElementById('calcError'); + const ctaBtn = document.getElementById('calcCTABtn'); + + if (!overlay) return; + + function open() { overlay.classList.add('open'); document.body.style.overflow = 'hidden'; } + function close() { overlay.classList.remove('open'); document.body.style.overflow = ''; } + + openBtn?.addEventListener('click', open); + closeBtn?.addEventListener('click', close); + overlay.addEventListener('click', e => { if (e.target === overlay) close(); }); + document.addEventListener('keydown', e => { if (e.key === 'Escape') close(); }); + + recalcBtn?.addEventListener('click', () => { + calcRes.classList.add('hidden'); + calcForm.classList.remove('hidden'); + }); + + // Close modal and scroll to signup when CTA clicked + ctaBtn?.addEventListener('click', () => { + close(); + }); + + submitBtn?.addEventListener('click', () => { + const homesites = parseFloat(document.getElementById('calcHomesites').value) || 0; + const propertyType = document.getElementById('calcPropertyType').value; + const annualIncome = parseFloat(document.getElementById('calcAnnualIncome').value) || 0; + const paymentFreq = document.getElementById('calcPaymentFreq').value; + const reserveFunds = parseFloat(document.getElementById('calcReserveFunds').value) || 0; + const interest2025 = parseFloat(document.getElementById('calcInterest2025').value) || 0; + + if (!homesites || !annualIncome) { + calcErr.classList.remove('hidden'); + return; + } + calcErr.classList.add('hidden'); + + // ── Conservative investment assumptions ── + // Operating cash: depending on payment frequency, portion investable in high-yield savings + const opMultiplier = { monthly: 0.10, quarterly: 0.20, annually: 0.35 }[paymentFreq] || 0.10; + const opRate = 0.040; // 4.0% money market / HYSA + const resRatio = 0.65; // 65% of reserves investable (keep 35% liquid) + const resRate = 0.0425; // 4.25% CD ladder / short-term treasuries + + const investableOp = annualIncome * opMultiplier; + const investableRes = reserveFunds * resRatio; + const opInterest = Math.round(investableOp * opRate); + const resInterest = Math.round(investableRes * resRate); + const totalPotential = opInterest + resInterest; + const increase = totalPotential - interest2025; + const pctIncrease = interest2025 > 0 + ? Math.round((increase / interest2025) * 100) + : (totalPotential > 0 ? 100 : 0); + + // ── Populate results ── + const fmt = n => '$' + Math.round(n).toLocaleString(); + + document.getElementById('resultAmount').textContent = fmt(totalPotential); + document.getElementById('resultCurrent').textContent = fmt(interest2025); + document.getElementById('resultOperating').textContent = fmt(opInterest); + document.getElementById('resultReserve').textContent = fmt(resInterest); + + const badge = document.getElementById('resultBadge'); + if (increase > 0) { + badge.textContent = `+${fmt(increase)} · +${pctIncrease}%`; + badge.style.display = 'inline-block'; + } else { + badge.style.display = 'none'; + } + + // ── AI-style suggestion text ── + const typeLabels = { sfh:'single-family home', townhomes:'townhome', condos:'condo', mixed:'mixed-use', '':'' }; + const typeLabel = typeLabels[propertyType] || ''; + const freqLabel = { monthly:'monthly', quarterly:'quarterly', annually:'annual' }[paymentFreq]; + const communityDesc = [homesites && `${homesites}-unit`, typeLabel, 'community'].filter(Boolean).join(' '); + + let ai = `Based on your ${communityDesc} collecting ${fmt(annualIncome)} in ${freqLabel} dues`; + if (reserveFunds > 0) ai += ` and ${fmt(reserveFunds)} in reserve funds`; + ai += `, a conservative investment strategy could generate approximately ${fmt(totalPotential)} in annual interest income. `; + if (resInterest > 0) ai += `Deploying ${fmt(investableRes)} of your reserve funds into a short-term CD ladder at ~4.25% yields ${fmt(resInterest)} annually. `; + if (opInterest > 0) ai += `Keeping a ${fmt(investableOp)} operating cash buffer in a high-yield money market at ~4.0% adds another ${fmt(opInterest)}. `; + if (interest2025 > 0 && increase > 0) { + ai += `That's a ${fmt(increase)} improvement (+${pctIncrease}%) over your 2025 interest income of ${fmt(interest2025)} — with no additional risk.`; + } else if (interest2025 === 0) { + ai += `This would represent entirely new interest income for your community at no additional risk.`; + } + + document.getElementById('calcAiText').textContent = ai; + + // ── Animate the main number ── + animateValue(document.getElementById('resultAmount'), 0, totalPotential); + + calcForm.classList.add('hidden'); + calcRes.classList.remove('hidden'); + }); + + function animateValue(el, from, to) { + const duration = 900; + const start = performance.now(); + function step(now) { + const progress = Math.min((now - start) / duration, 1); + const ease = 1 - Math.pow(1 - progress, 3); + el.textContent = '$' + Math.round(from + (to - from) * ease).toLocaleString(); + if (progress < 1) requestAnimationFrame(step); + } + requestAnimationFrame(step); + } +})(); + // ── Screenshot Carousel ────────────────────────────────── (function initCarousel() { const carousel = document.getElementById('screenshotCarousel'); diff --git a/index.html b/index.html index 4375105..c23efcb 100644 --- a/index.html +++ b/index.html @@ -63,6 +63,7 @@