pT+cH+2) continue;
ctx.strokeStyle=gv===0?'rgba(255,59,59,0.45)':'rgba(28,42,62,0.9)';ctx.lineWidth=gv===0?1.5:0.5;
if(gv===0) ctx.setLineDash([4,3]);
ctx.beginPath();ctx.moveTo(pL,gy);ctx.lineTo(pL+cW,gy);ctx.stroke();ctx.setLineDash([]);
ctx.fillStyle=gv===0?'rgba(255,80,80,0.85)':'#4a6278';
ctx.font=(gv===0?'bold ':'')+'9px ui-monospace';ctx.textAlign='right';
ctx.fillText(gv===0?'$0':fmtK(gv),pL-3,gy+3);
}
if(zeroY>=pT&&zeroY<=pT+cH){ctx.fillStyle='rgba(255,80,80,0.6)';ctx.font='bold 8px ui-monospace';ctx.textAlign='left';ctx.fillText('DEPLETED',pL+4,zeroY-4);}
// Crisis band — shaded column spanning the war-start week
var cxBand=toX(CRISIS_W-1), cxLine=toX(CRISIS_W);
ctx.fillStyle='rgba(255,176,32,0.06)';
ctx.fillRect(cxBand,pT,cxLine-cxBand,cH);
// Crisis vertical line
ctx.strokeStyle='rgba(255,176,32,0.8)';ctx.lineWidth=1.5;ctx.setLineDash([3,3]);
ctx.beginPath();ctx.moveTo(cxLine,pT);ctx.lineTo(cxLine,pT+cH);ctx.stroke();ctx.setLineDash([]);
ctx.fillStyle='rgba(255,176,32,0.95)';ctx.font='bold 8px ui-monospace';ctx.textAlign='center';
ctx.fillText('⚡ W9',cxLine,pT+10);
ctx.fillStyle='rgba(255,176,32,0.7)';ctx.font='7px ui-monospace';
ctx.fillText('FEB 28',cxLine,pT+20);ctx.fillText('WAR',cxLine,pT+29);
// Today line
var txL=toX(TODAY_W);
ctx.strokeStyle='rgba(255,255,255,0.18)';ctx.lineWidth=1;ctx.setLineDash([2,4]);
ctx.beginPath();ctx.moveTo(txL,pT);ctx.lineTo(txL,pT+cH);ctx.stroke();ctx.setLineDash([]);
ctx.fillStyle='rgba(255,255,255,0.45)';ctx.font='bold 8px ui-monospace';ctx.textAlign='center';ctx.fillText('NOW',txL,pT+10);
// Trajectory lines + zero crossings
trajs.forEach(function(pts,i){
var col=TIER_COLORS[i]||'#888';
ctx.beginPath();ctx.strokeStyle=col;ctx.lineWidth=2;ctx.lineJoin='round';
pts.forEach(function(p,idx){var x=toX(p.w),y=toY(p.v);idx===0?ctx.moveTo(x,y):ctx.lineTo(x,y);});
ctx.stroke();
var zc=findZeroCrossing(pts);
if(zc!==null&&zeroY>=pT&&zeroY<=pT+cH){
var zx=toX(zc);
ctx.beginPath();ctx.arc(zx,zeroY,4.5,0,Math.PI*2);ctx.fillStyle=col;ctx.fill();
ctx.strokeStyle='rgba(0,0,0,0.6)';ctx.lineWidth=1;ctx.stroke();
ctx.fillStyle=col;ctx.font='bold 9px ui-monospace';ctx.textAlign='center';
ctx.fillText('W'+Math.round(zc),zx,zeroY-10);
}
// W26 end label
var last=pts[pts.length-1];
var ey=Math.max(pT+5,Math.min(pT+cH-4,toY(last.v)));
ctx.fillStyle=col;ctx.font='bold 9px ui-monospace';ctx.textAlign='left';
ctx.fillText(fmtK(last.v),pL+cW+4,ey+3);
});
// X-axis: tick EVERY week, label at major weeks
var bottomY=pT+cH;
var majorWks=[0,4,8,12,16,20,25];
for(var wt=0;wt<=CHART_WEEKS;wt++){
var xt=toX(wt),isMaj=majorWks.indexOf(wt)>=0;
ctx.strokeStyle=isMaj?'#2a3f5a':'#162030';ctx.lineWidth=isMaj?0.8:0.4;
ctx.beginPath();ctx.moveTo(xt,bottomY);ctx.lineTo(xt,bottomY+(isMaj?7:3));ctx.stroke();
}
ctx.fillStyle='#5a7490';ctx.font='8px ui-monospace';ctx.textAlign='center';
majorWks.forEach(function(wt){
var xt=toX(wt),dn=WEEK_DATES[wt]||'';
ctx.fillStyle='#6a8aa0';
ctx.fillText('W'+(wt+1),xt,H-22);
ctx.fillStyle='#3d5068';
ctx.fillText(dn,xt,H-10);
});
}
function buildCFLegend(tiers){
var html='';
tiers.forEach(function(t,i){var col=TIER_COLORS[i]||'#888';html+='
'+escHtml(t.name.split('/')[0].trim())+'
';});
return html+'
';
}
function buildCFDepletion(tiers,noDebt){
var html='';
tiers.forEach(function(t,i){
var pts=calcTrajectory(t,noDebt),zc=findZeroCrossing(pts),last=pts[pts.length-1];
var col=TIER_COLORS[i]||'#888';
var cls=zc?'cf-dep-red':last.v>0?'cf-dep-green':'cf-dep-red';
if(col==='#22d3ee') cls='cf-dep-cyan';
else if(!zc&&last.v>0&&i<=1) cls='cf-dep-amber';
var label=t.name.split('/')[0].split('(')[0].trim();
var status=zc?'depleted W'+Math.round(zc):(last.v>=0?'surplus':'deficit');
html+='
'+escHtml(label)+' · '+status+' · W26: '+fmtK(last.v)+'
';
});
return html+'
';
}
function buildAssumptions(tiers,noDebt){
var BASES=[
'Westpac 2025: 65-74 median $15,829 — assets test + 5yr cost erosion',
'Savings.com.au 2025: 50% renters <$5k; Westpac 30-34 median $1,104',
'Westpac 35-44 median $811 cash; mtg avg $38.7k incl offset. 5yr rate squeeze',
'Mtg holder avg $38.7k adj 50% — $1M+ debt at 4.35% drained buffer',
'Asset-rich/cash-poor — $10.5k/mo debt svc consumed buffer',
'Morningstar 2025: 15% of 65+ carry housing debt; 39% carry credit card balance'
];
var html='';
// ── EXPLAINER: Why burn > income ──
html+='
';
html+='📐 HOW TO READ THIS TABLE
';
html+='The "Net (Crisis)" column is the monthly deficit — how much more is spent than earned each month. ';
html+='It can be larger than income when Debt Service + Living Costs together exceed take-home pay. ';
html+='For Middle Income: Debt Service ($8,875) alone exceeds income ($8,200). Every dollar earned is consumed by debt obligations, ';
html+='and the $6,900+ in living costs must come from savings. ';
html+='This reflects 3 years of RBA rates at 4.35% (2022–2025) pushing repayments up ~10% of income (RBA 2025 Bulletin). ';
html+='The table below shows the full arithmetic: Income − Debt Service − Living − Crisis Surcharge = Net Monthly.';
html+='
';
html+='
⚙ Monthly Cash Flow Breakdown — W1 (Jan 5, 2026)'+(noDebt?' · Scenario B (Zero Debt)':'')+'
';
html+='
';
html+='
';
html+='Total Out = Debt Service + Living Costs + Crisis Surcharge. ';
html+='Net = Income − Total Out (negative = drawing from savings each month). ';
if(noDebt){
html+='Scenario B sets Debt Service = $0 and Total Debt = $0 for all rows to isolate the pure cost-of-living shock. ';
html+='This shows which households are structurally vulnerable even without mortgage, car-loan, or card repayments. ';
} else {
html+='⚠ Debt/Inc >70% means debt alone consumes most of each dollar earned. ';
html+='At >100% (Middle Income), debt service alone exceeds all take-home pay — the household runs at a structural deficit regardless of the crisis. ';
}
html+='The Markets tab shows item-level % price increases; this Households table converts those into a $/month surcharge based on each cohort’s likely spending mix (fuel, food, utilities, indirect goods). '; html+='Liquid savings = accessible cash only — excludes super, home equity, and investment assets.';
html+='
';
html+='
';
return html;
}
// ── BANK ──
var _lastBankTiers=null,_lastBankTiersND=null;
function renderBank(be){
if(!be||!be.household) return;
var el=document.getElementById('bank-content');
document.getElementById('bank-dtg').textContent=fmtDTG(be.lastUpdated);
var tiers=be.household.tiers||[],gov=be.government||{};
var hasRetiree=tiers.some(function(t){return t.name.indexOf('Retiree')>=0;});
if(!hasRetiree) tiers=tiers.concat([{name:'Retiree (Super)',income:4900,debt:0,debtService:0,liquidSavings:55000,monthlyDeficit:-400,monthlyExtra:300,runwayMonths:137}]);
var tiersOrig=tiers; // all 6 tiers including Retiree
var tiersAll=tiers;
_lastBankTiers=tiersOrig;_lastBankTiersND=tiersAll;
var html='';
// Scenario A
html+='';
html+='
SCENARIO A — WITH CURRENT DEBT & OBLIGATIONS (INC. RETIREE)
';
html+='
Week-by-week liquid savings from W1 (Jan 5). All debt obligations included. Crisis surcharge applied W9+ (Iran war, Feb 28). This surcharge is the household dollar translation of the item inflation shown in Markets. Retiree (super, zero debt) included for comparison. W26 = Jun 29, 2026.
';
html+=buildAssumptions(tiersOrig);
html+=buildCFLegend(tiersOrig);
html+='
';
html+=buildCFDepletion(tiersOrig,false);
html+='
';
// Scenario B
html+='';
html+='
SCENARIO B — ZERO DEBT (ALL DEMOGRAPHICS)
';
html+='
Same demographics, debt service removed. Middle & High Income convert from deficit to surplus. Retiree is unchanged (already zero debt).
';
html+=buildAssumptions(tiersAll,true);
html+=buildCFLegend(tiersAll);
html+='
';
html+=buildCFDepletion(tiersAll,true);
html+='
';
// Table
html+='';
html+='⚠️ KEY INSIGHT: Without debt, Pensioner still depletes by W16 — income is genuinely insufficient. Low Income survives to W26 (+$560). Avg Worker holds +$12k. Middle and High Income accumulate wealth during the crisis. Debt — not the fuel crisis — is the primary financial threat for the top three brackets.
';
html+='🏛️ Govt: $'+(gov.totalBufferBn||4.75)+'bn committed · Excise cut expires Jun 30 · Monthly burn ~$'+((gov.monthlyBurnRateM||900)/1000).toFixed(1)+'bn
';
el.innerHTML=html;
// Only draw now if the section is already visible; otherwise showSection handles it
var _hs=document.getElementById('sec-households');
if(_hs&&_hs.classList.contains('active')){
requestAnimationFrame(function(){requestAnimationFrame(function(){
drawCFChart('cfCanvasA',tiersOrig,false);
drawCFChart('cfCanvasB',tiersAll,true);
});});
}
}
// ── UTILS ──
function escHtml(s){if(!s) return '';return String(s).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"');}
window.addEventListener('resize',function(){
clearTimeout(window._rt);
window._rt=setTimeout(function(){
if(_lastInf) drawCPIChart(_lastInf);
if(_lastBankTiers) drawCFChart('cfCanvasA',_lastBankTiers,false);
if(_lastBankTiersND) drawCFChart('cfCanvasB',_lastBankTiersND,true);
},150);
});
// ── MAIN ──
function refresh(){
api('/data.json').then(function(data){
updateHeader(data);
renderTier1(data);
renderAlert(data);
renderTrajectory(data.trajectory||null);
renderEvents(data.dbEvents||[],data.dbLastUpdated);
renderReviewPanels(data);
renderServo(data.servoOutages);
renderFleet(data.shipments);
renderReserves(data.reserves);
renderRefineries(data.refineries);
renderRoutes(data.shippingRoutes);
renderTroopDeployments(data.troopDeployments);
renderDominoModel(data.dominoModel);
renderCPI(data.inflation);
renderBank(data.bankEmpty);
}).catch(function(e){console.error('Refresh error:',e);});
}
refresh();
setInterval(refresh,REFRESH_MS);
})();