function calcBracketTax(taxable, brackets) { let tax = 0, remaining = taxable; for (let b of brackets) { if (remaining <= 0) break; let inBracket = Math.min(remaining, Math.max(0, b.max - b.min)); tax += inBracket * b.r; remaining -= inBracket; } return tax; } function calcStateTax(taxable, stateCode) { const s = STATE_DATA[stateCode] || {t:'none'}; if (s.t === 'none') return 0; if (s.t === 'flat') return Math.max(0, taxable * s.v); if (s.t === 'brackets') return calcBracketTax(taxable, s.v); return 0; } function formatMoney(n) { //return '$' + Math.round(n).toLocaleString('en-US'); return '$' + n.toFixed(2).toLocaleString('en-US'); } let currentData = {}; function renderResults(d) { const panel = document.getElementById('rightPanel'); panel.innerHTML = `

💵 Paycheck Breakdown

Net Take-Home
${formatMoney(d.net)}
/year | ${formatMoney(d.netPer)} /${d.freq === 52 ? 'week' : d.freq === 26 ? '2 weeks' : d.freq === 24 ? '2 weeks' : d.freq === 12 ? 'month' : 'year'}
Federal Tax
${formatMoney(d.fed)}
${(d.fed/d.gross*100||0).toFixed(1)}% effective rate
FICA (SS + Medicare)
${formatMoney(d.fica)}
SS: ${formatMoney(d.ss)} | Medicare: ${formatMoney(d.med + d.addMed)}
State Tax
${formatMoney(d.stTax)}
${(d.stTax/d.gross*100||0).toFixed(1)}% effective rate
City Tax
${formatMoney(d.ctTax)}
${(d.ctTax/d.gross*100||0).toFixed(1)}% effective rate
Retirement (401k)
${formatMoney(d.retirement)}
${(d.retPct*100).toFixed(1)}% of gross
Other Deductions
${formatMoney(d.preTax + d.postTax + d.other)}
Pre/Post/Other combined

📊 Allocation Breakdown

CategoryAnnualPer Pay% of Gross
Gross Pay${formatMoney(d.gross)}${formatMoney(d.gross/d.freq)}100%
Retirement (401k)${formatMoney(d.retirement)}${formatMoney(d.retirement/d.freq)}${(d.retPct*100).toFixed(1)}%
Pre-Tax Deductions${formatMoney(d.preTax)}${formatMoney(d.preTax/d.freq)}${(d.preTax/d.gross*100).toFixed(1)}%
Federal Tax${formatMoney(d.fed)}${formatMoney(d.fed/d.freq)}${(d.fed/d.gross*100).toFixed(1)}%
FICA (SS + Med)${formatMoney(d.fica)}${formatMoney(d.fica/d.freq)}${(d.fica/d.gross*100).toFixed(1)}%
State Tax${formatMoney(d.stTax)}${formatMoney(d.stTax/d.freq)}${(d.stTax/d.gross*100).toFixed(1)}%
City Tax${formatMoney(d.ctTax)}${formatMoney(d.ctTax/d.freq)}${(d.ctTax/d.gross*100).toFixed(1)}%
Post-Tax & Other${formatMoney(d.postTax + d.other)}${formatMoney((d.postTax + d.other)/d.freq)}${((d.postTax + d.other)/d.gross*100).toFixed(1)}%
Net Take-Home${formatMoney(d.net)}${formatMoney(d.netPer)}${(d.net/d.gross*100).toFixed(1)}%

🔄 Retirement Contribution Analysis

Contribution %Annual 401kNet Pay / PeriodEffective Tax RateAnnual Take-Home
Annual Tax Shield
-${formatMoney(d.fed * d.retPct)}
Annual 401k Savings
${formatMoney(d.retirement)}
`; updateAnalysis(); } function updateAnalysis() { if (!currentData.gross) return; const d = currentData; const currentPct = parseFloat(document.getElementById('retSlider').value); document.getElementById('sliderVal').textContent = currentPct.toFixed(1) + '%'; const rows = []; const step = 1; for (let p = 0; p <= 25; p += step) { const pct = p / 100; const retAmt = d.gross * pct; const stateCode = document.getElementById('state').value; const fedtaxable = Math.max(0, d.gross - d.stdDed - retAmt - d.preTax); const taxable = Math.max(0, d.gross - retAmt - d.preTax); const fed = calcBracketTax(fedtaxable, FED_BRACKETS[d.status]); const fica = d.fica; // SS/Medicare don't change with 401k //const stTax = d.stTax; const stTax = calcStateTax(taxable, stateCode); const cityPct = (parseFloat(document.getElementById('cityTax').value) / 100) || 0; const ctTax = Math.max(0,taxable * cityPct); const net = d.gross - retAmt - d.preTax - fed - fica - stTax - ctTax - d.postTax - d.other; const netPer = net / d.freq; const effRate = d.gross > 0 ? ((fed + fica + stTax + ctTax) / d.gross) * 100 : 0; rows.push({pct, retAmt, netPer, effRate, net}); } const tbody = document.getElementById('analysisBody'); tbody.innerHTML = rows.map(r => ` ${(r.pct*100).toFixed(1)}% ${formatMoney(r.retAmt)} ${formatMoney(r.netPer)} ${r.effRate.toFixed(1)}% ${formatMoney(r.net)} `).join(''); drawChart(rows, currentPct); } function drawChart(rows, currentPct) { const canvas = document.getElementById('analysisChart'); const ctx = canvas.getContext('2d'); const dpr = window.devicePixelRatio || 1; const rect = canvas.parentElement.getBoundingClientRect(); canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; ctx.scale(dpr, dpr); const w = rect.width, h = rect.height; ctx.clearRect(0, 0, w, h); const maxVal = Math.max(...rows.map(r => r.net)); const barWidth = Math.min((w - 40) / rows.length * 0.6, 40); const gap = (w - 40) / rows.length * 0.4; rows.forEach((r, i) => { const barH = (r.net / maxVal) * (h - 50); const x = 20 + i * (barWidth + gap); const y = h - 30 - barH; ctx.fillStyle = Math.abs(r.pct - currentPct) < 0.005 ? '#6c5ce7' : '#2d3148'; ctx.beginPath(); ctx.roundRect(x, y, barWidth, barH, 4); ctx.fill(); ctx.fillStyle = '#8b8fa3'; ctx.font = '11px sans-serif'; ctx.textAlign = 'center'; ctx.fillText((r.pct*100).toFixed(0) + '%', x + barWidth/2, h - 10); if (Math.abs(r.pct - currentPct) < 0.005) { ctx.fillStyle = '#a29bfe'; ctx.font = 'bold 12px sans-serif'; ctx.fillText(formatMoney(r.net), x + barWidth/2, y - 8); } }); } // Wire up events document.getElementById('calcBtn').addEventListener('click', calculate); document.getElementById('retPct').addEventListener('input', (e) => { document.getElementById('retPctDisplay').textContent = e.target.value + '%'; }); document.getElementById('cityTax').addEventListener('input', (e) => { document.getElementById('cityTaxDisplay').textContent = e.target.value + '%'; }); // Auto-calculate on load window.addEventListener('DOMContentLoaded', () => { // Attach listeners to all inputs for auto-update ['grossPay','payCheck','payFreq','filingStatus','state','preTaxPay','postTaxPay','otherPay'].forEach(id => { document.getElementById(id).addEventListener('input', calculate); document.getElementById(id).addEventListener('change', calculate); }); calculate(); });