Loading...
——————
——:——
⛽ Diesel Reserves
days
🏭 SG Refinery
%
capacity
⛽ Servo Outages
⚡ War + Fuel + Food ——
Loading...
🧠 Research Review——
⛽ Fuel Levels——
🛢️ Fleet Arrivals
⛽ Servo Outages——
🚢 Supply Routes / Chokepoints
🏭 Asian Refineries
🎯 Invasion Watch — Troop Deployments PRE-DEPLOYMENT
🁢 Domino Cascade — 6-Month Outlook
🧠 Research Review——
📊 Price Shock Monitor——
🛒 Highest Item Inflation Since Jan 1
🧠 Research Review——
🏦 Household Savings Trajectory — W1→W26——
🧠 Research Review——
📡 Supply Chain Event Feed ——
Loading events...
FRAN // Supply Chain Intelligence  |  Sources: FuelRadar · ABS · RBA · DCCEEW · IEA · Lloyd's · Bloomberg · Guardian · Reuters  |  All data estimated — verify independently
'; el.innerHTML=html; } // ── CPI CANVAS ── function drawCPIChart(inf){ var canvas=document.getElementById('cpiCanvas'); if(!canvas||!inf) return; var dpr=window.devicePixelRatio||1,W=canvas.offsetWidth,H=240; if(!W||W<50) return; // canvas is hidden — bail canvas.width=W*dpr;canvas.height=H*dpr;canvas.style.width=W+'px';canvas.style.height=H+'px'; var ctx=canvas.getContext('2d');ctx.scale(dpr,dpr); var pL=38,pR=10,pT=12,pB=26,tW=W-pL-pR,cH=H-pT-pB; var totalDays=180; var m=inf.metro||{}, r=inf.regional||{}; var mC=m.current||7.2, rC=r.current||9.8; var mPeak=m.peakMetro||10.8, rPeak=r.peakRegional||14.5; var maxY=Math.max(15,mPeak,rPeak)*1.12; maxY=Math.ceil(maxY/2)*2; function toX(d){return pL+(d/totalDays)*tW;} function toY(v){return pT+cH-(v/maxY)*cH;} var bands=[{l:5,c:'rgba(245,158,11,0.09)'},{l:7,c:'rgba(249,115,22,0.10)'},{l:9,c:'rgba(239,68,68,0.12)'},{l:12,c:'rgba(220,38,38,0.14)'}]; var prev=pT+cH; bands.forEach(function(b){var y=toY(b.l);if(y>pT){ctx.fillStyle=b.c;ctx.fillRect(pL,y,tW,prev-y);prev=y;}}); ctx.strokeStyle='#1f2937';ctx.lineWidth=0.5; for(var gv=0;gv<=maxY;gv+=2){ var gy=toY(gv);ctx.beginPath();ctx.moveTo(pL,gy);ctx.lineTo(pL+tW,gy);ctx.stroke(); ctx.fillStyle='#4a6278';ctx.font='9px ui-monospace';ctx.textAlign='right';ctx.fillText(gv+'%',pL-3,gy+3); } var tlines=[{l:5,c:'rgba(245,158,11,0.5)',lbl:'5% Alert'},{l:7,c:'rgba(249,115,22,0.55)',lbl:'7% Warn'},{l:9,c:'rgba(239,68,68,0.6)',lbl:'9% Emerg'},{l:12,c:'rgba(220,38,38,0.6)',lbl:'12% Crisis'}]; tlines.forEach(function(t){ if(t.l>maxY) return; var y=toY(t.l);ctx.save();ctx.strokeStyle=t.c;ctx.lineWidth=1;ctx.setLineDash([2,3]);ctx.beginPath();ctx.moveTo(pL,y);ctx.lineTo(pL+tW,y);ctx.stroke();ctx.setLineDash([]);ctx.restore();ctx.fillStyle=t.c;ctx.font='bold 8px ui-monospace';ctx.textAlign='left';ctx.fillText(t.lbl,pL+3,y-2);}); var mPeakDay=103; // May 15 from Feb 1 baseline var rPeakDay=108; // May 20 from Feb 1 baseline var todayDay=61; function lerp(a,day){if(day<=a[0].d)return a[0].v;if(day>=a[a.length-1].d)return a[a.length-1].v;for(var i=0;i=a[i].d&&day<=a[i+1].d){var t=(day-a[i].d)/(a[i+1].d-a[i].d);return a[i].v+t*(a[i+1].v-a[i+1>=a.length?i:i].v);}return a[a.length-1].v;} function interp(a,day){if(day<=a[0].d)return a[0].v;if(day>=a[a.length-1].d)return a[a.length-1].v;for(var i=0;i=a[i].d&&day<=a[i+1].d){var t=(day-a[i].d)/(a[i+1].d-a[i].d);return a[i].v+t*(a[i+1].v-a[i].v);}}return a[a.length-1].v;} var mHist=[{d:0,v:m['60daysAgo']||0.8},{d:30,v:m['30daysAgo']||2.1},{d:todayDay,v:mC}]; var rHist=[{d:0,v:r['60daysAgo']||1.1},{d:30,v:r['30daysAgo']||3.4},{d:todayDay,v:rC}]; var mProj=[{d:todayDay,v:mC},{d:mPeakDay,v:mPeak},{d:180,v:Math.max(mC,mPeak-0.5)}]; var rProj=[{d:todayDay,v:rC},{d:rPeakDay,v:rPeak},{d:180,v:Math.max(rC,rPeak-0.7)}]; function fill(a,endD,col){ctx.beginPath();for(var d=0;d<=endD;d++){var x=toX(d),y=toY(interp(a,d));d===0?ctx.moveTo(x,y):ctx.lineTo(x,y);}ctx.lineTo(toX(endD),pT+cH);ctx.lineTo(toX(0),pT+cH);ctx.closePath();ctx.fillStyle=col;ctx.fill();} function line(a,startD,endD,col,w,dash){ctx.save();if(dash)ctx.setLineDash(dash);ctx.beginPath();for(var d=startD;d<=endD;d++){var x=toX(d),y=toY(interp(a,d));d===startD?ctx.moveTo(x,y):ctx.lineTo(x,y);}ctx.strokeStyle=col;ctx.lineWidth=w;ctx.stroke();ctx.restore();} fill(mHist,todayDay,'rgba(74,158,255,0.08)');fill(rHist,todayDay,'rgba(255,176,32,0.08)'); line(mHist,0,todayDay,'#4a9eff',1.8);line(rHist,0,todayDay,'#ffb020',1.8); line(mProj,todayDay,180,'rgba(74,158,255,0.72)',1.5,[4,3]); line(rProj,todayDay,180,'rgba(255,176,32,0.72)',1.5,[4,3]); var wL=toX(27); ctx.strokeStyle='rgba(255,176,32,0.5)';ctx.lineWidth=1.5;ctx.setLineDash([3,3]); ctx.beginPath();ctx.moveTo(wL,pT);ctx.lineTo(wL,pT+cH);ctx.stroke();ctx.setLineDash([]); ctx.fillStyle='rgba(255,176,32,0.8)';ctx.font='bold 8px ui-monospace';ctx.textAlign='center';ctx.fillText('⚡ WAR',wL,pT+18); var tx=toX(todayDay);ctx.save();ctx.strokeStyle='rgba(255,255,255,0.2)';ctx.lineWidth=1;ctx.setLineDash([3,3]);ctx.beginPath();ctx.moveTo(tx,pT);ctx.lineTo(tx,pT+cH);ctx.stroke();ctx.setLineDash([]);ctx.restore(); ctx.fillStyle='rgba(255,255,255,0.4)';ctx.font='bold 8px ui-monospace';ctx.textAlign='center'; ctx.fillText('TODAY',tx,pT+10);ctx.fillText('PROJECTED',pL+tW-45,pT+10); var mpX=toX(mPeakDay), rpX=toX(rPeakDay); ctx.fillStyle='rgba(74,158,255,0.85)';ctx.font='bold 8px ui-monospace';ctx.fillText('Metro peak '+mPeak+'%',Math.min(W-50,mpX+20),toY(mPeak)-4); ctx.fillStyle='rgba(255,176,32,0.9)';ctx.fillText('Regional peak '+rPeak+'%',Math.min(W-58,rpX+22),toY(rPeak)+12); ctx.fillStyle='#4a6278';ctx.font='8px ui-monospace';ctx.textAlign='center'; [{d:0,l:'Feb 1'},{d:30,l:'Mar 3'},{d:60,l:'Apr 2'},{d:90,l:'May 1'},{d:120,l:'May 31'},{d:150,l:'Jun 30'},{d:180,l:'Jul 30'}].forEach(function(x){ctx.fillText(x.l,toX(x.d),H-8);}); } // ── ITEM INFLATION CANVAS ── function drawItemInflationChart(inf){ var series=inf&&inf.weeklyItemSeries?inf.weeklyItemSeries:[]; if(!series.length) return; var groups=[ {key:'fuel', title:'Fuel / Energy', match:function(s){return /Diesel|LPG|Electricity/i.test(s.label||'');}, colors:['#ff3b3b','#ffb020','#facc15']}, {key:'food', title:'Food', match:function(s){return /Tomatoes|Lettuce|Bread/i.test(s.label||'');}, colors:['#00e87a','#22d3ee','#4ade80']}, {key:'services', title:'Services', match:function(s){return /airfares|freight/i.test(s.label||'');}, colors:['#4a9eff','#c084fc','#f472b6']} ]; groups.forEach(function(g){ var canvas=document.getElementById('itemInflationCanvas-'+g.key); if(!canvas) return; var subset=series.filter(g.match); if(!subset.length) return; var dpr=window.devicePixelRatio||1,W=canvas.offsetWidth,H=150; if(!W||W<50) return; canvas.width=W*dpr;canvas.height=H*dpr;canvas.style.width=W+'px';canvas.style.height=H+'px'; var ctx=canvas.getContext('2d');ctx.scale(dpr,dpr); var pL=28,pR=8,pT=12,pB=22,cW=W-pL-pR,cH=H-pT-pB; var maxW=15; var maxY=0; subset.forEach(function(s){ var pts=s.points||[]; pts.forEach(function(p){maxY=Math.max(maxY,p.v||0);}); if(pts.length>=2){ var last=pts[pts.length-1], prev=pts[pts.length-2]; var step=(last.v||0)-(prev.v||0); maxY=Math.max(maxY,(last.v||0)+step*2); } }); maxY=Math.ceil((maxY+1)/2)*2; function toX(w){return pL+((w-1)/(maxW-1))*cW;} function toY(v){return pT+cH-(v/maxY)*cH;} ctx.strokeStyle='#1f2937';ctx.lineWidth=0.5; for(var gy=0;gy<=maxY;gy+=Math.max(2,Math.ceil(maxY/5/2)*2)){ var y=toY(gy); ctx.beginPath();ctx.moveTo(pL,y);ctx.lineTo(pL+cW,y);ctx.stroke(); ctx.fillStyle='#4a6278';ctx.font='8px ui-monospace';ctx.textAlign='right';ctx.fillText(gy+'%',pL-3,y+3); } for(var gw=1;gw<=maxW;gw++){ var x=toX(gw); ctx.strokeStyle=gw>13?'rgba(74,98,120,0.35)':'rgba(28,42,62,0.45)';ctx.lineWidth=0.5; ctx.beginPath();ctx.moveTo(x,pT);ctx.lineTo(x,pT+cH);ctx.stroke(); ctx.fillStyle='#4a6278';ctx.font='7px ui-monospace';ctx.textAlign='center';ctx.fillText('W'+gw,x,H-7); } var warX=toX(9); var nowW=13, nowX=toX(nowW), projW=15, projX=toX(projW); ctx.fillStyle='rgba(255,176,32,0.08)'; ctx.fillRect(warX,pT,Math.max(0,nowX-warX),cH); ctx.fillStyle='rgba(74,158,255,0.04)'; ctx.fillRect(nowX,pT,Math.max(0,projX-nowX),cH); ctx.save();ctx.strokeStyle='rgba(255,176,32,0.8)';ctx.lineWidth=1;ctx.setLineDash([3,3]); ctx.beginPath();ctx.moveTo(warX,pT);ctx.lineTo(warX,pT+cH);ctx.stroke();ctx.restore(); ctx.fillStyle='rgba(255,176,32,0.95)';ctx.font='bold 7px ui-monospace';ctx.textAlign='center';ctx.fillText('WAR',warX,pT+8); ctx.save();ctx.strokeStyle='rgba(255,255,255,0.55)';ctx.lineWidth=1;ctx.setLineDash([2,2]); ctx.beginPath();ctx.moveTo(nowX,pT);ctx.lineTo(nowX,pT+cH);ctx.stroke();ctx.restore(); ctx.fillStyle='rgba(255,255,255,0.85)';ctx.font='bold 7px ui-monospace';ctx.textAlign='center';ctx.fillText('NOW W13',nowX,pT+18); ctx.fillStyle='rgba(74,158,255,0.8)';ctx.fillText('PROJ W15',projX,pT+8); subset.forEach(function(s,idx){ var pts=s.points||[], col=g.colors[idx%g.colors.length]; if(!pts.length) return; ctx.beginPath(); pts.forEach(function(p,i){ var x=toX(p.w), y=toY(p.v||0); i===0?ctx.moveTo(x,y):ctx.lineTo(x,y); }); ctx.strokeStyle=col;ctx.lineWidth=1.8;ctx.stroke(); pts.forEach(function(p){ var x=toX(p.w), y=toY(p.v||0); ctx.fillStyle=col; ctx.beginPath(); ctx.arc(x,y,2,0,Math.PI*2); ctx.fill(); }); var lp=pts[pts.length-1], lx=toX(lp.w), ly=toY(lp.v||0); ctx.fillStyle=col; ctx.beginPath(); ctx.arc(lx,ly,3,0,Math.PI*2); ctx.fill(); var lineLbl=(s.emoji?s.emoji+' ':'')+(s.label||''); var valLbl=(lp.v||0).toFixed(1)+'%'; ctx.font='bold 8px ui-monospace'; var lineTw=ctx.measureText(lineLbl).width; var valTw=ctx.measureText(valLbl).width; var boxW=Math.max(lineTw,valTw)+6; var bx=Math.min(W-boxW-10, lx+6), by=Math.max(pT+10, Math.min(H-18, ly-10)); ctx.fillStyle='rgba(8,12,18,0.88)'; ctx.fillRect(bx-3,by-15,boxW,18); ctx.fillStyle=col; ctx.textAlign='left'; ctx.fillText(lineLbl,bx,by-6); ctx.fillText(valLbl,bx,by+2); if(pts.length>=2){ var prev=pts[pts.length-2], last=pts[pts.length-1]; var step=(last.v||0)-(prev.v||0); var p14=Math.max(0,(last.v||0)+step); var p15=Math.max(0,(last.v||0)+step*2); ctx.save(); ctx.strokeStyle=col;ctx.lineWidth=1.4;ctx.setLineDash([4,3]); ctx.beginPath(); ctx.moveTo(toX(last.w),toY(last.v||0)); ctx.lineTo(toX(14),toY(p14)); ctx.lineTo(toX(15),toY(p15)); ctx.stroke(); ctx.restore(); ctx.fillStyle=col; ctx.beginPath(); ctx.arc(toX(15),toY(p15),2.4,0,Math.PI*2); ctx.fill(); var plbl='→ '+p15.toFixed(1)+'%'; var ptw=ctx.measureText(plbl).width; var pbx=Math.min(W-ptw-10, toX(15)+6), pby=Math.max(pT+10, Math.min(H-8, toY(p15)-2)); ctx.fillStyle='rgba(8,12,18,0.82)'; ctx.fillRect(pbx-3,pby-8,ptw+6,11); ctx.fillStyle=col; ctx.textAlign='left'; ctx.fillText(plbl,pbx,pby); } }); }); } // ── CPI PANEL ── function renderCPI(inf){ if(!inf) return;_lastInf=inf; var el=document.getElementById('cpi-content'); document.getElementById('cpi-dtg').textContent=fmtDTG(inf.lastUpdated); var m=inf.metro||{},r=inf.regional||{}; function tier(v){return v>=12?'crisis':v>=9?'emerg':v>=7?'warn':'ok';} function tierCls(t){return t==='emerg'||t==='crisis'?'tier-emerg':t==='warn'?'tier-warn':'tier-ok';} function tierLbl(t){return{ok:'OK',warn:'WARNING',emerg:'EMERGENCY',crisis:'CRISIS'}[t]||t.toUpperCase();} var mT=tier(m.current||0),rT=tier(r.current||0); var html='
'; html+='
Metro
'+m.current+'%
'+tierLbl(mT)+'
'; html+='
Regional
'+r.current+'%
'+tierLbl(rT)+'
'; html+='
'; html+='
Metro
Regional
Projection
'; var states=inf.states||[]; html+='
'; states.forEach(function(s){ var t=s.tier||'ok',bg=t==='crisis'?'rgba(255,59,59,0.2)':t==='emergency'?'rgba(255,59,59,0.1)':t==='warning'?'rgba(255,176,32,0.1)':'rgba(74,158,255,0.08)'; var col=t==='crisis'||t==='emergency'?'var(--red)':t==='warning'?'var(--amber)':'var(--green)'; var tArr=s.trend>=0.6?'▲▲':s.trend>=0.3?'▲':'➡'; html+=''+s.state+' '+(s.regional!==null?s.regional+'%':'—')+' '+tArr+''; }); html+='
'; el.innerHTML=html; var dEl = document.getElementById('drivers-content'); var items = (inf.weeklyItems&&inf.weeklyItems.length)?inf.weeklyItems:null; if(dEl && items){ var dHtml = '
Observed retail price increases since pre-war baseline (late Feb 26). Not official CPI — these are market price movements. Y-axis = % change from pre-war.
'; dHtml += '
'; dHtml += '
⛽ Fuel / Energy
'; dHtml += '
🥬 Food
'; dHtml += '
✈️ Services / Utilities
'; dHtml += '
'; dHtml += '
'; var maxC = Math.max.apply(null, items.map(function(d){return d.weeklyChange||0;})); items.forEach(function(d, i){ var change = d.weeklyChange||0; var pct = maxC?(change / maxC) * 100:0; var col = i===0 ? 'var(--red)' : i<3 ? 'var(--amber)' : 'var(--blue)'; dHtml += '
'; dHtml += '
'+(d.emoji||'🛒')+'
'; dHtml += '
'; dHtml += '
'; dHtml += ''+escHtml(d.label)+''; dHtml += '+'+change.toFixed(1)+'% YTD'; dHtml += '
'; dHtml += '
'+escHtml(d.category||'')+' · '+escHtml(d.basis||'')+'
'; dHtml += '
'; dHtml += '
'; dHtml += '
'; }); dHtml += '
Item trajectories show cumulative price increases since Jan 1, 2026. WAR marks the disruption point, NOW W13 marks the current week, the amber band shows the post-war inflation acceleration window, and the dashed line projects two weeks forward to W15. The Households tab converts these item-level % increases into monthly dollar impact for each demographic basket.
'; dEl.innerHTML = dHtml; } else if(dEl && inf.drivers && inf.drivers.length){ var dHtml2 = '
'; var maxD = Math.max.apply(null, inf.drivers.map(function(d){return d.contribution||0;})); inf.drivers.forEach(function(d, i){ var pct2 = maxD?((d.contribution||0) / maxD) * 100:0; var col2 = i===0 ? 'var(--red)' : i===1 ? 'var(--amber)' : 'var(--blue)'; dHtml2 += '
'; dHtml2 += '
'+d.emoji+'
'; dHtml2 += '
'; dHtml2 += '
'; dHtml2 += ''+escHtml(d.label)+''; dHtml2 += '+'+(d.contribution||0)+'%'; dHtml2 += '
'; dHtml2 += '
'; dHtml2 += '
'; dHtml2 += '
'; }); dEl.innerHTML = dHtml2; } // Only draw if markets section is currently visible var _ms=document.getElementById('sec-markets'); if(_ms&&_ms.classList.contains('active')){ requestAnimationFrame(function(){requestAnimationFrame(function(){drawCPIChart(_lastInf);drawItemInflationChart(_lastInf);});}); } } // ── CASHFLOW CHARTS ── var TIER_COLORS=['#ff3b3b','#ffb020','#4a9eff','#c084fc','#00e87a','#22d3ee']; var CRISIS_W=9;var TODAY_W=13;var CHART_WEEKS=26;var WPM=4.333; function calcTrajectory(tier,noDebt){ var crisisMo=noDebt?(tier.monthlyDeficit+(tier.debtService||0)):tier.monthlyDeficit; var preMo=crisisMo+(tier.monthlyExtra||0); var preWk=preMo/WPM,crisisWk=crisisMo/WPM; var bal=tier.liquidSavings; var pts=[{w:0,v:bal}]; for(var w=1;w<=CHART_WEEKS;w++){bal+=(w0&&pts[i].v<=0){var t=pts[i-1].v/(pts[i-1].v-pts[i].v);return pts[i-1].w+t;} } return null; } function fmtK(v){ var abs=Math.abs(v); var s=abs>=1000?(Math.round(abs/100)/10)+'k':Math.round(abs).toString(); return (v<0?'-$':'+$')+s; } // Week → date label map (2026, W starting Mon Jan 5) var WEEK_DATES={0:'Jan 5',1:'Jan 12',2:'Jan 19',3:'Jan 26',4:'Feb 2',5:'Feb 9',6:'Feb 16',7:'Feb 23',8:'Mar 2',9:'Mar 9',10:'Mar 16',11:'Mar 23',12:'Mar 30',13:'Apr 6',14:'Apr 13',15:'Apr 20',16:'Apr 27',17:'May 4',18:'May 11',19:'May 18',20:'May 25',21:'Jun 1',22:'Jun 8',23:'Jun 15',24:'Jun 22',25:'Jun 29',26:'Jul 6'}; function drawCFChart(canvasId,tiers,noDebt){ var canvas=document.getElementById(canvasId); if(!canvas||!tiers.length) return; var dpr=window.devicePixelRatio||1; var W=canvas.offsetWidth,H=300; if(!W||W<50) return; // canvas is hidden — bail, showSection will redraw canvas.width=W*dpr;canvas.height=H*dpr;canvas.style.width=W+'px';canvas.style.height=H+'px'; var ctx=canvas.getContext('2d');ctx.scale(dpr,dpr); var pL=58,pR=52,pT=22,pB=36; var cW=W-pL-pR,cH=H-pT-pB; var trajs=tiers.map(function(t){return calcTrajectory(t,noDebt);}); var minY=0,maxY=0; trajs.forEach(function(pts){pts.forEach(function(p){minY=Math.min(minY,p.v);maxY=Math.max(maxY,p.v);});}); var yStep=maxY>60000?20000:maxY>25000?10000:maxY>10000?5000:2000; minY=Math.floor((minY*1.12)/yStep)*yStep; maxY=Math.ceil((maxY*1.08)/yStep)*yStep; if(minY===0) minY=-yStep; function toX(w){return pL+(w/CHART_WEEKS)*cW;} function toY(v){return pT+cH*(1-(v-minY)/(maxY-minY));} var zeroY=toY(0); // Zone fills if(zeroYpT+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+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; tiers.forEach(function(t,i){ var col = TIER_COLORS[i]||'#888'; var inc = t.income||0; var dsRaw = t.debtService||0; var ds = noDebt?0:dsRaw; var extra = t.monthlyExtra||0; var defCrisis = noDebt?((t.monthlyDeficit||0)+dsRaw):(t.monthlyDeficit||0); // remove debt drag in Scenario B var defPrewar = defCrisis + extra; var totalOut = inc + Math.abs(defCrisis); var living = totalOut - ds - extra; var dsInc = inc>0 ? Math.round(ds/inc*100) : 0; var debt = noDebt?0:(t.debt||0); var debtStr = debt>=1000000?'$'+(debt/1000000).toFixed(2)+'M':debt>=1000?'$'+Math.round(debt/1000)+'k':'$'+debt; var label = t.name.split('/')[0].trim(); var cb = t.crisisBreakdown||{}; var breakStr = cb.fuel?'fuel +$'+cb.fuel+' · food +$'+cb.food+' · util +$'+cb.utilities:'—'; // Colour coding var dsCol = dsInc>100?'var(--red)':dsInc>70?'var(--amber)':'var(--text-dim)'; var outCol = dsInc>100?'var(--red)':'var(--text-dim)'; var preCol = defPrewar<0?'var(--amber)':'var(--green)'; var criCol = 'var(--red)'; // Warn flag var warnFlag = dsInc>=100 ? '' : dsInc>=70 ? '' : ''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; }); html+='
DemographicIncomeDebt SvcDebt/IncLiving= Total OutNet (pre-war)⚡ SurchargeNet (crisis)Savings · Basis
'+escHtml(label)+'$'+inc.toLocaleString()+'$'+ds.toLocaleString()+warnFlag+''+(dsInc>0?dsInc+'%':'—')+'$'+Math.round(living).toLocaleString()+'$'+totalOut.toLocaleString()+''+(defPrewar<0?'-$':'+$')+Math.abs(Math.round(defPrewar)).toLocaleString()+'+$'+extra.toLocaleString()+'
'+escHtml(breakStr)+'
-$'+Math.abs(defCrisis).toLocaleString()+'$'+t.liquidSavings.toLocaleString()+' · '+escHtml(BASES[i]||'—')+'
'; 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+=''; html+=''; tiersAll.forEach(function(t,i){ var cf=t.monthlyDeficit||0,ds=t.debtService||0,rw=t.runwayMonths||999; var rcls=rw<=3?'crit-row':rw<=12?'warn-row':''; var col=TIER_COLORS[i]||'#888'; var ptsD=calcTrajectory(t,false),zcD=findZeroCrossing(ptsD); var ptsN=calcTrajectory(t,true),ndLast=ptsN[ptsN.length-1].v; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; html+=''; }); html+='
BracketIncome/moDebt SvcCrisis+Monthly FlowDepletes (debt)W26 (no debt)
'+escHtml(t.name.split('/')[0].trim())+'$'+(t.income||0).toLocaleString()+'$'+(ds||0).toLocaleString()+'+$'+(t.monthlyExtra||0)+''+(cf<0?'-$':'+$')+Math.abs(cf).toLocaleString()+''+(zcD?'W'+Math.round(zcD):'SURVIVES')+''+fmtK(ndLast)+'
'; 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); })();