diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9b319a5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Dockerfile.inline
+deploy-nogit.sh
diff --git a/index.html b/index.html
index e21953c..ede3603 100644
--- a/index.html
+++ b/index.html
@@ -342,11 +342,18 @@ Check your Browserslist config to be sure that your targets are set up correctly
Load by phase (Mean kW per hour)
@@ -1242,6 +1252,45 @@ Object.entries(phaseMeta).forEach(([id,m])=>{
rebalance_kva:d.rebalance_kva, phase_note:m.note(d),
aggNote:PHASE_AGGNOTE[id]});
});
+
+/* ---------- supply headroom (peak demand vs supply capacity, kVA) ---------- */
+const HEADROOM={
+ kaimana:{supply:60,peak:44,flag:"Peaks near three-quarters of supply at the afternoon milking."},
+ coolstore:{supply:50,peak:41,flag:"Running at 82% of supply on peak — little room left for another load."},
+ riverside:{supply:40,peak:26,flag:"Comfortable — about a third of supply spare at peak."},
+ tikipunga:{supply:60,peak:22,flag:"Plenty of headroom — well under supply capacity."},
+ cookham:{supply:45,peak:28,flag:"Steady — around 60% of supply at the daytime peak."},
+ kamo:{supply:30,peak:12,flag:"Light load — large headroom on the supply."}
+};
+Object.entries(HEADROOM).forEach(([id,h])=>{ if(siteById[id]) Object.assign(siteById[id],h); });
+function headroomTone(pct){ return pct>85?"#E23B34":(pct>=70?"#F59E0B":"#1F9E70"); }
+function headroomBar(s,variant){
+ if(!s||!s.supply) return "";
+ const pct=Math.round(s.peak/s.supply*100), w=Math.min(100,pct), c=headroomTone(pct);
+ if(variant==="mini"){
+ return `
+
+ Supply headroom
+ ${s.peak}/${s.supply} kVA · ${pct}%
+
+
+
+
+
`;
+ }
+ return `
+
Supply headroom
+
+ Peak demand vs supply capacity
+ ${s.peak} / ${s.supply} kVA · ${pct}%
+
+
+
+
+
${s.flag}
+
Peak demand from the meter’s per-phase apparent power; supply capacity is the site’s connection rating. Illustrative sample data.
+
`;
+}
const STATUS_LABEL={red:"Action needed",amber:"Opportunity",green:"Normal"};
const STATUS_RANK={red:0,amber:1,green:2};
@@ -1358,6 +1407,8 @@ window.dashboard=function(){
healthB(){ const s=this.currentSite; return s?healthBlock(s):""; },
watchB(){ const s=this.currentSite; return s?watchingList(s):""; },
actionB(){ const s=this.currentSite; return s?actionHtml(s):""; },
+ headroomMini(s){ return headroomBar(s,"mini"); },
+ headroomB(){ const s=this.currentSite; return s?headroomBar(s,"full"):""; },
sankeyB(){ const s=this.currentSite; return s?sankeySVG(energyData[s.id].sankey):""; },
sankeyLeg(){ const s=this.currentSite; return s?sankeyLegend(energyData[s.id].sankey):""; },
sankeyNote(){ const s=this.currentSite; return s?energyData[s.id].sankey.note:""; },