diff --git a/index.html b/index.html
index 71e5abd..76047fc 100644
--- a/index.html
+++ b/index.html
@@ -25,6 +25,7 @@ const label=d=>d.slice(5).replace('-','/');
function bars(el,data,series,max){const showValues=data.length<=12;el.style.minWidth=Math.max(0,data.length*48)+'px';el.innerHTML=data.map(d=>`
${series.map(s=>`
${showValues?`${s.format(d[s.key])}`:''}
`).join('')}
${label(d.date)}
`).join('')}
function lineChart(el,data,series,format){const w=Math.max(760,data.length*42),h=260,p={l:48,r:18,t:18,b:34},vals=data.flatMap(d=>series.map(s=>Number(d[s.key])||0)),max=Math.max(...vals,1),min=0,x=i=>p.l+i*(w-p.l-p.r)/Math.max(data.length-1,1),y=v=>h-p.b-(v-min)*(h-p.t-p.b)/(max-min||1),ticks=[0,.25,.5,.75,1];el.innerHTML=``}
async function sha256(value){const bytes=new TextEncoder().encode(value);const digest=await crypto.subtle.digest('SHA-256',bytes);return [...new Uint8Array(digest)].map(x=>x.toString(16).padStart(2,'0')).join('')}
+const ACCESS_CODE_DIGEST={salt:'amazon-za-report-access-v1',hash:'3ffcf23473036ad716ae6862a3be38dd9a2158d5a637681d9f0a962e69692161'};
function showReport(){document.getElementById('login').hidden=true;document.getElementById('report').hidden=false;loadReport()}
async function loadReport(){fetch('data.json',{cache:'no-store'}).then(r=>{if(!r.ok)throw Error('data.json '+r.status);return r.json()}).then(data=>{
const t=data.totals,b=data.budget,all=data.daily,d=all.filter(x=>x.is_actual),fx=16.5,payinZar=49.5,monthlyBudgetZar=544500,dailyBudgetZar=18150,dailyCap=367,installCap=1740,usedBudgetZar=t.e1e2*payinZar,remainingBudgetZar=monthlyBudgetZar-usedBudgetZar;document.getElementById('range').textContent=`Aug 1–31, 2026 · Data through ${data.meta.data_cutoff}`;
@@ -35,6 +36,6 @@ async function loadReport(){fetch('data.json',{cache:'no-store'}).then(r=>{if(!r
document.getElementById('rows').innerHTML=all.map((x,i)=>{const prev=i?all[i-1]:null,spike=x.is_actual&&prev&&prev.is_actual&&Math.abs((x.e1e2_rate||0)-(prev.e1e2_rate||0))>=5;return `| ${x.date} | ${fmt(x.install)} | ${fmt(x.event1)} | ${fmt(x.event2)} | ${fmt(x.e1e2)} | ${pct(x.e1e2_rate)} | ${zar(x.e1e2*payinZar)} | ${zar(dailyBudgetZar)} | ${pct(x.e1e2/dailyCap*100)} | ${money(x.midas_spend)} |
`}).join('');
document.getElementById('weeklyRows').innerHTML=data.weekly.map(w=>`| ${w.label} | ${fmt(w.install)} | ${fmt(w.e1e2)} | ${pct(w.e1e2_rate)} | ${zar(w.e1e2*payinZar)} | ${money(w.midas_spend)} |
`).join('');
}).catch(e=>{document.getElementById('range').textContent='Failed to load data: '+e.message})}
-document.getElementById('loginForm').addEventListener('submit',async e=>{e.preventDefault();const button=document.getElementById('loginButton'),error=document.getElementById('loginError');button.disabled=true;error.textContent='';try{const access=await fetch('access.json',{cache:'no-store'}).then(r=>{if(!r.ok)throw Error('access configuration unavailable');return r.json()}),code=document.getElementById('accessCode').value,codeHash=await sha256(access.salt+':'+code);if(codeHash===access.hash){sessionStorage.setItem('amazonZaCodeAccessV1','granted');showReport()}else{error.textContent='Incorrect access code.'}}catch(_){error.textContent='Unable to verify access. Please try again.'}finally{button.disabled=false}});
+document.getElementById('loginForm').addEventListener('submit',async e=>{e.preventDefault();const button=document.getElementById('loginButton'),error=document.getElementById('loginError');button.disabled=true;error.textContent='';try{const code=document.getElementById('accessCode').value,codeHash=await sha256(ACCESS_CODE_DIGEST.salt+':'+code);if(codeHash===ACCESS_CODE_DIGEST.hash){sessionStorage.setItem('amazonZaCodeAccessV1','granted');showReport()}else{error.textContent='Incorrect access code.'}}catch(_){error.textContent='Unable to verify access. Please try again.'}finally{button.disabled=false}});
if(sessionStorage.getItem('amazonZaCodeAccessV1')==='granted')showReport();