var tierCount = 0, launchCount = 0, sponsorCount = 0, coachCount = 0; var CUR = { symbol: '$', code: 'USD' }; var MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December']; // Currency config β€” symbol and display label var CURRENCIES = { USD: { symbol: '$', label: '$ USD' }, CAD: { symbol: '$', label: '$ CAD' }, GBP: { symbol: 'Β£', label: 'Β£ GBP' }, AUD: { symbol: '$', label: '$ AUD' }, EUR: { symbol: '€', label: '€ EUR' } }; function setCurrency(code) { CUR = { symbol: CURRENCIES[code].symbol, code: code }; ['USD','CAD','GBP','AUD','EUR'].forEach(function(c) { var btn = document.getElementById('cur'+c); if (btn) btn.classList.toggle('on', c === code); }); document.getElementById('goalPrefix').textContent = CUR.symbol; document.querySelectorAll('.cur-prefix').forEach(function(el) { el.textContent = CUR.symbol; }); calc(); } function fmt(n) { return CUR.symbol + Math.round(n).toLocaleString(); } function tstr(m) { var y = Math.floor(m/12), mo = m%12; if (y > 0 && mo > 0) return y+'y '+mo+'mo'; if (y > 0) return y+' year'+(y>1?'s':''); return m+' month'+(m!==1?'s':''); } function initDates() { var now = new Date(); var mSel = document.getElementById('sMon'); var ySel = document.getElementById('sYear'); mSel.innerHTML = ''; ySel.innerHTML = ''; MONTHS.forEach(function(m, i) { var o = document.createElement('option'); o.value = i; o.textContent = m; if (i === now.getMonth()) o.selected = true; mSel.appendChild(o); }); for (var y = now.getFullYear(); y <= now.getFullYear() + 5; y++) { var o = document.createElement('option'); o.value = y; o.textContent = y; ySel.appendChild(o); } } function getStart() { return { m: parseInt(document.getElementById('sMon').value), y: parseInt(document.getElementById('sYear').value) }; } function monthLabel(offset) { var s = getStart(); var d = new Date(s.y, s.m + offset, 1); return MONTHS[d.getMonth()] + ' ' + d.getFullYear(); } function monthOpts(selM, selY) { var now = new Date(); var mo = '', yo = ''; MONTHS.forEach(function(m, i) { mo += ''; }); for (var y = now.getFullYear(); y <= now.getFullYear()+5; y++) { yo += ''; } return { mo:mo, yo:yo }; } function launchOffset(id) { var s = getStart(); var lm = parseInt(document.getElementById('l'+id+'-m').value); var ly = parseInt(document.getElementById('l'+id+'-y').value); return (ly - s.y) * 12 + (lm - s.m) + 1; } function launchDateLabel(id) { var lm = parseInt(document.getElementById('l'+id+'-m').value); var ly = parseInt(document.getElementById('l'+id+'-y').value); return MONTHS[lm] + ' ' + ly; } function addTier(d) { d = d || { name:'', price:'', freq:'monthly', newM:'', startM:0, churn:false, churnR:5 }; tierCount++; var id = tierCount; var labels = ['First Tier','Second Tier','Third Tier','Fourth Tier']; var div = document.createElement('div'); div.className = 'tier-block'; div.id = 'tier-'+id; div.dataset.freq = d.freq; div.innerHTML = '
'+(labels[id-1]||'Tier '+id)+'
'+ '
'+ '
'+ '
'+CUR.symbol+'
'+ '
'+ ''+ ''+ ''+ '
'+ '
new members/mo
'+ '
'+ '
'+ '
'+ '
Include churn
'+ '
'+ 'Rate:'+ '
%
'+ '
'+ '
'; document.getElementById('tiersList').appendChild(div); if (tierCount >= 4) document.getElementById('addTierBtn').style.display = 'none'; calc(); } function rmTier(id) { document.getElementById('tier-'+id).remove(); document.getElementById('addTierBtn').style.display='flex'; calc(); } function setFreq(id, freq, btn) { document.getElementById('tier-'+id).dataset.freq = freq; document.getElementById('tier-'+id).querySelectorAll('.freq-btn').forEach(function(b){b.classList.remove('on');}); btn.classList.add('on'); calc(); } function togChurn(id) { document.getElementById('t'+id+'-tog').classList.toggle('on'); document.getElementById('t'+id+'-churnwrap').classList.toggle('show'); calc(); } function getTiers() { var tiers = []; document.querySelectorAll('.tier-block').forEach(function(block) { var id = block.id.replace('tier-',''); var name = (document.getElementById('t'+id+'-name').value||'Tier').trim()||'Tier'; var rawP = parseFloat(document.getElementById('t'+id+'-price').value)||0; var freq = block.dataset.freq||'monthly'; var mp = freq==='quarterly' ? rawP/3 : freq==='annual' ? rawP/12 : rawP; var newM = parseFloat(document.getElementById('t'+id+'-new').value)||0; var startM = parseFloat(document.getElementById('t'+id+'-start').value)||0; var churnOn = document.getElementById('t'+id+'-tog').classList.contains('on'); var churnR = churnOn ? (parseFloat(document.getElementById('t'+id+'-churnR').value)||0)/100 : 0; if (mp > 0 || newM > 0) tiers.push({name:name, mp:mp, newM:newM, startM:startM, churnR:churnR, rawP:rawP, freq:freq}); }); return tiers; } function addLaunch(d) { var now = new Date(); d = d || { name:'', price:'', parts:'', amt:'', lm:now.getMonth(), ly:now.getFullYear() }; launchCount++; var id = launchCount; var opts = monthOpts(d.lm, d.ly); // Determine mode: if amt is set but price/parts are not, it's direct mode var isDirect = (d.amt && !d.price && !d.parts) ? true : false; var isCalc = (d.price || d.parts) ? true : false; // If both price and amt exist (priceΓ—parts was calculated), show calculated state var calcedAmt = (d.price && d.parts) ? Math.round(parseFloat(d.price)*parseFloat(d.parts)) : ''; var amtDisplay = isCalc && calcedAmt ? calcedAmt : (isDirect ? d.amt : ''); var amtReadonly = isCalc && calcedAmt ? 'readonly' : ''; var amtBg = isCalc && calcedAmt ? 'style="background:var(--warm);color:var(--mid);"' : ''; var totMsg = isCalc && calcedAmt ? '= '+CUR.symbol+parseInt(calcedAmt).toLocaleString()+' total' : ''; var div = document.createElement('div'); div.className = 'launch-block'; div.id = 'launch-'+id; if(isDirect) div.dataset.totmode = 'direct'; div.innerHTML = '
Workshop / One-Time Offer
'+ '
'+ '
'+ '
'+CUR.symbol+'
'+ '
'+ '
'+CUR.symbol+'
'+totMsg+'
'+ '
'+ '
'+ ''+ '
'+ '
'; document.getElementById('launchList').appendChild(div); calc(); } function rmLaunch(id) { document.getElementById('launch-'+id).remove(); calc(); } function ltotMode(id,m){ document.getElementById('launch-'+id).dataset.totmode=m; } function ltot(id) { var block=document.getElementById('launch-'+id); if(block.dataset.totmode==='direct') return; var pEl=document.getElementById('l'+id+'-price'),nEl=document.getElementById('l'+id+'-parts'),aEl=document.getElementById('l'+id+'-amt'),disp=document.getElementById('l'+id+'-tot'); var p=parseFloat(pEl.value)||0,n=parseFloat(nEl.value)||0; if(p>0&&n>0){var tot=Math.round(p*n);aEl.value=tot;aEl.style.background='var(--warm)';aEl.style.color='var(--mid)';aEl.readOnly=true;disp.textContent='= '+CUR.symbol+tot.toLocaleString()+' total';} else{aEl.value='';aEl.readOnly=false;aEl.style.background='';aEl.style.color='';disp.textContent='';} calc(); } function ltotFocus(id) { // User clicked into Total Revenue β€” switch to direct mode but DON'T clear price/parts var block=document.getElementById('launch-'+id); var pVal=document.getElementById('l'+id+'-price').value; var nVal=document.getElementById('l'+id+'-parts').value; // Only go direct if there's no priceΓ—parts to auto-calculate if(!(parseFloat(pVal)>0 && parseFloat(nVal)>0)){ ltotMode(id,'direct'); var aEl=document.getElementById('l'+id+'-amt'); aEl.readOnly=false;aEl.style.background='';aEl.style.color=''; document.getElementById('l'+id+'-tot').textContent=''; } } function ltotDirect(id) { var aEl=document.getElementById('l'+id+'-amt'); if(aEl.value){ ltotMode(id,'direct'); aEl.readOnly=false;aEl.style.background='';aEl.style.color=''; document.getElementById('l'+id+'-tot').textContent=''; } else { ltotMode(id,'calc'); document.getElementById('l'+id+'-tot').textContent=''; ltot(id); return; } calc(); } function ltotPriceFocus(id) { // User clicked into price or parts β€” switch back to calc mode, unlock total ltotMode(id,'calc'); var aEl=document.getElementById('l'+id+'-amt'); aEl.readOnly=false;aEl.style.background='';aEl.style.color=''; // Don't clear anything β€” just recalculate when they finish typing } function getLaunches() { var launches = []; document.querySelectorAll('.launch-block').forEach(function(row) { var id = row.id.replace('launch-',''); var name = (document.getElementById('l'+id+'-name').value||'Workshop').trim()||'Workshop'; var p = parseFloat(document.getElementById('l'+id+'-price').value)||0; var n = parseFloat(document.getElementById('l'+id+'-parts').value)||0; var direct = parseFloat(document.getElementById('l'+id+'-amt').value)||0; var amt = (p>0&&n>0) ? p*n : direct; var mo = launchOffset(id); var lbl = launchDateLabel(id); if (amt>0 && mo>0) launches.push({name:name, amt:amt, mo:mo, lbl:lbl, parts:n||0, price:p||0, btype:'workshop'}); }); return launches; } // ── Sponsorships ────────────────────────────────────────── function addSponsor(d) { var now = new Date(); d = d || { name:'', amt:'', lm:now.getMonth(), ly:now.getFullYear() }; sponsorCount++; var id = sponsorCount; var opts = monthOpts(d.lm !== undefined ? parseInt(d.lm) : now.getMonth(), d.ly ? parseInt(d.ly) : now.getFullYear()); var div = document.createElement('div'); div.className = 'sponsor-block'; div.id = 'sponsor-'+id; div.innerHTML = '
Sponsorship / Underwriter
'+ ''; document.getElementById('sponsorsList').appendChild(div); calc(); } function rmSponsor(id) { var el=document.getElementById('sponsor-'+id); if(el) el.remove(); calc(); } function getSponsors() { var sponsors = []; document.querySelectorAll('.sponsor-block').forEach(function(row) { var id = row.id.replace('sponsor-',''); var name = (document.getElementById('sp'+id+'-name').value||'').trim()||'Sponsor'; var amt = parseFloat(document.getElementById('sp'+id+'-amt').value)||0; var lm = parseInt(document.getElementById('sp'+id+'-m').value); var ly = parseInt(document.getElementById('sp'+id+'-y').value); var s = getStart(); var mo = (ly - s.y)*12 + (lm - s.m) + 1; var lbl = MONTHS[lm]+' '+ly; if (amt>0) sponsors.push({name:name, amt:amt, mo:mo, lbl:lbl, btype:'sponsor'}); }); return sponsors; } // ── 1:1 Coaching ───────────────────────────────────────── function addCoaching(d) { var now = new Date(); d = d || { name:'', ctype:'session', freq:'once', price:'', qty:'', lm:now.getMonth(), ly:now.getFullYear() }; coachCount++; var id = coachCount; var ctype = d.ctype || 'session'; var freq = ctype==='retainer' ? 'monthly' : (d.freq||'once'); var opts = monthOpts(d.lm !== undefined ? parseInt(d.lm) : now.getMonth(), d.ly ? parseInt(d.ly) : now.getFullYear()); var div = document.createElement('div'); div.className = 'coach-block'; div.id = 'coach-'+id; div.dataset.ctype = ctype; div.dataset.freq = freq; d.freq = freq; div.innerHTML = buildCoachInner(id, ctype, d, opts); document.getElementById('coachingList').appendChild(div); calc(); } function buildCoachInner(id, ctype, d, opts) { d = d||{}; var pLbl = ctype==='retainer' ? 'Monthly Rate' : ctype==='package' ? 'Package Price' : 'Price / Session'; var pPh = ctype==='retainer' ? '500' : ctype==='package' ? '450' : '150'; var qLbl = ctype==='retainer' ? 'Clients' : ctype==='package' ? 'Packages / Period' : 'Sessions / Period'; var qPh = ctype==='retainer' ? '2' : ctype==='package' ? '3' : '2'; var nPh = ctype==='retainer' ? 'e.g. Monthly Strategy Partner' : ctype==='package' ? 'e.g. 3-Session Kickstart' : 'e.g. Discovery Session'; var dateLbl = ctype==='retainer' ? 'Retainer Starts' : 'Starting'; var freq = d.freq || 'once'; var freqSection = ''; if (ctype !== 'retainer') { freqSection = '
'+ ''+ '
'+ ''+ ''+ ''+ '
'+ (freq!=='once'?'
↻ Repeats from start date β€” treated as recurring revenue
':'') + '
'; } var retainerNote = ctype==='retainer' ? '
↻ Added to monthly recurring baseline from start date
' : ''; var pill = ctype==='retainer' ? 'Retainer' : freq!=='once' ? ''+freq.charAt(0).toUpperCase()+freq.slice(1)+'' : ''; return '
1:1 Coaching'+pill+'
'+ '
'+ ''+ ''+ ''+ '
'+ '
'+ '
'+ '
'+CUR.symbol+'
'+ '
'+retainerNote+'
'+ '
'+ freqSection+ '
'+ ''+ '
'+ '
'; } function setCoachFreq(id, freq, btn) { var block = document.getElementById('coach-'+id); block.dataset.freq = freq; // Rebuild inner preserving all current values var now = new Date(); var d = { name: (document.getElementById('co'+id+'-name')||{}).value||'', price: (document.getElementById('co'+id+'-price')||{}).value||'', qty: (document.getElementById('co'+id+'-qty')||{}).value||'', lm: parseInt((document.getElementById('co'+id+'-m')||{}).value||now.getMonth()), ly: parseInt((document.getElementById('co'+id+'-y')||{}).value||now.getFullYear()), ctype: block.dataset.ctype, freq: freq }; var opts = monthOpts(d.lm, d.ly); block.innerHTML = buildCoachInner(id, d.ctype, d, opts); calc(); } function setCoachType(id, ctype, btn) { var block = document.getElementById('coach-'+id); var now = new Date(); var d = { name: (document.getElementById('co'+id+'-name')||{}).value||'', price: (document.getElementById('co'+id+'-price')||{}).value||'', qty: (document.getElementById('co'+id+'-qty')||{}).value||'', lm: parseInt((document.getElementById('co'+id+'-m')||{}).value||now.getMonth()), ly: parseInt((document.getElementById('co'+id+'-y')||{}).value||now.getFullYear()), ctype: ctype, freq: ctype==='retainer' ? 'monthly' : (block.dataset.freq||'once') }; var opts = monthOpts(d.lm, d.ly); block.dataset.ctype = ctype; block.dataset.freq = d.freq; block.innerHTML = buildCoachInner(id, ctype, d, opts); calc(); } function rmCoaching(id) { var el=document.getElementById('coach-'+id); if(el) el.remove(); calc(); } function getCoaching() { var coaching = []; document.querySelectorAll('.coach-block').forEach(function(block) { var id = block.id.replace('coach-',''); var ctype = block.dataset.ctype||'session'; var freq = block.dataset.freq || (ctype==='retainer'?'monthly':'once'); var name = (document.getElementById('co'+id+'-name').value||'').trim()||'Coaching'; var price = parseFloat(document.getElementById('co'+id+'-price').value)||0; var qty = parseFloat(document.getElementById('co'+id+'-qty').value)||1; var lm = parseInt(document.getElementById('co'+id+'-m').value); var ly = parseInt(document.getElementById('co'+id+'-y').value); var s = getStart(); var mo = (ly - s.y)*12 + (lm - s.m) + 1; var lbl = MONTHS[lm]+' '+ly; // For retainer or recurring sessions/packages: monthly MRR contribution // For quarterly: price*qty per quarter = price*qty/3 per month var amtPerOccurrence = price * qty; var mrrContrib = 0; if (ctype==='retainer' || freq==='monthly') { mrrContrib = amtPerOccurrence; } else if (freq==='quarterly') { mrrContrib = amtPerOccurrence / 3; } if (price>0) coaching.push({ name:name, price:price, qty:qty, amt:amtPerOccurrence, mo:mo, lbl:lbl, ctype:ctype, freq:freq, mrrContrib:mrrContrib, btype:'coach' }); }); return coaching; } function escAttr(v) { if(v===undefined||v===null) return ''; return String(v).replace(/"/g,'"').replace(/'/g,'''); } function updateStickyBar(goal, rows, rGoal, cGoal) { var set = function(id, val, highlight) { var el = document.getElementById(id); if (!el) return; el.textContent = val; el.className = 'sticky-val' + (highlight ? ' has-goal' : (val === 'β€”' || val === 'not set') ? ' empty' : ''); }; var goalVal = parseFloat(document.getElementById('goal').value)||0; set('st-goal', goalVal > 0 ? fmt(goalVal)+'/mo' : 'not set', false); if (!rows || !rows.length) { set('st-mrr','β€”',false); set('st-when','β€”',false); set('st-cwhen','β€”',false); set('st-boost','β€”',false); return; } // Starting MRR β€” month 1 baseline set('st-mrr', fmt(rows[0].mrr)+'/mo', false); // Recurring alone goal date set('st-when', rGoal ? rows[rGoal-1].lbl : 'not yet', rGoal != null); // With everything goal date var combinedGoal = cGoal || rGoal; set('st-cwhen', combinedGoal ? rows[combinedGoal-1].lbl : 'not yet', combinedGoal != null); // Months sooner β€” only meaningful if boosts actually move the needle if (rGoal && cGoal && cGoal < rGoal) { var diff = rGoal - cGoal; set('st-boost', diff + ' month' + (diff !== 1 ? 's' : ''), true); } else if (rGoal && (!cGoal || cGoal >= rGoal)) { set('st-boost', 'β€”', false); } else { set('st-boost', 'β€”', false); } } function calc() { var goal = parseFloat(document.getElementById('goal').value)||0; var tiers = getTiers(); var launches = getLaunches(); var sponsors = getSponsors(); var coaching = getCoaching(); var resEl = document.getElementById('resContent'); var tCard = document.getElementById('tableCard'); var tbody = document.getElementById('tBody'); if (!tiers.length || goal<=0) { resEl.innerHTML = '
Fill in your income goal and at least one membership tier to see your projection.
'; tCard.style.display='none'; updateStickyBar(goal, null, null, null); updateURL(); return; } // Coaching with mrrContrib > 0 (retainers + recurring sessions/packages) add to MRR var recurringCoach = coaching.filter(function(c){ return c.mrrContrib > 0; }); // One-time boosts: only "once" sessions/packages var oneTimeCoach = coaching.filter(function(c){ return c.mrrContrib === 0; }); // All one-time boosts pool var allBoosts = launches.concat(sponsors).concat(oneTimeCoach); var MAX=360, mc=tiers.map(function(t){return t.startM;}), rows=[], rGoal=null, cGoal=null; for (var m=1; m<=MAX; m++) { var totM=0, totMRR=0; for (var i=0; i=r.mo) totMRR+=r.mrrContrib; }); var mBoosts=allBoosts.filter(function(b){return b.mo===m;}); var boostTot=mBoosts.reduce(function(s,b){return s+b.amt;},0); var combined=totMRR+boostTot; rows.push({m:m, lbl:monthLabel(m-1), members:Math.round(totM), mrr:totMRR, boostTot:boostTot, combined:combined, mW:mBoosts.filter(function(b){return b.btype==='workshop';}), mS:mBoosts.filter(function(b){return b.btype==='sponsor';}), mC:mBoosts.filter(function(b){return b.btype==='coach';}) }); if (rGoal===null && totMRR>=goal) rGoal=m; if (cGoal===null && combined>=goal) cGoal=m; } var milestones=[0.25,0.5,0.75].map(function(pct){var row=rows.find(function(r){return r.mrr>=goal*pct;});return{pct:Math.round(pct*100),lbl:row?row.lbl:null,mrr:row?row.mrr:0};}); var html=''; if (rGoal) { html+='
Recurring Revenue Alone
'+rows[rGoal-1].lbl+'
'+tstr(rGoal)+' Β· '+fmt(goal)+'/mo
'; if (cGoal&&cGoal
'+tstr(rGoal-cGoal)+' sooner Β· '+tstr(cGoal)+' total
'; } else { html+='
With All One-Time Boosts
No change
Boosts land after recurring goal is reached
'; } html+='
'; } else { var fin=rows[rows.length-1].mrr; if (cGoal) { html+='
Recurring Revenue Alone
Plateaus at '+fmt(fin)+'/mo
Won\'t reach '+fmt(goal)+'/mo alone
With All One-Time Boosts
'+rows[cGoal-1].lbl+'
'+tstr(cGoal)+' β€” boosts bridge the gap
'; } else { resEl.innerHTML='
Recurring revenue plateaus at '+fmt(fin)+'/mo. Try adding more members per month or more one-time revenue.
'; tCard.style.display='none'; updateStickyBar(goal, rows, null, null); updateURL(); return; } } html+='
'; milestones.forEach(function(ms){html+='
'+ms.pct+'% of goal
'+(ms.lbl||'β€”')+'
'+fmt(ms.mrr)+'/mo
';}); var avgP=tiers.reduce(function(s,t){return s+t.mp;},0)/tiers.length; html+='
Avg member value
'+fmt(avgP)+'
/mo blended
'; // Workshops summary strip var activeWorkshops=launches.filter(function(l){return l.mo>0&&l.mo<=MAX;}); if (activeWorkshops.length) { var wTot=activeWorkshops.reduce(function(s,l){return s+l.amt;},0); html+='
πŸŽ‰ Workshops & One-Time Offers
'; activeWorkshops.sort(function(a,b){return a.mo-b.mo;}).forEach(function(l){ var detail=l.price>0&&l.parts>0 ? l.parts+' Γ— '+fmt(l.price) : ''; html+='
'+l.name+' ('+l.lbl+(detail?' Β· '+detail:'')+')'+fmt(l.amt)+'
'; }); html+='
Total'+fmt(wTot)+'
'; } // Sponsor summary strip if (sponsors.length) { var sponsorTot=sponsors.reduce(function(s,sp){return s+sp.amt;},0); html+='
πŸ’Ό Sponsorships & Underwriting
'; sponsors.forEach(function(sp){ html+='
'+sp.name+' ('+sp.lbl+')'+fmt(sp.amt)+'
'; }); html+='
Total'+fmt(sponsorTot)+'
'; } // Coaching summary strip if (coaching.length) { html+='
🎯 1:1 Coaching
'; recurringCoach.forEach(function(c){ var freqLbl = c.ctype==='retainer' ? 'retainer' : c.freq; var mrrLbl = c.freq==='quarterly' ? fmt(c.mrrContrib)+'/mo ('+fmt(c.amt)+'/quarter)' : fmt(c.mrrContrib)+'/mo'; html+='
'+c.name+' ('+freqLbl+' Β· starts '+c.lbl+')'+mrrLbl+'
'; }); oneTimeCoach.forEach(function(c){ html+='
'+c.name+' ('+c.lbl+')'+fmt(c.amt)+'
'; }); if (recurringCoach.length) { var totalRecurringMRR=recurringCoach.reduce(function(s,r){return s+r.mrrContrib;},0); html+='
Coaching MRR added+'+fmt(totalRecurringMRR)+'/mo
'; } html+='
'; } // Workshop/launch boosts section (original behavior preserved) var allBoostItems=allBoosts.filter(function(b){return b.mo>0&&b.mo<=MAX;}); if (allBoostItems.length) { html+='
One-Time Revenue Timeline
'; allBoostItems.sort(function(a,b){return a.mo-b.mo;}).forEach(function(b){ var icon=b.btype==='sponsor'?'πŸ’Ό':b.btype==='coach'?'🎯':'πŸŽ‰'; var rowMRR=rows[b.mo-1]?rows[b.mo-1].mrr:0; var saved=null; if (rGoal&&b.mo<=rGoal) { var boostTarget=goal-b.amt; var wbg=rows.find(function(r){return r.m>=b.mo&&r.mrr>=boostTarget;}); if(wbg) saved=rGoal-wbg.m; } var saveStr=saved===null?'':saved===0?'Goal already reached':saved>0?saved+' month'+(saved!==1?'s':'')+' sooner':''; html+='
'+icon+' '+b.name+' ('+b.lbl+')'+fmt(b.amt)+''+(saveStr?'β†’ '+saveStr+'':'')+'
'; }); html+='
'; } resEl.innerHTML=html; var show=Math.min(rows.length, Math.max((rGoal||cGoal||12)+3,12)); tbody.innerHTML=''; for (var i=0; i';}); r.mS.forEach(function(b){badges+='πŸ’Ό '+b.name+'';}); r.mC.forEach(function(b){badges+='🎯 '+b.name+'';}); tr.innerHTML=''+r.lbl+badges+''+ ''+r.members.toLocaleString()+''+ ''+fmt(r.mrr)+''+ ''+(r.boostTot>0?'+'+fmt(r.boostTot)+'':'β€”')+''+ ''+fmt(r.combined)+''; tbody.appendChild(tr); } tCard.style.display='block'; updateStickyBar(goal, rows, rGoal, cGoal); updateURL(); } function buildSummary() { var tiers=getTiers(), launches=getLaunches(), sponsors=getSponsors(), coaching=getCoaching(); var goal=parseFloat(document.getElementById('goal').value)||0; var mSel=document.getElementById('sMon'), ySel=document.getElementById('sYear'); var startLbl=mSel.options[mSel.selectedIndex].text+' '+ySel.value; var scenName=document.getElementById('scenarioName').value.trim(); document.title = scenName ? scenName+' Β· Community Revenue Calculator' : 'Community Revenue Calculator Β· Kelly Pratt'; var html='
Scenario Overview
'; html+='
Monthly Goal'+fmt(goal)+'/mo
'; html+='
Starting'+startLbl+'
'; html+='
Currency'+CUR.code+'
'; html+='
'; if (tiers.length) { html+='
Membership Tiers
'; tiers.forEach(function(t){ var fLbl=t.freq==='quarterly'?'/quarter':t.freq==='annual'?'/year':'/month'; var details=fmt(t.rawP)+fLbl+' Β· '+t.newM+' new/mo'; if(t.startM>0) details+=' Β· '+t.startM+' existing'; if(t.churnR>0) details+=' Β· '+(t.churnR*100).toFixed(1)+'% churn'; html+='
'+t.name+''+details+'
'; }); html+='
'; } if (launches.length) { html+='
Workshops & One-Time Launches
'; launches.forEach(function(l){ var ldetail; if(l.parts && l.price) { ldetail = l.parts+' participants Γ— '+fmt(l.price)+' = '+fmt(l.amt)+' Β· '+l.lbl; } else if(l.parts) { ldetail = l.parts+' participants Β· '+fmt(l.amt)+' Β· '+l.lbl; } else { ldetail = fmt(l.amt)+' Β· '+l.lbl; } html+='
'+l.name+''+ldetail+'
'; }); html+='
'; } if (sponsors.length) { html+='
Sponsorships & Underwriting
'; sponsors.forEach(function(s){ html+='
'+s.name+''+fmt(s.amt)+' Β· '+s.lbl+'
'; }); html+='
'; } if (coaching.length) { html+='
1:1 Coaching
'; coaching.forEach(function(c){ var isRecurring = c.ctype==='retainer' || (c.freq && c.freq!=='once'); var mrrC = c.mrrContrib !== undefined ? c.mrrContrib : (c.ctype==='retainer'||c.freq==='monthly') ? c.price*(c.qty||1) : c.freq==='quarterly' ? (c.price*(c.qty||1))/3 : 0; var freqLbl = c.ctype==='retainer' ? 'retainer' : (c.freq&&c.freq!=='once') ? c.freq : 'one-time'; var det = isRecurring ? fmt(mrrC)+'/mo ('+freqLbl+' Β· starts '+c.lbl+')' : fmt(c.price*(c.qty||1))+' Β· '+c.lbl; html+='
'+c.name+''+det+'
'; }); html+='
'; } var notes=document.getElementById('scenarioNotes').value.trim(); if(notes){html+='
Notes
'+notes.replace(/\n/g,'
')+'
';} document.getElementById('pdfSummary').innerHTML=html; document.getElementById('pdfScenario').textContent=scenName; document.getElementById('pdfMeta').textContent='Generated '+new Date().toLocaleDateString('en-US',{month:'long',day:'numeric',year:'numeric'}); } function showPDF(cb) { buildSummary(); var toShow=[document.getElementById('pdfHdr'),document.getElementById('pdfSummary'),document.querySelector('.pdf-foot')]; var toHide=[document.querySelector('.print-bar'),document.querySelector('.about-card'),document.querySelector('.gift-card'),document.querySelector('header'),document.querySelector('.currency-bar'),document.getElementById('addTierBtn')].concat(Array.from(document.querySelectorAll('.add-btn,.rm-btn,.tog-wrap,.freq-sel,.rst-btn'))); toShow.forEach(function(el){if(el) el.style.display='block';}); toHide.forEach(function(el){if(el) el.style.display='none';}); cb(function(){ toShow.forEach(function(el){if(el) el.style.display='';}); toHide.forEach(function(el){if(el) el.style.display='';}); }); } function doPrint(){showPDF(function(restore){setTimeout(function(){window.print();restore();},120);});} function getState() { var tiers=[],launches=[],sponsors=[],coaching=[]; document.querySelectorAll('.tier-block').forEach(function(b){ var id=b.id.replace('tier-',''); tiers.push({name:document.getElementById('t'+id+'-name').value,price:document.getElementById('t'+id+'-price').value,freq:b.dataset.freq,newM:document.getElementById('t'+id+'-new').value,startM:document.getElementById('t'+id+'-start').value,churn:document.getElementById('t'+id+'-tog').classList.contains('on'),churnR:document.getElementById('t'+id+'-churnR').value}); }); document.querySelectorAll('.launch-block').forEach(function(r){ var id=r.id.replace('launch-',''); var mode=r.dataset.totmode||'calc'; launches.push({name:document.getElementById('l'+id+'-name').value,price:document.getElementById('l'+id+'-price').value,parts:document.getElementById('l'+id+'-parts').value,amt:document.getElementById('l'+id+'-amt').value,lm:document.getElementById('l'+id+'-m').value,ly:document.getElementById('l'+id+'-y').value,mode:mode}); }); document.querySelectorAll('.sponsor-block').forEach(function(r){ var id=r.id.replace('sponsor-',''); sponsors.push({name:document.getElementById('sp'+id+'-name').value,amt:document.getElementById('sp'+id+'-amt').value,lm:document.getElementById('sp'+id+'-m').value,ly:document.getElementById('sp'+id+'-y').value}); }); document.querySelectorAll('.coach-block').forEach(function(b){ var id=b.id.replace('coach-',''); coaching.push({name:document.getElementById('co'+id+'-name').value,ctype:b.dataset.ctype,freq:b.dataset.freq||'once',price:document.getElementById('co'+id+'-price').value,qty:document.getElementById('co'+id+'-qty').value,lm:document.getElementById('co'+id+'-m').value,ly:document.getElementById('co'+id+'-y').value}); }); return{goal:document.getElementById('goal').value,sm:document.getElementById('sMon').value,sy:document.getElementById('sYear').value,sn:document.getElementById('scenarioName').value,notes:document.getElementById('scenarioNotes').value,cur:CUR.code,tiers:tiers,launches:launches,sponsors:sponsors,coaching:coaching}; } function updateURL(){try{var enc=btoa(encodeURIComponent(JSON.stringify(getState())));history.replaceState(null,'',location.pathname+'?s='+enc);}catch(e){}} function loadURL(){ try{ var p=new URLSearchParams(location.search).get('s'); if(!p) return false; var st=JSON.parse(decodeURIComponent(atob(p))); if(st.goal) document.getElementById('goal').value=st.goal; if(st.sm!==undefined) document.getElementById('sMon').value=st.sm; if(st.sy!==undefined) document.getElementById('sYear').value=st.sy; if(st.sn) document.getElementById('scenarioName').value=st.sn; if(st.notes) document.getElementById('scenarioNotes').value=st.notes; if(st.cur) setCurrency(st.cur); (st.tiers||[]).forEach(function(t){addTier(t);}); (st.launches||[]).forEach(function(l){addLaunch(l);}); (st.sponsors||[]).forEach(function(s){addSponsor(s);}); (st.coaching||[]).forEach(function(c){addCoaching(c);}); return true; }catch(e){return false;} } function doShare(){ if(location.protocol === 'file:'){ alert('Share links only work once this calculator is live on your website.\n\nOnce it\'s at kellypratt.com/revenuecalculator, the πŸ”— Share Link button will generate a shareable URL with all your numbers encoded in it.'); return; } updateURL(); var url=location.href; if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(url).then(flashCopied).catch(function(){prompt('Copy this link:',url);});} else{prompt('Copy this link:',url);} } function flashCopied(){var el=document.getElementById('copiedMsg');el.classList.add('show');setTimeout(function(){el.classList.remove('show');},3000);} function doPostInStudio(){ updateURL(); var scenName = document.getElementById('scenarioName').value.trim(); var label = scenName ? '"'+scenName+'"' : 'my revenue scenario'; // Check if we're on a real URL or a local file var isLocal = location.protocol === 'file:'; var shareUrl = isLocal ? '[paste your share link here]' : location.href; var msg = 'Just ran '+label+' through the Community Revenue Calculator β€” would love your eyes on it! πŸ‘€\n\n'+(isLocal ? '⚠️ Share link not available from local file β€” use the πŸ”— Share Link button once this is live on the site, then paste it here.\n' : shareUrl+'\n')+'\nWhat do you see that I might be missing? 🌱'; if(navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(msg).then(function(){ var el=document.getElementById('studioMsg'); el.classList.add('show'); setTimeout(function(){el.classList.remove('show');},4000); setTimeout(function(){window.open('https://members.athenavillage.com/spaces/17638940/feed','_blank');},400); }).catch(function(){ prompt('Copy this to post in Creator Conversations:',msg); window.open('https://members.athenavillage.com/spaces/17638940/feed','_blank'); }); } else { prompt('Copy this to post in Creator Conversations:',msg); window.open('https://members.athenavillage.com/spaces/17638940/feed','_blank'); } } function doSendToKelly(){ window.open('https://members.athenavillage.com/plans/1936469?bundle_token=d1c9ecb81e2e542025c468ccfa961077','_blank'); } function doReset(){ if(!confirm('Reset everything and start fresh?')) return; document.getElementById('tiersList').innerHTML=''; document.getElementById('launchList').innerHTML=''; document.getElementById('sponsorsList').innerHTML=''; document.getElementById('coachingList').innerHTML=''; document.getElementById('resContent').innerHTML='
Fill in your income goal and at least one membership tier to see your projection.
'; document.getElementById('tableCard').style.display='none'; document.getElementById('scenarioName').value=''; document.getElementById('scenarioNotes').value=''; document.getElementById('goal').value=''; document.getElementById('addTierBtn').style.display='flex'; tierCount=0; launchCount=0; sponsorCount=0; coachCount=0; setCurrency('USD'); initDates(); addTier({name:'',price:'',freq:'monthly',newM:'',startM:0,churn:false,churnR:5}); history.replaceState(null,'',location.pathname); } // Init initDates(); var loaded = loadURL(); if (!loaded) { // Mighty Networks recommended defaults β€” empty names so users fill in their own addTier({name:'', price:'29', freq:'monthly', newM:'5', startM:0, churn:true, churnR:5}); addLaunch({name:'', price:'', parts:'', amt:'', lm:new Date().getMonth(), ly:new Date().getFullYear()}); }