diff --git a/gateway/scripts/templates/dashboard.html b/gateway/scripts/templates/dashboard.html
index c4ee7e4..7b366e1 100644
--- a/gateway/scripts/templates/dashboard.html
+++ b/gateway/scripts/templates/dashboard.html
@@ -2,23 +2,20 @@
AgentsMeeting Dashboard
-AgentsMeeting
-Dashboard
+AgentsMeeting
Dashboard
+function toast(m,t){var e=qs('toast');e.textContent=m;e.className='toast show '+(t||'ok');setTimeout(function(){e.className='toast'},2500)}
+function init(){var b='',p='';for(var i=0;i'+t.label+'';
+if(t.children){p+='';
+for(var j=0;j'+c.label+'';}
+p+='
';
+for(var j=0;j
';}
+p+='';}else{p+='';}}
+qs('tabs').innerHTML=b;qs('panes').innerHTML=p;}
+function sw(id){if(id==='principles'){at='principles';as=localStorage.getItem('s')||'git'}else{at=id;localStorage.setItem('t',id)}
+document.querySelectorAll('.tab-btn').forEach(function(b){b.classList.toggle('active',b.textContent.trim()===T.find(function(t){return t.id===at||(t.children&&t.children.some(function(c){return c.id===at}))}).label)});
+document.querySelectorAll('.tab-pane').forEach(function(p){p.classList.toggle('active',p.id==='pn-'+id)});
+if(id==='principles'){document.querySelectorAll('.sub-btn').forEach(function(b){b.classList.toggle('active',b.textContent.trim()===T[3].children.find(function(c){return c.id===as}).label)});document.querySelectorAll('[id^="sp-"]').forEach(function(p){p.style.display=p.id==='sp-'+as?'':'none'});(FETCHES[as]||function(){})()}}
+function sws(id){as=id;localStorage.setItem('s',id);document.querySelectorAll('.sub-btn').forEach(function(b){b.classList.toggle('active',b.textContent.trim()===T[3].children.find(function(c){return c.id===as}).label)});document.querySelectorAll('[id^="sp-"]').forEach(function(p){p.style.display=p.id==='sp-'+id?'':'none'});(FETCHES[id]||function(){})()}
+function fill(id,h){var e=qs('ct-'+id);if(e)e.innerHTML=h}
+var L=new Set();
+async function fa(){try{var r=await fetch('/api/agents'),d=await r.json(),on=d.filter(function(a){return a.status==='online'}).length,er=d.reduce(function(s,a){return s+(a.errors||0)},0);
+var h=''+on+'/'+d.length+'
Agents Online
';
+for(var i=0;i
'+esc(a.display_name||a.name)+(pb?' '+esc(a.platform)+'':'')+'
'+esc(a.jid||'')+(a.host?' · '+esc(a.host):'')+'
'+(a.message_count_5min||0)+'
msg/5m
';
+if(a.services){for(var k=0;k
'+esc(s.type)+(s.port?':'+s.port+'':'')+'';}}
+h+='Logs
Click Logs to load
';}
+h+='';qs('ct-agents').innerHTML=h;}catch(e){}}
+async function doAct(id,act){try{var r=await fetch('/api/agents/'+id+'/'+act,{method:'POST'}),d=await r.json();toast(act+(d.ok?' OK':' FAILED'),d.ok?'ok':'err');setTimeout(fa,1500)}catch(e){toast('Error: '+e.message,'err')}}
+function tgL(id){if(L.has(id))L.delete(id);else L.add(id);var lp=qs('lp-'+id);if(lp)lp.className='lp'+(L.has(id)?' open':'');if(L.has(id))fL(id)}
+async function fL(id){var el=qs('lc-'+id);if(!el||!L.has(id))return;try{var r=await fetch('/api/agents/'+id+'/logs?lines=50'),d=await r.json();el.innerHTML=(d.lines||[]).map(function(l){return''+esc(l)+'
'}).join('')||'no log data';el.scrollTop=el.scrollHeight}catch(e){el.innerHTML='Failed'}}
+async function fG(){try{var r=await fetch('/api/git'),d=await r.json();var h='';for(var i=0;i'+esc(d.log[i])+'';}if(!h)h='No commits';fill('git',h)}catch(e){}}
+async function fS(){try{var r=await fetch('/api/services'),d=await r.json(),ok=0;var h='| Service | Port | Health | WD | Type |
';for(var i=0;i'+esc(s.name)+' | '+(s.port||'-')+' | '+(a?'OK':'DOWN')+' | '+(s.watchdog?'Y':'N')+' | '+esc(s.type)+' | ';}h+='
';fill('svc',h)}catch(e){}}
+async function fM(){try{var r=await fetch('/api/monitor'),d=await r.json();var h='';if(d.tier1)h+='T1: '+d.tier1.summary.ok+'/'+d.tier1.summary.total+' ok
';if(d.tier2)h+='T2: '+d.tier2.summary.ok+'/'+d.tier2.summary.total+' ok
';fill('mon',h)}catch(e){}}
+async function fT(){try{var r=await fetch('/api/todos'),d=await r.json();fill('todos',''+(d.todos.length)+' records')}catch(e){}}
+async function fE(){try{var r=await fetch('/api/metagrowth'),d=await r.json();fill('meta','fixes: '+(d.total_fixes||0)+' commits: '+(d.commit_count||0))}catch(e){}}
+async function fF(){try{var r=await fetch('/api/expected'),d=await r.json();var ok=0;for(var i=0;i'+n+' tasks':'No tasks')}catch(e){}}
+async function fI(){try{var r=await fetch('/api/platform'),d=await r.json();var h='Platform
';for(var i=0;i'+esc(d[i].name)+'
';}h+='
';try{var r2=await fetch('/api/ejabberd'),e=await r2.json();var ea=e.alive||e.xmpp_bot_connected;h+='';}catch(e2){}fill('infra',h)}catch(e){}}
+var FETCHES={agents:fa,git:fG,svc:fS,mon:fM,todos:fT,meta:fE,exp:fF,kanban:fK,infra:fI,principles:function(){return(FETCHES[as]||function(){})()}};
+async function loadAll(){var d=new Date();qs('subtitle').textContent=d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate()+' '+d.getHours()+':'+d.getMinutes();await Promise.all(Object.keys(FETCHES).map(function(k){return FETCHES[k]()}))}
+init();loadAll();setInterval(loadAll,10000);setInterval(fK,15000);
+