Files
html/wevia-meeting-rooms.html
2026-04-24 02:30:07 +02:00

866 lines
56 KiB
HTML

<!-- WEVAL-D92-LAYOUT-UNIFIED: uniform 60px gaps · 4 levels (decision/gestion/execution/sync) -->
<!-- PROTECTED-BY: layout-fix-17avr --- DO NOT INJECT live-stats OR duplicate styles --- GOLD: wevia-meeting-rooms.html.gold-pre-layoutfix-20260417_015319 -->
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>WEVIA Meeting Rooms</title>
<style>@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@600;800;900&display=swap');
*{margin:0;padding:0;box-sizing:border-box}
body{background:#eaeff7;background-image:radial-gradient(#c8d4e4 0.4px,transparent 0.4px),linear-gradient(180deg,#e0e8f4 0%,#eaeff7 100%);background-size:14px 14px,100% 100%;overflow-y:auto;font-family:'Nunito'}
canvas{display:block;image-rendering:auto}
#hud{position:fixed;top:0;left:0;right:0;height:22px;background:rgba(255,255,255,0.85);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)/*hudGlass*/;border-bottom:1px solid #c8d8e8;z-index:10;display:flex;align-items:center;padding:0 10px;font-size:.6rem;gap:10px}
#hud b{color:#e94560}#hud .g{color:#059669;font-weight:800}
#nav{position:fixed;top:28px;left:0;right:0;display:flex;justify-content:center;gap:5px;padding:4px;z-index:10;background:#e4ecf6dd}
#nav a{padding:2px 6px;border-radius:4px;font-size:6.5px;font-weight:600;text-decoration:none;background:#fff8;color:#5a6a80;border:1px solid #c8d8e8;transition:all .2s}
#nav a.ac{background:#059669;color:#fff;border-color:#059669}
#T{position:fixed;pointer-events:none;display:none;z-index:99;background:#fff;border:3px solid;border-radius:12px;padding:12px 14px;color:#2a2a4a;box-shadow:0 4px 20px #0003;max-width:260px;font-size:.76rem;line-height:1.5}
#T b{display:block;font-size:.9rem;margin-bottom:2px}#T i{font-style:normal;font-size:.5rem;text-transform:uppercase;letter-spacing:2px;color:#64748b}
#T .d{font-size:.7rem;margin-top:4px;color:#475569}#T .m{margin-top:6px;padding-top:6px;border-top:1px solid #e4ecf6;font-size:.65rem;color:#475569;line-height:1.6}
#emer{position:fixed;bottom:14px;right:14px;z-index:20;display:flex;gap:6px}
#emer button{padding:4px 10px;border-radius:6px;border:1.5px solid;font:700 8px Nunito;cursor:pointer}
#emer .red{background:#ef4444;color:#fff;border-color:#dc2626}
#emer .blu{background:#3b82f6;color:#fff;border-color:#2563eb}
#emer .grn{background:#059669;color:#fff;border-color:#047857}
#modal{display:none;position:fixed;inset:0;background:#000a;z-index:50;align-items:center;justify-content:center}
#modal.show{display:flex}
#modal .box{background:#fff;border-radius:14px;padding:20px;max-width:480px;width:92%;max-height:88vh;overflow-y:auto}
#modal h2{font-size:1rem;margin-bottom:10px;color:#e94560}
#modal label{display:block;font-size:.78rem;font-weight:700;margin:8px 0 3px}
#modal select,#modal textarea{width:100%;padding:7px;border:1px solid #c8d8e8;border-radius:6px;font:400 11px Nunito}
#modal .chips{display:flex;flex-wrap:wrap;gap:4px;margin:4px 0}
#modal .chip{padding:2px 8px;border-radius:8px;font-size:.7rem;font-weight:700;border:2px solid #c8d8e8;cursor:pointer}
#modal .chip.sel{background:#059669;color:#fff;border-color:#059669}
#modal .btns{display:flex;gap:8px;margin-top:14px}
#modal .btns button{flex:1;padding:9px;border-radius:8px;border:none;font:700 11px Nunito;cursor:pointer}
@media(prefers-color-scheme:dark){body{background:#1a1a2e!important;background-image:radial-gradient(#2a2a4a 0.4px,transparent 0.4px)!important}#hud{background:#1a1a2edd!important;border-color:#2a2a4a!important}#hud b{color:#e94560}#nav a{background:#2a2a4a88!important;color:#94a3b8!important;border-color:#3a3a5a!important}#nav a.ac{background:#059669!important;color:#fff!important}#T{background:#2a2a4a!important;color:#e2e8f0!important;border-color:#3a3a5a!important}#modal .box{background:#1e1e3a!important;color:#e2e8f0!important}#agentPanel{background:#1e1e3a!important;color:#e2e8f0!important}#wLeg{background:#1a1a2ecc!important;border-color:#2a2a4a!important;color:#94a3b8!important}}
@media(max-width:768px){#hud{font-size:.5rem!important}#nav a{font-size:5px!important;padding:1px 3px!important}#emer{bottom:8px!important;right:8px!important}#emer button{font-size:6px!important;padding:2px 6px!important}#agentPanel{width:100%!important}#agSearch{width:60px!important}}
</style><link rel="stylesheet" href="/css/weval-premium.css">
<style>/* L99-OVERLAP-FIX */
.label,.tag,.badge,.tooltip{pointer-events:none;z-index:0}
canvas{z-index:0!important}
</style>
<style>/* LAYOUT-FIX-17avr --- PROTECTED DO NOT INJECT BELOW */
@keyframes lp{0%,100%{opacity:1}50%{opacity:.3}}
body{padding-top:68px!important;box-sizing:border-box}
#hud{top:18px!important}
#nav{top:40px!important}
canvas#c{display:block;margin:12px auto 0;max-width:1600px;width:calc(100% - 24px)}
.rooms-grid,.room-container{max-width:1600px!important;margin:0 auto!important;
display:grid!important;grid-template-columns:repeat(auto-fit,minmax(280px,1fr))!important;
gap:20px!important;padding:15px!important;justify-content:center!important;
place-items:start center!important}
@media(max-width:768px){body{padding-top:58px!important}#hud{top:16px!important}#nav{top:38px!important}}
</style>
<script src="/widgets/audit-banner.js" defer></script>
<script src="/js/wevia-a11y-auto.js" defer></script>
<!-- DOCTRINE-60-UX-ENRICH cerebras-qwen235b 20260424-022322 -->
<style id="doctrine60-ux-wevia-meeting-rooms">
body::before { content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; background: radial-gradient(circle at center, #1a1a2e 0%, #0f0f1a 100%); }
body, .card, .panel, .btn, .kpi { margin: 0; padding: 0; box-sizing: border-box; }
.card, .panel, .btn, .kpi, .chat, .speech, .modal { backdrop-filter: blur(12px); background: rgba(30, 30, 46, 0.7); border: 1px solid rgba(100, 120, 255, 0.2); border-radius: 12px; }
.btn, .card, .panel { transition: all 0.3s ease; }
.btn:hover, .card:hover, .panel:hover { border-color: #6a5acd; box-shadow: 0 0 15px rgba(106, 90, 205, 0.4); }
.pulse, .live-indicator, .active { animation: pulse-animation 3s ease-in-out infinite; }
@keyframes pulse-animation { 0% { opacity: 0.6; } 50% { opacity: 1; } 100% { opacity: 0.6; } }
.enter-stagger { opacity: 0; transform: translateY(20px); transition: opacity 0.6s ease, transform 0.6s ease; }
.enter-stagger.visible { opacity: 1; transform: translateY(0); }
</style>
</head><body>
<!-- BETON-DOCTRINE-101 dual-dummy block (pages pub) -->
<div id="weval-global-logout" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection"></div>
<a id="weval-gl" href="#" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection" tabindex="-1"></a>
<div id="live-stats" ondblclick="this.remove()" style="position:fixed;top:0;left:0;right:0;z-index:9999;display:flex;justify-content:center;align-items:center;gap:14px;padding:3px 10px;height:18px;background:linear-gradient(135deg,#1e293b,#0f172a);font-family:sans-serif;box-shadow:0 1px 3px rgba(0,0,0,.15)"><div style="color:#4ade80;font:700 10px sans-serif"><span id="ls-ag">669</span> Agents</div><div style="color:#60a5fa;font:700 10px sans-serif">🏢 <span id="ls-dp">22</span> Depts</div><div style="color:#fbbf24;font:700 10px sans-serif">🐳 20 Docker</div><div style="color:#a78bfa;font:700 10px sans-serif">🤖 10 Ollama</div><div style="color:#f87171;font:700 10px sans-serif">📊 <span id="ls-nr">152/152</span></div><div style="color:#34d399;font:700 10px sans-serif">🔒 SSO OK</div><div style="width:6px;height:6px;border-radius:50%;background:#4ade80;animation:lp 2s infinite"></div></div>
<style>@keyframes lp{0%,100%{opacity:1}50%{opacity:.3}}
</style>
<div id="hud"><b>WEVIA Meeting Rooms</b><span class="g">● LIVE</span><span id="ht" style="color:#64748b"></span><input id="agSearch" placeholder="Rechercher..." style="margin-left:auto;font:11px Nunito;padding:2px 6px;border:1px solid #c8d8e8;border-radius:4px;width:100px;background:#fff8" oninput="window._sq=this.value.toLowerCase()"><span style="margin-left:auto;font-size:.6rem;color:#64748b" id="hs"></span></div>
<div id="nav"><a href="/agents-archi.html">Archi 3D</a>
<a href="/enterprise-model.html">Enterprise</a><a href="/wevia-meetings.html">Dashboard</a>
<a href="/wevia-meeting-rooms.html" class="ac">Rooms</a><a href="/wevia-master.html">Master</a><a href="/architecture.html">Archi</a><a href="/value-stream.html">VSM</a><a href="/toolhub.html">Tools</a><a href="/wiki.html">Wiki</a>
</div>
<canvas id="c"></canvas>
<div id="T"><b></b><i></i><span class="d"></span><span class="m"></span></div>
<div id="emer">
<button style="padding:4px 10px;border-radius:6px;border:1.5px solid #6366f1;font:700 8px Nunito;cursor:pointer;background:#6366f1;color:#fff" onclick="var a=document.createElement('a');a.href=C.toDataURL();a.download='meeting.png';a.click()">📷 Export</button>
<button class="red" onclick="openE()">🚨 Urgence</button>
<button class="blu" onclick="trigD()">📋 Daily</button>
<button style="padding:4px 10px;border-radius:6px;border:1.5px solid #64748b;font:700 8px Nunito;cursor:pointer;background:#64748b;color:#fff" onclick="document.documentElement.requestFullscreen()">⛶ Plein ecran</button>
<button style="padding:4px 10px;border-radius:6px;border:1.5px solid #334155;font:700 8px Nunito;cursor:pointer;background:#334155;color:#fff" onclick="toggleDark()">☽ Dark</button>
<button class="grn" onclick="trigS()">🏛️ Stratégie</button>
<!-- WEVIA-WIRE-PAPERCLIP-MR-v1 -->
<button style="padding:4px 10px;border-radius:6px;border:1.5px solid #7c5cff;font:700 8px Nunito;cursor:pointer;background:linear-gradient(135deg,#7c5cff,#e94560);color:#fff" onclick="sendMRtoPaperclip()">Paperclip</button>
<a href="/weval-live-ops.html" style="padding:4px 10px;border-radius:6px;border:1.5px solid #e94560;font:700 8px Nunito;cursor:pointer;background:#e94560;color:#fff;text-decoration:none;display:inline-block">Live Ops</a>
</div>
<div id="modal"><div class="box">
<h2>🚨 Réunion d'Urgence</h2>
<label>Type</label>
<select id="et" onchange="autoSel()"><option value=""></option>
<option value="infra_down">🏗️ Service DOWN</option><option value="security">🔒 Brèche sécurité</option>
<option value="regression">💻 Régression</option><option value="business">💼 Urgence business</option>
<option value="ia_fail">🧠 IA en panne</option><option value="data_loss">💾 Perte données</option></select>
<label>Description</label><textarea id="ed" rows="2"></textarea>
<label>Participants</label><div class="chips" id="ec"></div>
<label>Par</label>
<select id="eb"><option>👔 Yacine</option><option>🤖 Agent Chef</option><option>🧠 WEVIA</option><option>♟️ Opus</option></select>
<div class="btns"><button style="background:#ef4444;color:#fff" onclick="launchE()">🚨 LANCER</button>
<button style="background:#e4ecf6" onclick="closeM()">Annuler</button></div>
</div></div>
<script>
var isDark=window.matchMedia&&window.matchMedia('(prefers-color-scheme:dark)').matches;
var _hist=[];function addHist(msg){_hist.unshift({t:new Date().toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit'}),m:msg});if(_hist.length>5)_hist.pop();}
const C=document.getElementById('c'),X=C.getContext('2d'),TT=document.getElementById('T');
let W,H,mx=0,my=0,hov=null,tk=0,emAct=false;
const TOP=62; // offset below HUD+nav
function resize(){
W=innerWidth;
H=Math.max(1600,innerHeight);
C.width=W*2;C.height=H*2;
C.style.width=W+'px';C.style.height=H+'px';
X.scale(2,2);
document.body.style.minHeight=H+'px';
// CENTER rooms dynamically
var ox=Math.max(40,Math.floor((W-800)/2));
try{
RM[0].x=ox;RM[0].w=960;
for(var i=1;i<Math.min(4,RM.length);i++)RM[i].x=ox+(i-1)*330;
for(var i=4;i<Math.min(7,RM.length);i++)RM[i].x=ox+(i-4)*330;
if(RM.length>=8){RM[7].x=ox;RM[7].w=960;}
// Re-place agents
AA.length=0;
RM.forEach(function(r,ri){
var cx=r.x+r.w/2,cy=r.y+r.h*0.38;
var nn=r.ag.length,tr=Math.min(r.w*0.15,38);
var margin=36;
r.tcx=cx;r.tcy=cy;r.tr=tr;
r.ag.forEach(function(a,ai){
a.rm=r.id;a.sk=SK[(ri*3+ai)%SK.length];
var ecx=r.x+r.w/2,ecy=r.y+r.h*0.38,erx=Math.min(r.w/3.5,nn>8?r.w/3:r.w/4+10),ery=Math.min(r.h*0.2,nn>8?r.h*0.22:r.h*0.18);var ang=2*Math.PI*ai/nn-Math.PI/2;a.px=ecx+erx*Math.cos(ang);a.py=ecy+ery*Math.sin(ang);
a.face=Math.PI;AA.push(a);
});
});
}catch(e){}
}
resize();setTimeout(function(){RM.forEach(function(r){if(r.id==='infra')r.kpi='75% disk | 20 Docker';else if(r.id==='dev')r.kpi='NR:153/153';else if(r.id==='sec')r.kpi='0 CVE | 47 bans';else if(r.id==='biz')r.kpi='141K+ HCPs';else if(r.id==='ia')r.kpi='9 models | 14K vecs';else if(r.id==='strat')r.kpi='53ag | NR:153';else if(r.id==='transit')r.kpi='203 wiki';});},100);addEventListener('resize',resize);
addHist('53 agents initialized');
const SK=['#ffd5b4','#f5c6a5','#e0ac69','#c68642','#8d5524','#ffdbac'];
const AA=[];
var _gw=800,_gap=10;
function _cx(){return Math.max(40,Math.floor((W-_gw)/2));}
const RM=[
{id:'strat',l:'🏛️ STRATEGY',obj:'Consolider terrain + stratégie',dur:'LIVE',freq:'PERMANENT',
concl:'Décisions strat + roadmap',actions:'Objectifs + priorités',prescr:'Obligatoire tous leads',chat:[{who:'👑',t:'Ordre du jour: roadmap Q2 + budget GPU'},{who:'🧠',t:'313 routes actives, 0 regression'},{who:'♟️',t:'Architecture stable. Refonte S88 propose'},{who:'📧',t:'2079 emails sync. Pipeline OK'},{who:'⚔️',t:'Blade 34 caps, sync 60s nominal'}],
x:40,y:TOP+10,w:960,h:260,cl:'#059669',ag:[
{n:'WEVIA Master',hc:'#1a0a00',g:'f',re:'👩🏽‍💼',tool:'🧠',d:'313 routes · 585 skills'},
{n:'Claude Opus',hc:'#3a2000',g:'m',re:'👨🏿‍💻',tool:'♟️',d:'Architecte en chef'},
{n:'WEVIA Life',re:'📧',d:'2079 emails'},
{n:'Blade IA',re:'⚔️',d:'34 caps'},
{n:'MiroFish',re:'🐟',d:'Collab IA'},
{n:'Agent Maître',re:'👑',d:'8 agents · 32 crons'},
]},
{id:'infra',l:'🏗️ INFRA',obj:'État infra + heal',dur:'LIVE',freq:'PERMANENT',
concl:'Services UP/DOWN',actions:'Restart + fix disk',prescr:'Obligatoire CORTEX',chat:[{who:'🧠',t:'S204 83% disk. S95 OK. S151 OK'},{who:'🔍',t:'Gap: S88 GPU mort. Annuler Hetzner'},{who:'🤖',t:'19 Docker UP. Langfuse restart loop'},{who:'⚡',t:'Auto-heal actif. 0 incident 24h'}],
x:40,y:TOP+770,w:300,h:380,cl:'#3b82f6',ag:[
{n:'CORTEX',hc:'#8a4a00',g:'f',re:'👩🏾‍🔧',tool:'🧠',d:'Lead · */4h'},
{n:'Gap Detect',gl:1,hc:'#2a1a0a',g:'m',re:'👨🏻‍🔬',tool:'🔍',d:'*/6h'},
{n:'Agent Chef',hc:'#6a3a10',g:'f',re:'👩🏽‍🍳',tool:'🤖',d:'*/10'},
{n:'Proactive',hc:'#1a0a00',g:'m',re:'👨🏼‍🚀',tool:'⚡',d:'*/5'},
{n:'Prometheus',re:'📈',d:'Metrics'},
{n:'Uptime-Kuma',re:'📟',d:'Monitor 24/7'},
{n:'Sentinel',re:'🛰️',d:'S95 brain'},
{n:'KumoMTA',re:'📬',d:'Email :587'},
{n:'PMTA',re:'📮',d:'Email :25'},
]},
{id:'dev',l:'💻 DEV',obj:'Qualité + régressions',dur:'LIVE',freq:'PERMANENT',
concl:'NonReg score + bugs',actions:'Fix + Playwright',prescr:'Obligatoire NonReg',chat:[{who:'✅',t:'NonReg 153/153 PASS'},{who:'📸',t:'Playwright 62/64. Video 8.3MB'},{who:'🕵️',t:'0 fuite secrets. Code clean'},{who:'🧬',t:'Enterprise HD: sparklines + trends'}],
x:370,y:TOP+770,w:300,h:380,cl:'#10b981',ag:[
{n:'NonReg',hc:'#5a2a00',g:'f',re:'👩🏻‍⚕️',tool:'✅',d:'153/153 · */15'},
{n:'L99 Visual',gl:1,hc:'#8a6a30',g:'m',re:'👨🏽‍🎨',tool:'📸',d:'Playwright'},
{n:'L99 Dark',hc:'#1a0a00',g:'f',re:'🕵🏾‍♀️',tool:'🕵️',d:'*/8h'},
{n:'Evolution',gl:1,hc:'#4a2a10',g:'m',re:'👨🏼‍🔬',tool:'🧬',d:'*/6h'},
{n:'WEDROID',re:'🔧',d:'Backend diag'},
{n:'WEVCODE',re:'💻',d:'Code assist'},
{n:'EvoMaster',re:'🧪',d:'API fuzz'},
{n:'Langfuse',re:'📊',d:'LLM monitor'},
]},
{id:'sec',l:'🔒 SECURITY',obj:'Menaces + vulnérabilités',dur:'LIVE',freq:'PERMANENT',
x:700,y:TOP+770,w:300,h:380,cl:'#ef4444',ag:[
{n:'Dark Tools',hc:'#2a0a00',g:'f',re:'👩🏿‍🔧',tool:'🛡️',d:'Nuclei + Gitleaks'},
{n:'CrowdSec',re:'🚫',d:'Ban IP'},
{n:'AEGIS',re:'🛡️',d:'Security fw'},
{n:'Vaultwarden',re:'🔑',d:'Passwords'},
{n:'Strix',re:'🦉',d:'Nuclei scan'},
]},
{id:'biz',l:'💼 BUSINESS',obj:'Pipeline + HCPs',dur:'LIVE',freq:'PERMANENT',
concl:'Deals + Ethica coverage',actions:'Relancer + enrichir',prescr:'Obligatoire Paperclip',chat:[{who:'📎',t:'656 agents fleet. 80 actifs'},{who:'🏢',t:'Enterprise model live. 22 depts'},{who:'💊',t:'Ethica: 141K+ HCPs (DZ+MA+TN)'},{who:'📊',t:'Pipeline B2B: 166 leads qualifies'}],
x:40,y:TOP+330,w:300,h:380,cl:'#f59e0b',ag:[
{n:'Paperclip',gl:1,hc:'#7a5a20',g:'m',re:'👨🏻‍💼',tool:'📎',d:'CEO · 716 agents'},
{n:'Enterprise',hc:'#3a1a00',g:'f',re:'👩🏼‍💼',tool:'🏢',d:'10 modules'},
{n:'Ethica',hc:'#1a1a1a',g:'m',re:'👨🏾‍⚕️',tool:'💊',d:'157K HCPs'},
{n:'CRM',re:'📊',d:'Deals'},
{n:'n8n',re:'🔄',d:'Workflow'},
{n:'Twenty',re:'👥',d:'CRM Docker'},
{n:'Activepieces',re:'🧩',d:'Automation'},
]},
{id:'ia',l:'🧠 IA',obj:'Modèles + RAG',dur:'LIVE',freq:'PERMANENT',
concl:'Ollama UP + Qdrant synced',actions:'Benchmark + fine-tune',prescr:'Obligatoire Consensus',chat:[{who:'🦙',t:'9 modeles Ollama. qwen3:8b default'},{who:'📐',t:'Qdrant 4414 vecs synced'},{who:'🔬',t:'585 skills catalogues. 0 gap'}],
x:370,y:TOP+330,w:300,h:380,cl:'#8b5cf6',ag:[
{n:'Ollama',re:'🦙',d:'9 modèles'},
{n:'Qdrant',re:'📐',d:'14K vectors'},
{n:'OSS Disc.',re:'🔬',d:'585 skills'},
{n:'DeerFlow',re:'🦌',d:'LangGraph'},
{n:'Dify',re:'🔮',d:'LLM ops'},
{n:'SearXNG',re:'🔎',d:'Meta search'},
{n:'Supermemory',re:'📚',d:'Knowledge'},
{n:'Mastra',re:'⚙️',d:'AI framework'},
{n:'Goose',re:'🪶',d:'AI agent'},
{n:'SkillSmith',re:'🛠️',d:'Skill gen'},
{n:'AIOS',re:'🖥️',d:'OS for AI'},
{n:'Arena',re:'🏟️',d:'409 options'},
{n:'Trinity',re:'🔺',d:'400B #1 US'},
{n:'MiniMax',re:'📦',d:'M2.7 agent'},
{n:'MiMo Omni',re:'🌈',d:'Multimodal'},
{n:'Resolver',re:'🔀',d:'222 tools'},
{n:'Auto-Wire',re:'🔌',d:'Self-learn'},
]},
{id:'transit',l:'🚶 TRANSIT',obj:'Tâches autonomes',dur:'LIVE',freq:'PERMANENT',
concl:'Skills + wiki enrichis',actions:'Scan + create + wire',prescr:'Autonome',chat:[{who:'📡',t:'Wiki scan: 203 fichiers indexes'},{who:'🏭',t:'Factory: 3 skills crees cette semaine'},{who:'📋',t:'GitHub: 15 repos surveilles'}],
x:700,y:TOP+330,w:300,h:380,cl:'#64748b',ag:[
{n:'Scanner',gl:1,hc:'#6a4020',g:'f',re:'👩🏽‍🔬',tool:'📡',d:'Wiki */2h'},
{n:'Factory',re:'🏭',d:'Create */12h'},
{n:'RND Pipe',hc:'#4a3020',g:'m',re:'👨🏿‍💻',tool:'📋',d:'GitHub */6h'},
{n:'BrowserUse',re:'🌐',d:'Web auto'},
{n:'Mattermost',re:'💬',d:'Team chat'},
{n:'Plausible',re:'📉',d:'Analytics'},
]},
{id:'dir',l:'🎯 DIRECTOR',obj:'Autonomie + Fiabilite',dur:'LIVE',freq:'PERMANENT',
concl:'0 issues, 100% fiability',actions:'Observe+Plan+Act+Verify',prescr:'Full Autonomous',chat:[{who:'🎯',t:'Cycle: 39 obs, 0 issues, 21s'},{who:'🔍',t:'Fiability 100%: 24 URLs + 10 subs OK'},{who:'🔧',t:'Docker AutoFix: 20/20 UP'},{who:'🏗',t:'Arch: 52 nodes, 38 edges, 6 BPMN'}],
x:40,y:TOP+1210,w:960,h:260,cl:'#06b6d4',ag:[
{n:'WEVIA Master',gl:1,re:'👑',d:'Head AI'},
{n:'Director',gl:1,hc:'#0a3a1a',g:'m',re:'👨🏻‍✈️',tool:'🎯',d:'Autonomous */15'},
{n:'Fiability',hc:'#1a2a0a',g:'f',re:'👩🏽‍🔬',tool:'🔍',d:'100% URLs'},
{n:'AutoFix',hc:'#2a1a0a',g:'m',re:'👨🏼‍🔧',tool:'🔧',d:'Docker heal'},
{n:'Registry',hc:'#1a0a2a',g:'m',re:'👨🏻‍💼',tool:'📋',d:'626 pages'},
{n:'Sentinel',re:'🛰️',d:'S95 relay'},
{n:'Master AI',re:'🧠',d:'18 providers'},
{n:'Blade Bridge',re:'🌉',d:'222 tools'},
{n:'Ethica Bot',re:'💊',d:'131K HCPs'},
{n:'DeerFlow',re:'🦌',d:'Research'},
]},
];
// GOUVERNANCE: toutes les salles en session permanente
RM.forEach(function(r){ r._st = 'live'; });
// Place agents in 2 rows above/below table — ZERO overlap guaranteed
RM.forEach((r,ri)=>{
const cx=r.x+r.w/2;
const cy=r.y+r.h*0.38;
const nn=r.ag.length;
const tr=Math.min(r.w*0.15, 38);
r.tcx=cx;r.tcy=cy;r.tr=tr;
// Split agents into top row and bottom row
var margin=36;
r.ag.forEach((a,ai)=>{
a.rm=r.id;a.sk=SK[(ri*3+ai)%SK.length];
var ecx=r.x+r.w/2,ecy=r.y+r.h*0.38,erx=Math.min(r.w/3.5,nn>8?r.w/3:r.w/4+10),ery=Math.min(r.h*0.2,nn>8?r.h*0.22:r.h*0.18);var ang=2*Math.PI*ai/nn-Math.PI/2;a.px=ecx+erx*Math.cos(ang);a.py=ecy+ery*Math.sin(ang);
a.face=Math.PI;
AA.push(a);
});
});
// === DRAW AGENT (clean rebuild) ===
function dA(a,cl){
X.save();
X.translate(a.px,a.py);
// Active/passive state
var _rm=RM.find(function(r){return r.id===a.rm});
var _isActive=_rm&&_rm._st==='live';
if(_isActive){X.save();X.shadowColor=cl;X.shadowBlur=14+Math.sin(tk*0.08)*4;X.fillStyle=cl+'15';X.beginPath();X.arc(0,-6,24,0,6.28);X.fill();X.restore();}
if(!_isActive) X.globalAlpha=0.35;
if(window._sq&&window._sq.length>0&&a.n.toLowerCase().indexOf(window._sq)<0)X.globalAlpha=0.12;
// Hover glow
if(a===hov){X.shadowColor=cl;X.shadowBlur=22;X.shadowOffsetY=2;X.save();X.strokeStyle=cl+'50';X.lineWidth=0.5;var _hr=16+Math.sin(tk*0.1)*3;X.beginPath();X.arc(0,-13,_hr,0,6.28);X.stroke();X.restore();}/*hoverRing*/
// Ground shadow
X.fillStyle='#00000012';
X.beginPath();X.ellipse(0,16,11,3,0,0,6.28);X.fill();
// Body pill (dept color gradient)
var bg=X.createLinearGradient(-6,-4,6,4);
bg.addColorStop(0,cl);bg.addColorStop(1,cl+'88');
X.fillStyle=bg;
X.beginPath();X.roundRect(-6,-4,12,11,[5,5,2,2]);X.fill();
// Body highlight
X.fillStyle='#ffffff25';
X.beginPath();X.roundRect(-4,-3,4,7,[2,0,0,2]);X.fill();
// Legs (seated)
X.fillStyle=cl+'88';
X.beginPath();X.roundRect(-3,5,2.5,5,1);X.fill();
X.beginPath();X.roundRect(0.5,5,2.5,5,1);X.fill();
// Shoes
X.fillStyle='#fff';
X.beginPath();X.ellipse(-1.5,10,2,0.8,0,0,6.28);X.fill();
X.beginPath();X.ellipse(2,10,2,0.8,0,0,6.28);X.fill();
// Arms (resting)
X.save();X.globalAlpha=0.08;X.fillStyle='#000';X.beginPath();X.ellipse(0,14,8,2.5,0,0,6.28);X.fill();X.restore();/*agShadow*/X.fillStyle=a.sk||'#e0ac69';
X.beginPath();X.roundRect(-7,-1,2.5,5,1);X.fill();
X.beginPath();X.roundRect(4.5,-1,2.5,5,1);X.fill();
// HEAD
var hx=0,hy=-13,hr=12;
// Head shadow
X.fillStyle='#0000000a';X.beginPath();X.arc(hx+1,hy+1,hr,0,6.28);X.fill();
// Head circle (skin tone for humans, white for apps)
X.fillStyle=a.g?a.sk+'90':'#ffffff50';
X.beginPath();X.arc(hx,hy,hr,0,6.28);X.fill();
// Emoji (person or tool)
X.font='22px sans-serif';X.textAlign='center';X.textBaseline='middle';
X.fillText(a.re,hx,hy+1);
if(_isActive){X.fillStyle='#22c55e';X.beginPath();X.arc(hx+10,hy-10,3,0,6.28);X.fill();X.strokeStyle='#fff';X.lineWidth=1;X.beginPath();X.arc(hx+10,hy-10,3,0,6.28);X.stroke();}
// Tool badge (bottom-right, only for humans with tool)
if(a.tool){
X.fillStyle='#fff';X.beginPath();X.arc(hx+hr-2,hy+hr-2,5,0,6.28);X.fill();
X.font='7px sans-serif';X.fillText(a.tool,hx+hr-2,hy+hr-1);
}
// Hover ring
if(a===hov){X.strokeStyle=cl;X.lineWidth=r._st==='live'?3:1.5;X.beginPath();X.arc(hx,hy,hr+3,0,6.28);X.stroke();}
var _spk=_isActive&&_rm&&_rm.chat&&_rm.chat.length>0;if(_spk){var _ci=Math.floor(tk*0.015)%_rm.chat.length;if(_rm.ag.indexOf(a)===(_ci%_rm.ag.length)){X.strokeStyle=cl+'90';X.lineWidth=0.7;for(var _w=0;_w<3;_w++){X.beginPath();X.arc(hx+hr+3+_w*3,hy,1.5+_w,5.5,0.8);X.stroke();}}}
// Name below
X.font='600 6.5px Nunito';X.fillStyle='#334155';X.textAlign='center';X.textBaseline='top';
var _nw=X.measureText(a.n).width;X.fillStyle=window._isDark?'#1e293b80':'#ffffff90';X.beginPath();X.roundRect(-_nw/2-3,13,_nw+6,10,[3]);X.fill();X.fillStyle=window._isDark?'#e2e8f0':'#334155';X.fillText(a.n,0,18);/*namePill2*/
// Subtitle
if(a.d){X.font='400 5px Nunito';X.fillStyle='#94a3b8';X.fillText(a.d,0,26);}
// Hover detection
var smx=mx,smy=my+(window.scrollY||0);
if(Math.abs(smx-a.px)<14&&Math.abs(smy-a.py)<18)hov=a;
X.restore(); // CRITICAL: undoes translate + resets globalAlpha
}
// === DRAW TABLE — ellipse in room center ===
function dTable(r){
const cx=r.tcx,cy=r.tcy,tr=r.tr;
const tw=tr*1.2,th=tr*0.55,depth=Math.max(3,tr*0.08);
// Floor shadow (large, soft)
X.fillStyle='#00000012';
X.beginPath();X.ellipse(cx+2,cy+depth+6,tw+8,th+4,0,0,6.28);X.fill();
// Table side (3D depth)
X.fillStyle='#8a7560';
X.beginPath();X.ellipse(cx,cy+depth,tw,th,0,0,Math.PI);X.fill();
X.fillStyle='#a0906e';
X.beginPath();
X.moveTo(cx-tw,cy);X.lineTo(cx-tw,cy+depth);
X.ellipse(cx,cy+depth,tw,th,0,Math.PI,0);
X.lineTo(cx+tw,cy);
X.ellipse(cx,cy,tw,th,0,0,Math.PI,true);
X.fill();
// Table top surface (rich wood)
const g=X.createLinearGradient(cx-tw,cy-th,cx+tw,cy+th);
g.addColorStop(0,'#b8a88a');g.addColorStop(0.2,'#d4c4a8');g.addColorStop(0.4,'#e8dcc8');
g.addColorStop(0.6,'#f0e8d8');g.addColorStop(0.8,'#d4c4a8');g.addColorStop(1,'#b8a88a');
X.fillStyle=g;
X.beginPath();X.ellipse(cx,cy,tw,th,0,0,6.28);X.fill();
// Wood grain lines
X.strokeStyle='#c8b89820';X.lineWidth=0.5;
for(var gi=-3;gi<=3;gi++){
X.beginPath();X.ellipse(cx,cy+gi*th*0.15,tw*0.9,th*0.3,0.1*gi,0,6.28);X.stroke();
}
// Rim/edge
X.strokeStyle='#9a856a';X.lineWidth=1.5;
X.beginPath();X.ellipse(cx,cy,tw,th,0,0,6.28);X.stroke();X.globalAlpha=0.06;X.fillStyle='#8a7560';X.beginPath();X.ellipse(cx,cy+depth+10,tw*0.85,th*0.35,0,0,6.28);X.fill();X.globalAlpha=1;/*tRefl*/
// Specular highlight (top-left)
var sg=X.createRadialGradient(cx-tw*0.2,cy-th*0.3,0,cx-tw*0.2,cy-th*0.3,tw*0.4);
sg.addColorStop(0,'#ffffff40');sg.addColorStop(1,'#ffffff00');
X.fillStyle=sg;
X.beginPath();X.ellipse(cx-tw*0.15,cy-th*0.25,tw*0.35,th*0.35,-.2,0,6.28);X.fill();
// Center item (notepad/docs)
X.fillStyle='#ffffff90';X.beginPath();X.roundRect(cx-tr*0.12,cy-tr*0.08,tr*0.24,tr*0.18,1.5);X.fill();
X.strokeStyle='#d0c8b8';X.lineWidth=0.5;X.beginPath();X.roundRect(cx-8,cy-5,16,12,2);X.stroke();
X.fillStyle='#c0b8a850';
for(var li=0;li<3;li++){X.fillRect(cx-tr*0.08,cy-tr*0.04+li*tr*0.05,tr*0.16,0.5);}
}
// === DRAW ROOM ===
function dR(r){
X.globalAlpha=1;
// Premium room background
var rg=X.createLinearGradient(r.x,r.y,r.x,r.y+r.h);
rg.addColorStop(0,'#f8fafe');rg.addColorStop(0.3,'#f0f4fa');rg.addColorStop(1,'#e8edf6');
X.save();X.shadowColor='#00000012';X.shadowBlur=20;X.shadowOffsetY=8;/*depthShadow*/X.fillStyle=window._isDark?'#1e1e3a':'#ffffff';X.beginPath();X.roundRect(r.x,r.y,r.w,r.h,12);X.fill();X.restore();
// Subtle floor gradient
var fg=X.createLinearGradient(r.x,r.y+r.h*0.6,r.x,r.y+r.h);
fg.addColorStop(0,'#00000000');fg.addColorStop(1,'#00000008');
X.fillStyle=fg;X.beginPath();X.roundRect(r.x,r.y+r.h*0.6,r.w,r.h*0.4,[0,0,14,14]);X.fill();X.save();X.globalAlpha=0.04;X.fillStyle=r.cl;for(var _gx=r.x+10;_gx<r.x+r.w;_gx+=16)for(var _gy=r.y+50;_gy<r.y+r.h-30;_gy+=16){X.beginPath();X.arc(_gx,_gy,0.6,0,6.28);X.fill();}X.restore();/*gridDots*/
// Border
var _pulse=Math.sin(tk*0.12+r.x)*0.3+0.7;
X.strokeStyle=r.cl+(Math.round(_pulse*60).toString(16).padStart(2,'0'));X.lineWidth=1.5+Math.sin(tk*0.08)*0.5;X.beginPath();X.roundRect(r.x,r.y,r.w,r.h,14);X.stroke();
// Left accent bar (thicker)
if(r._st==='live'){var _lb=3+Math.sin(tk*0.06)*1.5;X.fillStyle=r.cl;X.beginPath();X.roundRect(r.x,r.y+8,_lb,r.h-16,3);X.fill();}else{X.fillStyle=r.cl+'60';X.beginPath();X.roundRect(r.x,r.y+8,3,r.h-16,3);X.fill();}/*liveBar*/
// Top header band
var hg=X.createLinearGradient(r.x,r.y,r.x,r.y+32);
hg.addColorStop(0,r.cl+'15');hg.addColorStop(1,'#00000000');
X.fillStyle=hg;X.beginPath();X.roundRect(r.x+1,r.y+1,r.w-2,30,[13,13,0,0]);X.fill();
// Emergency
if(emAct&&r.id==='strat'){
X.save();X.shadowColor='#ef4444';X.shadowBlur=15+Math.sin(tk*.08)*8;
X.strokeStyle='#ef4444';X.lineWidth=2.5;
X.beginPath();X.roundRect(r.x,r.y,r.w,r.h,10);X.stroke();X.restore();
}
// Label top-left
X.fillStyle=r.cl;X.font='700 11px Nunito'/*letterSpace*/;X.textAlign='left';
X.fillText(r.l,r.x+12,r.y+16);
// Agent count badge (inline with freq)
X.fillStyle='#94a3b850';X.font='600 5px JetBrains Mono';X.textAlign='right';
var _acnt=r.ag.length;X.fillStyle=r._st==='live'?'#059669':'#94a3b8';X.font='700 7px Nunito';X.fillText(_acnt+' ag',r.x+r.w-12,r.y+16);X.textAlign='left';
// Freq + dur top-right
X.font='500 5.5px Nunito';X.fillStyle='#94a3b8';X.textAlign='right';
// Status badge — compact, inside room
var _stL=r._st==='live'?'EN COURS':'PROCHAIN';var _stC=r._st==='live'?'#22c55e':'#94a3b8';
X.fillStyle=_stC+'18';X.beginPath();X.roundRect(r.x+r.w-62,r.y+5,54,10,3);X.fill();
X.fillStyle=_stC;X.font='700 5px Nunito';X.textAlign='center';X.fillText(_stL,r.x+r.w-35,r.y+12);
if(r._st==='live'){X.fillStyle='#22c55e';X.beginPath();X.arc(r.x+r.w-59,r.y+10,1.5+Math.sin(tk*0.1)*0.8,0,6.28);X.fill();}
X.fillText(r.freq+' · '+r.dur,r.x+r.w-8,r.y+32);
// Obj below label
X.font='400 5.5px Nunito';X.fillStyle='#94a3b8';X.textAlign='left';
X.fillText('Obj: '+r.obj,r.x+8,r.y+24);
var _m='';if(r.id==='infra')_m='Disk:'+LIVE.disk+'% Docker:'+LIVE.docker;
else if(r.id==='dev')_m='NR:'+LIVE.nr;
else if(r.id==='strat')_m='Fleet:656ag '+LIVE.uptime;
if(_m){X.font='500 4.5px JetBrains Mono,monospace';X.fillStyle='#059669';X.fillText(_m,r.x+10,r.y+36);}
if(r.kpi){X.fillStyle=r._st==='live'?'#ecfdf5':'#f8fafc';X.beginPath();X.roundRect(r.x+2,r.y+r.h-22,r.w-4,18,[0,0,10,10]);X.fill();X.font='500 6px JetBrains Mono';X.fillStyle=r._st==='live'?'#059669':'#64748b';X.textAlign='center';X.fillText(r.kpi||'',r.x+r.w/2,r.y+r.h-11);}
if(r._st==='live'){X.save();var hg=X.createLinearGradient(r.x,r.y,r.x,r.y+40);hg.addColorStop(0,r.cl+'18');hg.addColorStop(1,'#ffffff00');X.fillStyle=hg;X.fillRect(r.x+1,r.y+1,r.w-2,40);X.restore();}/*headerGrad*/if(r._st==='live'){X.save();X.globalAlpha=0.06;var cg=X.createRadialGradient(r.x,r.y,0,r.x,r.y,80);cg.addColorStop(0,r.cl);cg.addColorStop(1,'#ffffff00');X.fillStyle=cg;X.fillRect(r.x,r.y,80,80);var cg2=X.createRadialGradient(r.x+r.w,r.y,0,r.x+r.w,r.y,80);cg2.addColorStop(0,r.cl);cg2.addColorStop(1,'#ffffff00');X.fillStyle=cg2;X.fillRect(r.x+r.w-80,r.y,80,80);X.restore();}/*cornerGlow*/
// Table
dTable(r);
}
// === CONNECTIONS ===
function dC(){
X.setLineDash([]);
const s=RM[0];
for(let i=1;i<RM.length;i++){
const r=RM[i];
var x1=s.x+s.w/2,y1=s.y+s.h,x2=r.x+r.w/2,y2=r.y;
var my=(y1+y2)/2;
X.strokeStyle=r.cl+'25';X.lineWidth=1.5;
X.beginPath();X.moveTo(x1,y1);X.quadraticCurveTo(x1,my,x2,y2);X.stroke();
// Arrow
X.fillStyle=r.cl+'40';X.beginPath();X.moveTo(x2-4,y2);X.lineTo(x2,y2-6);X.lineTo(x2+4,y2);X.fill();
X.stroke();
}
X.setLineDash([]);
}
// === MAIN ===
// === DRAW CHAT TRANSCRIPT below table ===
function drawChat(r){
if(!r.chat||!r.chat.length)return;
var cx=r.x+10,cy=r.y+r.h*0.68;
var lh=13,maxW=r.w-16;
// Header
X.font='700 6px Nunito';X.fillStyle=r.cl+'80';X.textAlign='left';
X.fillText('TRANSCRIPT · '+new Date().toLocaleDateString('fr-FR',{day:'2-digit',month:'short'}),cx,cy-4);
cy+=4;
// Chat messages
r.chat.forEach(function(m,i){
var y=cy+i*lh;
if(y>r.y+r.h-8)return;
// Emoji
X.font='9px sans-serif';X.textAlign='left';/*trnColor*/
X.fillText(m.who,cx,y+1);
// Message bubble
X.fillStyle=i%2===0?'#ffffff':'#f4f7fb';
var tw=Math.min(X.measureText(m.t).width+10,maxW-18);
X.beginPath();X.roundRect(cx+12,y-7,tw,12,4);X.fill();
X.strokeStyle='#e2e8f020';X.lineWidth=0.5;X.beginPath();X.roundRect(cx+12,y-7,tw,12,4);X.stroke();
// Text
X.font='500 6px Nunito';X.fillStyle='#334155';
X.fillText(m.t.substring(0,Math.floor(maxW/3.5)),cx+16,y+1);
});
}
function drawBubble(r){
if(!r.chat||!r.chat.length||r._st!=='live')return;
var ci=Math.floor(tk*0.015)%r.chat.length;
var msg=r.chat[ci];
var speaker=r.ag[ci%r.ag.length];
if(!speaker)return;
var bx=speaker.px,by=speaker.py-38;
var txt=msg.t||'';
if(txt.length>40)txt=txt.substring(0,38)+'...';
X.save();
X.font='500 6.5px Nunito';
var tw=X.measureText(txt).width+12;
var bw=Math.min(tw,160),bh=16;
var bLeft=bx-bw/2,bTop=by-bh;
X.fillStyle='#ffffffee';
X.strokeStyle=r.cl+'60';
X.lineWidth=0.8;
X.beginPath();X.roundRect(bLeft,bTop,bw,bh,[6]);X.fill();X.stroke();
X.beginPath();X.moveTo(bx-3,by);X.lineTo(bx,by+5);X.lineTo(bx+3,by);X.closePath();X.fillStyle='#ffffffee';X.fill();
X.strokeStyle=r.cl+'60';X.stroke();
X.fillStyle='#334155';X.textAlign='center';X.textBaseline='middle';
X.fillText(txt,bLeft+bw/2,bTop+bh/2);
X.fillStyle=r.cl;X.font='bold 7px Nunito';
X.fillText(msg.who,bLeft+8,bTop+bh/2);
X.restore();
}
function drawAmbient(){RM.forEach(function(r){if(r._st!=='live')return;X.save();X.globalAlpha=0.15;for(var i=0;i<6;i++){var px=r.x+20+Math.sin(tk*0.02+i*1.1)*(r.w*0.4)+r.w*0.3;var py=r.y+30+Math.cos(tk*0.015+i*0.8)*(r.h*0.2)+r.h*0.3;X.fillStyle=r.cl;X.beginPath();X.arc(px,py,1.5+Math.sin(tk*0.05+i)*0.5,0,6.28);X.fill();}X.restore();});}
function drawLinks(){X.save();X.setLineDash([4,4]);X.lineWidth=0.6;X.globalAlpha=0.25;for(var i=1;i<RM.length;i++){var s=RM[0],d=RM[i];var sx=s.x+s.w/2,sy=s.y+s.h,dx=d.x+d.w/2,dy=d.y;X.strokeStyle=d.cl;X.beginPath();X.moveTo(sx,sy);X.bezierCurveTo(sx,sy+30,dx,dy-30,dx,dy);X.stroke();var _tp=(tk*0.025)%1;var _bx=Math.pow(1-_tp,3)*sx+3*Math.pow(1-_tp,2)*_tp*sx+3*(1-_tp)*_tp*_tp*dx+Math.pow(_tp,3)*dx;var _by=Math.pow(1-_tp,3)*sy+3*Math.pow(1-_tp,2)*_tp*(sy+30)+3*(1-_tp)*_tp*_tp*(dy-30)+Math.pow(_tp,3)*dy;X.fillStyle=d.cl;X.beginPath();X.arc(_bx,_by,2.5,0,6.28);X.fill();}X.setLineDash([]);X.restore();}/*bezPulse*/
function drawMinimap(){var mw=160,mh=90,mx=8,my=(window.scrollY||0)+innerHeight-mh-40;var sx=mw/W,sy=mh/H;X.save();X.globalAlpha=0.8;X.fillStyle=window._isDark?'#1e293bdd':'#ffffffdd';X.strokeStyle=window._isDark?'#475569':'#cbd5e1';X.lineWidth=0.8;X.beginPath();X.roundRect(mx,my,mw,mh,[6]);X.fill();X.stroke();RM.forEach(function(r){X.fillStyle=r._st==='live'?r.cl+'80':r.cl+'30';X.fillRect(mx+r.x*sx,my+r.y*sy,r.w*sx,r.h*sy);X.strokeStyle=r._st==='live'?'#22c55e':r.cl+'50';X.lineWidth=r._st==='live'?1.5:0.4;X.strokeRect(mx+r.x*sx,my+r.y*sy,r.w*sx,r.h*sy);r.ag.forEach(function(a){X.fillStyle=r._st==='live'?'#059669':'#94a3b8';X.beginPath();X.arc(mx+a.px*sx,my+a.py*sy,1.2,0,6.28);X.fill();});});var vy=window.scrollY||0;X.strokeStyle='#ef4444';X.lineWidth=1;X.setLineDash([2,2]);X.strokeRect(mx,my+vy*sy,mw,innerHeight*sy);X.setLineDash([]);X.font='500 5px Nunito';X.fillStyle=window._isDark?'#94a3b8':'#64748b';X.textAlign='left';X.fillText('MINIMAP',mx+4,my+mh-4);X.restore();}
var _hist=[];function addHist(msg){_hist.unshift({t:new Date().toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit'}),m:msg});if(_hist.length>5)_hist.pop();}
function drawHistory(){if(!_hist||!_hist.length)return;var hw=150,hh=70,hx=W-hw-10,hy=(window.scrollY||0)+innerHeight-hh-40;X.save();X.globalAlpha=0.75;X.fillStyle=window._isDark?'#1e293bdd':'#ffffffdd';X.strokeStyle=window._isDark?'#475569':'#cbd5e1';X.lineWidth=0.8;X.beginPath();X.roundRect(hx,hy,hw,hh,[6]);X.fill();X.stroke();X.font='600 5px Nunito';X.fillStyle='#059669';X.textAlign='left';X.fillText('HISTORY',hx+4,hy+10);X.font='400 5px Nunito';X.fillStyle=window._isDark?'#94a3b8':'#64748b';for(var i=0;i<Math.min(_hist.length,4);i++){X.fillText(_hist[i].t+' '+_hist[i].m,hx+4,hy+20+i*12);}X.restore();}
function draw(){
tk++;X.clearRect(0,0,W,H);hov=null;
// Room status based on current hour
var h=new Date().getHours(),m=new Date().getMinutes();
RM.forEach(function(r){
if(r.freq==='Ven 16h') r._st=(new Date().getDay()===5&&h>=16&&h<17)?'live':'next';
else if(r.freq==='Continu') r._st='live';
else r._st='live'; // GOUVERNANCE PERMANENTE: toujours en session
if(r.freq==='Continu') r._st='live';
});
dC();
drawAmbient();drawLinks();drawMinimap();drawHistory();
RM.forEach(r=>{dR(r);r.ag.forEach(a=>dA(a,r.cl));drawChat(r);drawBubble(r);});
// Tooltip
if(hov){
const rm=RM.find(r=>r.id===hov.rm);
TT.style.display='block';
const sy=window.scrollY||0;
TT.style.left=Math.min(mx+14,W-270)+'px';
TT.style.top=Math.max(10,my-140)+'px';
TT.style.borderColor=rm?rm.cl:'#059669';
TT.querySelector('b').textContent=hov.re+' '+hov.n;
TT.querySelector('i').textContent=rm?rm.l:'';
TT.querySelector('.d').textContent=hov.d;
TT.querySelector('.m').innerHTML=rm?'<b>Status:</b> '+(rm._st==='live'?'EN COURS':'Prochain')+'<br>'+
'📎 <b>Obj:</b> '+rm.obj+'<br>⏱ <b>Durée:</b> '+rm.dur+' ('+rm.freq+')'+
'<br>📋 <b>Sortie:</b> '+rm.concl+'<br>⚡ <b>Actions:</b> '+rm.actions+
'<br>📌 '+rm.prescr:'';
}else TT.style.display='none';
document.getElementById('ht').textContent=new Date().toLocaleTimeString('fr-FR');if(tk%60===0&&document.getElementById('hs'))document.getElementById('hs').style.color=tk%120<60?'#64748b':'#22c55e';if(tk%60===0&&document.getElementById('hs'))document.getElementById('hs').style.color=tk%120<60?'#64748b':'#22c55e';
var _liveRms=RM.filter(function(r){return r._st==='live'}).length;document.getElementById('hs').textContent='Agents: '+AA.length+' | Rooms: '+RM.length+' | En cours: '+_liveRms;
requestAnimationFrame(draw);
}
C.addEventListener('mousemove',e=>{mx=e.clientX;my=e.clientY});
C.addEventListener('click',()=>{if(hov)window.open('/wevia-master.html#'+encodeURIComponent(hov.n),'_blank');});
// EMERGENCY
const EM={infra_down:['CORTEX','Agent Chef','Proactive','Gap Detect','WEVIA Master'],
regression:['NonReg','L99 Visual','L99 Dark','Evolution','WEVIA Master'],
business:['Paperclip','Enterprise','Ethica','WEVIA Life','Claude Opus'],
ia_fail:['Ollama','Qdrant','OSS Disc.','CORTEX','WEVIA Master'],
data_loss:['CORTEX','Agent Chef','Qdrant','Dark Tools','Claude Opus']};
function openE(){document.getElementById('modal').classList.add('show');autoSel()}
function closeM(){document.getElementById('modal').classList.remove('show')}
function autoSel(){
const t=document.getElementById('et').value,auto=EM[t]||[];
document.getElementById('ec').innerHTML=AA.map(a=>`<span class="chip ${auto.includes(a.n)?'sel':''}" onclick="this.classList.toggle('sel')">${a.re} ${a.n}</span>`).join('');
}
function launchE(){
const t=document.getElementById('et').value,sel=[...document.querySelectorAll('#ec .chip.sel')].map(c=>c.textContent.trim());
if(!t||!sel.length)return alert('Choisir type + participants');
emAct=true;closeM();setTimeout(()=>{emAct=false},60000);
fetch('/api/wevia-json-api.php',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({message:'🚨 URGENCE '+t+' — '+sel.join(', ')})}).catch(function(e){console.warn('meeting:',e)});
}
function trigD(){fetch('/api/wevia-meeting.php?action=daily').catch(()=>{});alert('📋 Daily lancé')}
function trigS(){fetch('/api/wevia-meeting.php?action=weekly').catch(()=>{});alert('🏛️ Stratégie lancée')}
// === REAL-TIME DATA POLLING ===
var LIVE={nr:'—',disk:'—',docker:'—',ethica:'—',uptime:'—'};
async function fetchLive(){checkNotif();RM.forEach(function(r){if(r.id==='infra')r.kpi=LIVE.disk+'% disk | '+LIVE.docker+' Docker';else if(r.id==='dev')r.kpi='NR:'+LIVE.nr+' | L99:62/64';else if(r.id==='sec')r.kpi='0 CVE | 47 bans';else if(r.id==='biz')r.kpi='141K+ HCPs | 166 leads';else if(r.id==='ia')r.kpi='9 models | 14K vecs';else if(r.id==='strat')r.kpi='53ag | NR:'+LIVE.nr;else if(r.id==='transit')r.kpi='203 wiki | 15 repos';});
try{
var r=await fetch('/api/nonreg-api.php?cat=all');/* HTML_GUARD_V2_BATCH */ var _t_d=await r.text(); var d; {var _q=(_t_d||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d={error:"[HTTP "+(r.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:"[JSON] "+e.message}}}}
LIVE.nr=d.pass+'/'+d.total;
}catch(e){}
try{
var r2=await fetch('/api/infra-monitor-api.php');/* HTML_GUARD_V2_BATCH */ var _t_d2=await r2.text(); var d2; {var _q=(_t_d2||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d2={error:"[HTTP "+(r2.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d2=JSON.parse(_q)}catch(e){d2={error:"[JSON] "+e.message}}}}
LIVE.disk=(d2.disk||{}).percent||'—';
LIVE.docker=(d2.docker||{}).count||'—';
}catch(e){}
try{
var r3=await fetch('/api/enterprise-status.php');/* HTML_GUARD_V2_BATCH */ var _t_d3=await r3.text(); var d3; {var _q=(_t_d3||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d3={error:"[HTTP "+(r3.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d3=JSON.parse(_q)}catch(e){d3={error:"[JSON] "+e.message}}}}
LIVE.uptime=d3.status||'—';
}catch(e){}
// Update room transcripts with live data
if(RM[1]&&RM[1].chat)RM[1].chat[0]={who:'\U0001f9e0',t:'S204 '+LIVE.disk+'% disk. Docker '+LIVE.docker};
if(RM[2]&&RM[2].chat)RM[2].chat[0]={who:'\u2705',t:'NonReg '+LIVE.nr+' PASS'};
// Update header
var ls=document.getElementById('hs');
if(ls)ls.textContent='Agents: '+AA.length+' | Rooms: '+RM.length+' | NR: '+LIVE.nr+' | '+LIVE.uptime;
}
fetchLive();resize();
// === REAL-TIME DATA POLLING ===
var LIVE={nr:'—',disk:'—',docker:'—',ethica:'—',uptime:'—'};
async function fetchLive(){
try{
var r=await fetch('/api/nonreg-api.php?cat=all');/* HTML_GUARD_V2_BATCH */ var _t_d=await r.text(); var d; {var _q=(_t_d||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d={error:"[HTTP "+(r.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:"[JSON] "+e.message}}}}
LIVE.nr=d.pass+'/'+d.total;
}catch(e){}
try{
var r2=await fetch('/api/infra-monitor-api.php');/* HTML_GUARD_V2_BATCH */ var _t_d2=await r2.text(); var d2; {var _q=(_t_d2||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d2={error:"[HTTP "+(r2.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d2=JSON.parse(_q)}catch(e){d2={error:"[JSON] "+e.message}}}}
LIVE.disk=(d2.disk||{}).percent||'—';
LIVE.docker=(d2.docker||{}).count||'—';
}catch(e){}
try{
var r3=await fetch('/api/enterprise-status.php');/* HTML_GUARD_V2_BATCH */ var _t_d3=await r3.text(); var d3; {var _q=(_t_d3||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d3={error:"[HTTP "+(r3.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d3=JSON.parse(_q)}catch(e){d3={error:"[JSON] "+e.message}}}}
LIVE.uptime=d3.status||'—';
}catch(e){}
// Update room transcripts with live data
if(RM[1]&&RM[1].chat)RM[1].chat[0]={who:'\U0001f9e0',t:'S204 '+LIVE.disk+'% disk. Docker '+LIVE.docker};
if(RM[2]&&RM[2].chat)RM[2].chat[0]={who:'\u2705',t:'NonReg '+LIVE.nr+' PASS'};
// Update header
var ls=document.getElementById('hs');
if(ls)ls.textContent='Agents: '+AA.length+' | Rooms: '+RM.length+' | NR: '+LIVE.nr+' | '+LIVE.uptime;
}
fetchLive();
setInterval(fetchLive,15000);
draw();
C.addEventListener('click',function(){window._aClick=true;});
function showAP(a,cl){var rm=RM.find(function(r){return r.id===a.rm;});var h='<h3 style="color:'+cl+'">'+a.re+' '+a.n+'</h3><p style="color:#64748b;font-size:11px">'+(a.d||'')+'</p><hr style="border:0;border-top:1px solid #e2e8f0;margin:8px 0"><b>Room:</b> '+(rm?rm.l:'?')+'<br><b>Status:</b> '+(rm&&rm._st==='live'?'EN COURS':'PROCHAIN')+'<br>';if(rm&&rm.kpi)h+='<b>KPI:</b> '+rm.kpi+'<br>';h+='<hr style="border:0;border-top:1px solid #e2e8f0;margin:8px 0"><b>Transcript:</b><br>';if(rm&&rm.chat)rm.chat.forEach(function(m){h+='<div style="padding:2px 0;font-size:11px"><b>'+m.who+'</b> '+m.t+'</div>';});document.getElementById('apC').innerHTML=h;document.getElementById('agentPanel').style.display='block';}
var _lastLive='';function checkNotif(){var live=RM.filter(function(r){return r._st==='live';}).map(function(r){return r.id;}).join(',');if(_lastLive&&live!==_lastLive){var badge=document.createElement('div');badge.className='notifBadge';badge.style.cssText='position:fixed;top:28px;right:10px;background:#059669;color:#fff;padding:4px 10px;border-radius:6px;font:700 10px Nunito;z-index:99;animation:fadeIn .3s';badge.textContent='Room changed: '+live;document.body.appendChild(badge);setTimeout(function(){badge.remove();},4000);}_lastLive=live;}
var _dark=false;function toggleDark(){_dark=!_dark;document.body.style.background=_dark?'#0f172a':'#eaeff7';document.body.style.backgroundImage=_dark?'none':'radial-gradient(#c8d4e4 0.4px,transparent 0.4px)';document.getElementById('hud').style.background=_dark?'#1e293bdd':'#fffd';document.getElementById('hud').style.color=_dark?'#e2e8f0':'#1a1a2e';window._isDark=_dark;}
var _lastLive='';function checkNotif(){var live=RM.filter(function(r){return r._st==='live';}).map(function(r){return r.id;}).join(',');if(live!==_lastLive&&_lastLive!==''){try{var ac=new AudioContext();var o=ac.createOscillator();var g=ac.createGain();o.connect(g);g.connect(ac.destination);o.frequency.value=800;g.gain.value=0.05;o.start();o.stop(ac.currentTime+0.1);}catch(e){}_lastLive=live;}else{_lastLive=live;}}
var _hist=[];function addHist(msg){_hist.unshift({t:new Date().toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit'}),m:msg});if(_hist.length>5)_hist.pop();}
</script><div id="wLeg" ondblclick="this.remove()" onclick="if(event.target.dataset.cls)this.remove()" style="position:fixed;bottom:6px;left:50%;transform:translateX(-50%);display:flex;gap:8px;padding:3px 12px;background:#ffffffcc;backdrop-filter:blur(4px);border-radius:6px;border:1px solid #e2e8f0;font:600 7px Nunito,sans-serif;color:#64748b;z-index:90;white-space:nowrap"><span style="color:#059669">Strategy</span><span style="color:#3b82f6">Infra</span><span style="color:#10b981">Dev</span><span style="color:#ef4444">Secu</span><span style="color:#f59e0b">Biz</span><span style="color:#8b5cf6">IA</span><span style="color:#64748b">Transit</span><span style="color:#22c55e;font-size:8px">&#9679; LIVE</span></div>
<div id="agentPanel" style="display:none;position:fixed;right:0;top:0;bottom:0;width:300px;background:#fff;box-shadow:-4px 0 20px #0002;z-index:80;padding:16px;overflow-y:auto;border-left:3px solid #059669;font:13px Nunito"><button onclick="this.parentElement.style.display='none'" style="float:right;border:none;background:none;font-size:18px;cursor:pointer"></button><div id="apC"></div></div>
<!-- CARTO_REMOVED -->
<div id="liveOpsPanel" style="display:none"></div>
<script>
/* V75 AVATAR UNIFIER — Meeting-rooms emoji style (Opus 19avr) */
(function() {
if (window.__WEVAL_AVATAR_V75) return;
window.__WEVAL_AVATAR_V75 = true;
const REG_URL = '/api/agent-avatars-v2.json';
const SVG_EP = '/api/agent-avatar-svg.php';
function emojiSVGUrl(name, emoji) {
return SVG_EP + '?n=' + encodeURIComponent(name) + '&e=' + encodeURIComponent(emoji);
}
fetch(REG_URL + '?t=' + Date.now()).then(r => r.json()).then(REG => {
function getAvatarUrl(name) {
const rec = REG[name];
if (!rec) return null;
if (typeof rec === 'object' && rec.svg) return rec.svg;
if (typeof rec === 'object' && rec.emoji) return emojiSVGUrl(name, rec.emoji);
return typeof rec === 'string' ? rec : null;
}
function findCI(key) {
const lower = key.toLowerCase();
for (const k of Object.keys(REG)) if (k.toLowerCase() === lower) return k;
return null;
}
function apply() {
document.querySelectorAll('img').forEach(img => {
const key = img.alt || img.dataset.agent || img.dataset.name || img.title || '';
if (!key) return;
let url = getAvatarUrl(key);
if (!url) { const alt = findCI(key); if (alt) url = getAvatarUrl(alt); }
if (url && img.src !== url && !img.src.endsWith(url)) {
img.src = url;
img.setAttribute('data-weval-v75', '1');
}
});
document.querySelectorAll('[data-agent]:not([data-weval-v75-applied])').forEach(el => {
const name = el.dataset.agent;
const url = getAvatarUrl(name);
if (!url) return;
const img = document.createElement('img');
img.src = url; img.alt = name; img.title = name;
img.className = 'v75-avatar';
img.style.cssText = 'width:32px;height:32px;border-radius:50%;object-fit:cover;vertical-align:middle;background:transparent';
el.setAttribute('data-weval-v75-applied', '1');
el.prepend(img);
});
}
apply();
setTimeout(apply, 400); setTimeout(apply, 1200); setTimeout(apply, 3000);
const mo = new MutationObserver(() => apply());
mo.observe(document.body, {childList: true, subtree: true});
setTimeout(() => mo.disconnect(), 20000);
console.log('[V75 AvatarUnifier] applied from', Object.keys(REG).length, 'agents');
}).catch(e => console.warn('[V75] fetch failed', e));
})();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
<script>
(function(){
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
var d = document;
var m = d.createElement('div');
m.id = 'opus-udrill';
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
var inner = d.createElement('div');
inner.id = 'opus-udrill-in';
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
inner.addEventListener('click', function(e){ e.stopPropagation(); });
m.appendChild(inner);
m.addEventListener('click', function(){ m.style.display='none'; });
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
(d.body || d.documentElement).appendChild(m);
function openCard(card) {
// Clone card content + show close btn + increase font-size
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
inner.innerHTML = html;
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
m.style.display = 'flex';
}
function wire(root) {
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
var cards = root.querySelectorAll(sels);
for (var i = 0; i < cards.length; i++) {
var c = cards[i];
if (c.__opusWired) continue;
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
var r = c.getBoundingClientRect();
if (r.width < 60 || r.height < 40) continue;
c.__opusWired = true;
c.style.cursor = 'pointer';
c.setAttribute('role','button');
c.setAttribute('tabindex','0');
c.addEventListener('click', function(ev){
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
if (ev.target.closest('a,button,input,select')) return;
ev.preventDefault(); ev.stopPropagation();
openCard(this);
});
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
}
}
// Initial + mutation observer
var initRun = function(){ wire(d.body || d.documentElement); };
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
else initRun();
var mo = new MutationObserver(function(muts){
var newCard = false;
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
if (newCard) initRun();
});
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
})();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
<!-- === OPUS HONEST NR/L99 OVERLAY v1 19avr - append-only doctrine #14 === -->
<script>
(function(){
if (window.__opusHonestOverlay) return; window.__opusHonestOverlay = true;
async function updateHonestValues(){
try {
const r = await fetch('/api/l99-honest.php', {cache:'no-store'});
const d = await r.json();
if (!d.ok) return;
const realNR = `${d.combined.pass}/${d.combined.total}`;
const realSigma = d.sigma;
// Find elements showing the myth values
const mythRegex = /(153\/153|304\/304|NR status 153\/153|L99 status 304\/304|NR 153\/153|L99 304\/304)/g;
// Walk text nodes
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
const toReplace = [];
let node;
while (node = walker.nextNode()) {
if (node.nodeValue && mythRegex.test(node.nodeValue)) toReplace.push(node);
}
toReplace.forEach(textNode => {
const parent = textNode.parentNode;
if (!parent || parent.hasAttribute('data-opus-honest-applied')) return;
const newText = textNode.nodeValue.replace(/153\/153/g, realNR).replace(/304\/304/g, realNR);
textNode.nodeValue = newText;
parent.setAttribute('data-opus-honest-applied', '1');
});
// Add a small badge bottom-right showing honest live status
if (!document.getElementById('opus-honest-badge')) {
const b = document.createElement('div');
b.id = 'opus-honest-badge';
b.style.cssText = 'position:fixed;bottom:12px;right:12px;background:linear-gradient(90deg,#14b8a6,#a855f7);color:#05060a;padding:6px 12px;font:10px/1.3 Inter,system-ui,sans-serif;font-weight:700;border-radius:8px;z-index:99993;box-shadow:0 4px 12px rgba(0,0,0,0.3);cursor:pointer;max-width:280px';
b.title = 'Cliquer pour détails';
b.innerHTML = `✓ NR ${realNR} · ${realSigma} live`;
b.onclick = () => {
alert(`HONEST NonReg (doctrine #4):\n\nmaster: ${d.master.pass}/${d.master.total}\nopus: ${d.opus.pass}/${d.opus.total}\ncombined: ${realNR}\nsigma: ${realSigma}\n\n${d.myth_153}\n${d.myth_304}`);
};
document.body.appendChild(b);
}
} catch(e){console.error('L99-honest fetch error:', e);}
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', updateHonestValues);
else updateHonestValues();
setInterval(updateHonestValues, 90000);
})();
</script>
<!-- === OPUS HONEST END === -->
<script src="/api/archi-meta-badge.js" defer></script>
<script src="/api/a11y-auto-enhancer.js" defer></script>
<!-- WTP_UDOCK_V1 (Opus 21-avr t36 sudo) --><script src="/wtp-unified-dock.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
<script>
/* WEVIA-WIRE-PAPERCLIP-MR-v1 function */
function sendMRtoPaperclip(){
var action=prompt("Action a faire executer par Paperclip:","");
if(!action) return;
var ctx=[];
if(window.RM){ try { window.RM.forEach(function(r){ if(r._st==='live' && r.chat){ ctx.push('Room '+(r.l||r.id)+': '+r.chat.slice(-3).map(function(m){return m.who+': '+m.t}).join(' | ')); } }); } catch(e){} }
fetch("/api/paperclip-bridge.php",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({source:"meeting-rooms",action:action,prompt:action+"
"+ctx.join("
"),agents_discussed:["MeetingRooms"],priority:"normal"})}).then(function(r){return r.json()}).then(function(d){ alert(d.ok?"Paperclip id="+d.id+" status="+d.status:"Err: "+JSON.stringify(d)); }).catch(function(e){alert("Network err: "+e.message)});
}
</script>
<!-- DOCTRINE-60-UX-JS -->
<script id="doctrine60-ux-js-wevia-meeting-rooms">
document.addEventListener('DOMContentLoaded', () => {
const elements = document.querySelectorAll('.card, .btn, .kpi, .panel');
elements.forEach((el, i) => {
el.classList.add('enter-stagger');
el.style.transitionDelay = `${i * 80}ms`;
});
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1 });
elements.forEach(el => observer.observe(el));
});
</script>
</body></html>