Front-load strategy mode: pocket 50% until L6, then 25% (near-identical arrival to flat 25%, ~double the early draw)

- pocketPctFor(): per-phase pocket resolution ('frontload' sentinel); computePlan/boostSim/plan-text/summary-card all honor it
- strategy select + share param mode=frontload + Sponsor Guide entry
- tests 7b: frontload matches 50%-behavior below L6 and 25%-behavior from L6, total <= flat-50
This commit is contained in:
martbost
2026-07-31 14:40:31 -05:00
committed by root
parent 5a6a3f0a04
commit 0db3b1c8e6
2 changed files with 41 additions and 13 deletions
+24 -13
View File
@@ -1256,6 +1256,7 @@
<select id="strategyMode"> <select id="strategyMode">
<option value="accelerate" selected>🚀 Accelerate — reinvest everything, fastest to 3× L7</option> <option value="accelerate" selected>🚀 Accelerate — reinvest everything, fastest to 3× L7</option>
<option value="pocket">💰 Pocket along the way — keep part of each profit</option> <option value="pocket">💰 Pocket along the way — keep part of each profit</option>
<option value="frontload">⚡ Front-load — pocket 50% until L6, then 25% (same arrival as flat 25%)</option>
<option value="park">🏝 Park &amp; collect — stop at a level, pocket it all</option> <option value="park">🏝 Park &amp; collect — stop at a level, pocket it all</option>
</select> </select>
</div> </div>
@@ -1511,6 +1512,7 @@
<p style="margin:6px 0 0;font-size:0.88rem;line-height:1.5;"> <p style="margin:6px 0 0;font-size:0.88rem;line-height:1.5;">
<strong>🚀 Accelerate</strong> (default): every profit reinvests — fastest path to 3× L7. Best for people who want the endgame and don't need cash along the way.<br> <strong>🚀 Accelerate</strong> (default): every profit reinvests — fastest path to 3× L7. Best for people who want the endgame and don't need cash along the way.<br>
<strong>💰 Pocket along the way</strong>: they keep 25/50/75% of each cycle's profit as spending money and bank the rest. The climb honestly slows down — the timeline and tables update to show it — but they see real money the whole way. Best for people who need proof it pays before they trust the climb.<br> <strong>💰 Pocket along the way</strong>: they keep 25/50/75% of each cycle's profit as spending money and bank the rest. The climb honestly slows down — the timeline and tables update to show it — but they see real money the whole way. Best for people who need proof it pays before they trust the climb.<br>
<strong>⚡ Front-load</strong>: pocket 50% of each cycle until L6, then tighten to 25% for the expensive late jumps. Because early upgrades are funded by the payout roll anyway, this arrives at 3× L7 on nearly the same date as flat 25% while paying roughly double during the early grind — usually the best effort-to-reward line for someone actively working the clicks. The share link remembers this choice too.<br>
<strong>🏝 Park &amp; collect</strong>: stop at a level on purpose and pocket the whole profit every cycle (e.g. L4+L3 ≈ $213/mo). Best for tight budgets who may never fund an L7 — and you still earn your 10% on every one of their cycles, forever.<br> <strong>🏝 Park &amp; collect</strong>: stop at a level on purpose and pocket the whole profit every cycle (e.g. L4+L3 ≈ $213/mo). Best for tight budgets who may never fund an L7 — and you still earn your 10% on every one of their cycles, forever.<br>
The share link remembers the strategy, so the plan they open is the plan you chose for them. The share link remembers the strategy, so the plan they open is the plan you chose for them.
</p> </p>
@@ -1608,9 +1610,16 @@ const PHASE_SLOTS = {
'L5+L4': [5, 4], 'L6': [6], 'L7': [7], '3xL7': [7, 7, 7], 'L5+L4': [5, 4], 'L6': [6], 'L7': [7], '3xL7': [7, 7, 7],
}; };
// pocketPct: number (flat) or the string 'frontload' = 50% below L6, 25%
// from L6 up (Marty 2026-07-31: take the money early when thresholds are
// cheap, tighten for the expensive late jumps — same arrival as flat 25%).
function pocketPctFor(pocketPct, phaseKey) {
if (pocketPct !== 'frontload') return pocketPct;
return Math.max(...PHASE_SLOTS[phaseKey]) >= 6 ? 25 : 50;
}
function computePlan(phases, pocketPct = 0) { function computePlan(phases, pocketPct = 0) {
const plan = {}; const plan = {};
const keepFrac = 1 - pocketPct / 100;
let bal = 0; let bal = 0;
const activated = new Set(); const activated = new Set();
for (let lv = 1; lv <= Math.max(...PHASE_SLOTS[phases[0]]); lv++) activated.add(lv); for (let lv = 1; lv <= Math.max(...PHASE_SLOTS[phases[0]]); lv++) activated.add(lv);
@@ -1640,7 +1649,7 @@ function computePlan(phases, pocketPct = 0) {
while (cycles < 500) { while (cycles < 500) {
cycles++; cycles++;
if (bal + p.payout - carriedCamp >= cost) { bal += p.payout - carriedCamp - cost; break; } if (bal + p.payout - carriedCamp >= cost) { bal += p.payout - carriedCamp - cost; break; }
bal += p.profit * keepFrac; bal += p.profit * (1 - pocketPctFor(pocketPct, k) / 100);
} }
plan[k] = { cycles, months: Math.round(cycles * p.daysPer / 30.4 * 10) / 10 }; plan[k] = { cycles, months: Math.round(cycles * p.daysPer / 30.4 * 10) / 10 };
} }
@@ -1679,7 +1688,6 @@ function laneStats(lanes) {
// Simulate from an arbitrary lane set + starting reserve to 3×L7. // Simulate from an arbitrary lane set + starting reserve to 3×L7.
// Mirrors computePlan's rules exactly; capped for safety. // Mirrors computePlan's rules exactly; capped for safety.
function boostSim(startLanes, bal0, pocketPct = 0) { function boostSim(startLanes, bal0, pocketPct = 0) {
const keepFrac = 1 - pocketPct / 100;
let lanes = [...startLanes].sort((a, b) => b - a); let lanes = [...startLanes].sort((a, b) => b - a);
let bal = bal0; let bal = bal0;
let cycles = 0; let cycles = 0;
@@ -1709,7 +1717,7 @@ function boostSim(startLanes, bal0, pocketPct = 0) {
while (spin++ < 500) { while (spin++ < 500) {
cycles++; cycles++;
if (bal + s.payout - carriedCamp >= cost) { bal += s.payout - carriedCamp - cost; break; } if (bal + s.payout - carriedCamp >= cost) { bal += s.payout - carriedCamp - cost; break; }
bal += s.profit * keepFrac; bal += s.profit * (1 - (pocketPct === 'frontload' ? (Math.max(...lanes) >= 6 ? 25 : 50) : pocketPct) / 100);
} }
lanes = targetSlots.sort((a, b) => b - a); lanes = targetSlots.sort((a, b) => b - a);
path.push(targetKey); path.push(targetKey);
@@ -1792,6 +1800,7 @@ function renderBoost(phaseKey, mode, pocketPct) {
'Activation is charged once per level; unspent cash goes into reserves either way. ' + 'Activation is charged once per level; unspent cash goes into reserves either way. ' +
(mode === 'park' ? 'Shown on the climb-to-endgame basis; if you park, the best combo is simply the highest profit/cycle you can afford.' : (mode === 'park' ? 'Shown on the climb-to-endgame basis; if you park, the best combo is simply the highest profit/cycle you can afford.' :
mode === 'pocket' ? `Simulated with your ${pocketPct}% pocket setting.` : mode === 'pocket' ? `Simulated with your ${pocketPct}% pocket setting.` :
mode === 'frontload' ? 'Simulated with the front-load setting: pocket 50% until L6, then 25%.' :
'Simulated in Accelerate mode, same reserve math as the rest of the calculator.'); 'Simulated in Accelerate mode, same reserve math as the rest of the calculator.');
} }
@@ -1901,7 +1910,8 @@ function updateAll() {
const selfLevel = parseInt(document.getElementById('selfFundLevel').value); const selfLevel = parseInt(document.getElementById('selfFundLevel').value);
const showRef = document.getElementById('showReferral').checked; const showRef = document.getElementById('showReferral').checked;
const mode = document.getElementById('strategyMode').value; const mode = document.getElementById('strategyMode').value;
const pocketPct = mode === 'pocket' ? (parseInt(document.getElementById('pocketPct').value) || 50) : 0; const pocketPct = mode === 'pocket' ? (parseInt(document.getElementById('pocketPct').value) || 50)
: mode === 'frontload' ? 'frontload' : 0;
document.getElementById('pocketPctGroup').style.display = mode === 'pocket' ? '' : 'none'; document.getElementById('pocketPctGroup').style.display = mode === 'pocket' ? '' : 'none';
document.getElementById('parkPhaseGroup').style.display = mode === 'park' ? '' : 'none'; document.getElementById('parkPhaseGroup').style.display = mode === 'park' ? '' : 'none';
@@ -1935,7 +1945,7 @@ function updateAll() {
const endCutPerCycle = PI[endKey].parked ? PI[endKey].payout * (commPct / 100) : THREE_L7_YOUR_CUT * mult; const endCutPerCycle = PI[endKey].parked ? PI[endKey].payout * (commPct / 100) : THREE_L7_YOUR_CUT * mult;
const monthlyPassive = Math.round(endCutPerCycle * 30.4 / 19 * numPeople); const monthlyPassive = Math.round(endCutPerCycle * 30.4 / 19 * numPeople);
const pocketedTotal = Math.round(phases.filter(k => k !== '3xL7' && !PI[k].parked) const pocketedTotal = Math.round(phases.filter(k => k !== '3xL7' && !PI[k].parked)
.reduce((s, k) => s + PI[k].profit * (pocketPct / 100) * PI[k].cycles, 0)); .reduce((s, k) => s + PI[k].profit * (pocketPctFor(pocketPct, k) / 100) * PI[k].cycles, 0));
let totalToEGPerPerson = 0; let totalToEGPerPerson = 0;
for (const k of phases) { for (const k of phases) {
@@ -1980,11 +1990,11 @@ function updateAll() {
<div class="label">${mode === 'park' ? 'Time to Park Level' : 'Time to 3× L7'}</div> <div class="label">${mode === 'park' ? 'Time to Park Level' : 'Time to 3× L7'}</div>
<div class="value orange" style="font-size:1.3rem">~${totalMonths} months</div> <div class="value orange" style="font-size:1.3rem">~${totalMonths} months</div>
</div> </div>
${mode === 'pocket' ? ` ${(mode === 'pocket' || mode === 'frontload') ? `
<div class="summary-card"> <div class="summary-card">
<div class="label">Pocketed Along the Way</div> <div class="label">Pocketed Along the Way</div>
<div class="value yellow">$${pocketedTotal.toLocaleString()}</div> <div class="value yellow">$${pocketedTotal.toLocaleString()}</div>
<div class="sub">${pocketPct}% of each cycle's profit</div> <div class="sub">${pocketPct === 'frontload' ? "50% early, 25% from L6" : pocketPct + "% of each cycle&#39;s profit"}</div>
</div>` : ''} </div>` : ''}
${mode === 'park' ? ` ${mode === 'park' ? `
<div class="summary-card"> <div class="summary-card">
@@ -2307,7 +2317,7 @@ document.querySelectorAll('.tab-btn').forEach(btn => {
if (el && [...el.options].some(o => o.value === self)) el.value = self; if (el && [...el.options].some(o => o.value === self)) el.value = self;
} }
const mode = p.get('mode'); const mode = p.get('mode');
if (mode === 'pocket' || mode === 'park') { if (mode === 'pocket' || mode === 'frontload' || mode === 'park') {
document.getElementById('strategyMode').value = mode; document.getElementById('strategyMode').value = mode;
const pk = p.get('pk'); const pk = p.get('pk');
if (pk && [...document.getElementById('pocketPct').options].some(o => o.value === pk)) if (pk && [...document.getElementById('pocketPct').options].some(o => o.value === pk))
@@ -2453,8 +2463,8 @@ function phasePlanLine(k, i, PI, mode, pocketPct) {
return `**Phase ${i + 1}: ${p.label} — PARK HERE** 🏝\nRun these campaigns on repeat, every ~19 days, and pocket the ~$${p.profit.toFixed(2)} profit each cycle — about $${perMo.toLocaleString()}/month of spending money for a few minutes of clicking a day. No more reinvesting, no more climbing. This is your income level.`; return `**Phase ${i + 1}: ${p.label} — PARK HERE** 🏝\nRun these campaigns on repeat, every ~19 days, and pocket the ~$${p.profit.toFixed(2)} profit each cycle — about $${perMo.toLocaleString()}/month of spending money for a few minutes of clicking a day. No more reinvesting, no more climbing. This is your income level.`;
} }
const base = `**Phase ${i + 1}: ${p.label}**\n${p.cycles} cycles, ~${p.months} months — $${p.payout.toFixed(2)}/cycle payout`; const base = `**Phase ${i + 1}: ${p.label}**\n${p.cycles} cycles, ~${p.months} months — $${p.payout.toFixed(2)}/cycle payout`;
if (mode === 'pocket') { if (mode === 'pocket' || mode === 'frontload') {
const pk = p.profit * pocketPct / 100; const pk = p.profit * pocketPctFor(pocketPct, k) / 100;
const totalPk = Math.round(pk * p.cycles * 100) / 100; const totalPk = Math.round(pk * p.cycles * 100) / 100;
return `${base}\nPocket ~$${pk.toFixed(2)} of each cycle's ~$${p.profit.toFixed(2)} profit (about $${totalPk.toFixed(2)} in your pocket across this phase) and bank the rest toward the next level. The timeline above already accounts for the slower climb.`; return `${base}\nPocket ~$${pk.toFixed(2)} of each cycle's ~$${p.profit.toFixed(2)} profit (about $${totalPk.toFixed(2)} in your pocket across this phase) and bank the rest toward the next level. The timeline above already accounts for the slower climb.`;
} }
@@ -2471,7 +2481,8 @@ function generatePlan() {
const selfCost = getLevelCost(selfLevel); const selfCost = getLevelCost(selfLevel);
const effective = getEffectiveStart(giftLevel, selfLevel); const effective = getEffectiveStart(giftLevel, selfLevel);
const mode = document.getElementById('strategyMode').value; const mode = document.getElementById('strategyMode').value;
const pocketPct = mode === 'pocket' ? (parseInt(document.getElementById('pocketPct').value) || 50) : 0; const pocketPct = mode === 'pocket' ? (parseInt(document.getElementById('pocketPct').value) || 50)
: mode === 'frontload' ? 'frontload' : 0;
let phases = getPhaseSequence(effective); let phases = getPhaseSequence(effective);
if (mode === 'park') phases = phases.slice(0, phases.indexOf(document.getElementById('parkPhase').value) + 1); if (mode === 'park') phases = phases.slice(0, phases.indexOf(document.getElementById('parkPhase').value) + 1);
const PI = phaseInfo(phases, pocketPct); const PI = phaseInfo(phases, pocketPct);
@@ -2479,7 +2490,7 @@ function generatePlan() {
const totalMonths = estimateMonths(phases, pocketPct); const totalMonths = estimateMonths(phases, pocketPct);
const endgameBlock = PI[endKey].parked const endgameBlock = PI[endKey].parked
? `**The destination: ${PI[endKey].label}** 🏝\nYou stop climbing here on purpose — roughly $${Math.round(HYBRIDS[endKey].profit * 30.4 / 19).toLocaleString()}/month pocketed, indefinitely, for a few minutes of clicking a day. (Want more later? The calculator link above shows the full climb to 3× L7 whenever you're ready.)` ? `**The destination: ${PI[endKey].label}** 🏝\nYou stop climbing here on purpose — roughly $${Math.round(HYBRIDS[endKey].profit * 30.4 / 19).toLocaleString()}/month pocketed, indefinitely, for a few minutes of clicking a day. (Want more later? The calculator link above shows the full climb to 3× L7 whenever you're ready.)`
: `**Phase ${phases.filter(x => x !== '3xL7').length + 1}: 3× L7 Endgame** 🎯\nAll three L7 campaigns running at once\n$9,720.00/cycle payout → ~$4,032/month${mode === 'pocket' ? ' — and from here it is ALL pocket money' : ' passive'}\nThis is the finish line${mode === 'pocket' ? `\n\n(CBP has a $10 minimum withdrawal and a 10% fee on external-wallet withdrawals — pocketed money accumulates in your balance until you cash out.)` : ''}`; : `**Phase ${phases.filter(x => x !== '3xL7').length + 1}: 3× L7 Endgame** 🎯\nAll three L7 campaigns running at once\n$9,720.00/cycle payout → ~$4,032/month${(mode === 'pocket' || mode === 'frontload') ? ' — and from here it is ALL pocket money' : ' passive'}\nThis is the finish line${(mode === 'pocket' || mode === 'frontload') ? `\n\n(CBP has a $10 minimum withdrawal and a 10% fee on external-wallet withdrawals — pocketed money accumulates in your balance until you cash out.)` : ''}`;
const roadmapHeading = PI[endKey].parked const roadmapHeading = PI[endKey].parked
? `Your roadmap to ~$${Math.round(HYBRIDS[endKey].profit * 30.4 / 19).toLocaleString()}/month pocket income:` ? `Your roadmap to ~$${Math.round(HYBRIDS[endKey].profit * 30.4 / 19).toLocaleString()}/month pocket income:`
: `Your full roadmap to $4,032/month:`; : `Your full roadmap to $4,032/month:`;
+17
View File
@@ -135,6 +135,23 @@ console.log("7) strategy modes:");
check("parked timeline runs 10 collection cycles", tl.length === 10); check("parked timeline runs 10 collection cycles", tl.length === 10);
} }
console.log("7b) Front-load strategy (pocket 50% until L6, then 25%):");
{
const phases = getPhaseSequence(0);
const fl = computePlan(phases, "frontload");
const p25 = computePlan(phases, 25);
const p50 = computePlan(phases, 50);
for (const k of phases) {
if (k === "3xL7") continue;
const isLate = Math.max(...PHASE_SLOTS[k]) >= 6;
const want = isLate ? p25[k].cycles : p50[k].cycles;
check(`frontload ${k} = ${isLate ? "25%" : "50%"} behavior (${fl[k].cycles}cy)`,
fl[k].cycles === want, `want ${want}`);
}
const total = (plan) => phases.reduce((s, k) => s + plan[k].cycles, 0);
check(`frontload total ${total(fl)} <= flat-50 total ${total(p50)}`, total(fl) <= total(p50));
}
console.log("8) Boost Planner (extra-funds combos, Marty 2026-07-31):"); console.log("8) Boost Planner (extra-funds combos, Marty 2026-07-31):");
{ {
// 8a. boostSim with $0 extra must agree with computePlan for every start. // 8a. boostSim with $0 extra must agree with computePlan for every start.