Files
html/medreach-dashboard.html
Opus c7994d48be
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
phase45 doctrine 183 inject 29 pages PRIO3 - 121 pages UX total
29 pages PRIO3 enrichies via inject-d60-direct.py:
registries: wtp-orphans-registry agents-unified-registry ia-sovereign-registry
hubs: wevia-hub vsm-hub wevads-hub weval-data-hub wevia-unified-hub tools-hub toolhub
dashboards: office-365-dashboard-live crm-pipeline-live orphans-dashboard
           medreach-dashboard wevia-director-dashboard security-dashboard
           wevia-memory-dashboard
monitors: sso-monitor monitoring
centers: mega-command-center trust-center
studios: bpmn-studio-live admin-saas
others: ethica-hcp-manager ops-screens-live vsm-pipelines lean-6sigma
        office-admins weval-live-ops

Cumul session Opus:
- 121 pages UX doctrine 60 (92 + 29)
- 31 tags Opus (avec phase45)
- 28 doctrines vault (146-183)

Handler inject-d60-direct.py prouve robuste sur 65+ pages consecutives.
ZERO regression. ZERO ecrasement. NR 153/153 invariant.
2026-04-24 14:25:37 +02:00

369 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>MedReach — Reach Report HCP Maghreb</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{--bg:#0a0c14;--s1:#111827;--s2:#1a1f35;--border:#1e293b;--t1:#f1f5f9;--t2:#94a3b8;--t3:#64748b;--accent:#7c3aed;--teal:#10b981;--amber:#f59e0b;--red:#ef4444;--blue:#3b82f6}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'DM Sans',sans-serif;background:var(--bg);color:var(--t1);min-height:100vh;overflow-x:hidden}
.top{display:flex;justify-content:space-between;align-items:center;padding:24px 40px;border-bottom:1px solid var(--border)}
.logo{display:flex;align-items:center;gap:14px}
.logo-mark{width:40px;height:40px;background:linear-gradient(135deg,var(--accent),#a855f7);border-radius:10px;display:flex;align-items:center;justify-content:center;font-weight:700;color:#fff;font-size:18px}
.logo h1{font-size:20px;font-weight:700;letter-spacing:-.5px}
.logo h1 span{color:var(--accent);font-weight:400}
.logo-sub{font-size:11px;color:var(--t3);margin-top:2px}
.meta{display:flex;gap:20px;align-items:center;font-size:12px;color:var(--t3)}
.meta .dot{width:8px;height:8px;border-radius:50%;background:var(--teal);animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
.container{max-width:1300px;margin:0 auto;padding:32px 40px}
.section-title{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.1em;color:var(--accent);margin-bottom:16px}
h2{font-size:22px;font-weight:700;margin-bottom:6px}
.subtitle{color:var(--t3);font-size:13px;margin-bottom:24px}
.kpi-row{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:32px}
.kpi{background:var(--s1);border:1px solid var(--border);border-radius:14px;padding:20px}
.kpi .val{font-family:'JetBrains Mono',monospace;font-size:28px;font-weight:700;margin-bottom:4px}
.kpi .lbl{font-size:11px;color:var(--t3);text-transform:uppercase;letter-spacing:.06em}
.kpi .bar{height:4px;border-radius:2px;background:var(--border);margin-top:10px;overflow:hidden}
.kpi .bar-fill{height:100%;border-radius:2px;transition:width 1s ease}
.green{color:var(--teal)}.amber{color:var(--amber)}.purple{color:var(--accent)}.blue{color:var(--blue)}.red{color:var(--red)}
.countries{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:32px}
.country-card{background:var(--s1);border:1px solid var(--border);border-radius:14px;padding:24px;position:relative;overflow:hidden}
.country-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px}
.country-card.tn::before{background:var(--red)}.country-card.dz::before{background:var(--teal)}.country-card.ma::before{background:var(--amber)}
.country-card .flag{font-size:28px;margin-bottom:8px}
.country-card h3{font-size:16px;font-weight:600;margin-bottom:12px}
.country-card .stats{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.country-card .stat{font-size:12px;color:var(--t3)}
.country-card .stat b{display:block;font-family:'JetBrains Mono',monospace;font-size:16px;color:var(--t1);margin-bottom:2px}
.table-wrap{background:var(--s1);border:1px solid var(--border);border-radius:14px;overflow:hidden;margin-bottom:32px}
.table-wrap h3{padding:20px 24px 0;font-size:15px;font-weight:600}
table{width:100%;border-collapse:collapse;font-size:13px}
thead{background:rgba(255,255,255,.02)}
th{text-align:left;padding:12px 16px;font-size:11px;color:var(--t3);text-transform:uppercase;letter-spacing:.06em;font-weight:600;border-bottom:1px solid var(--border)}
td{padding:10px 16px;border-bottom:1px solid rgba(255,255,255,.03);color:var(--t2)}
td:first-child{color:var(--t1);font-weight:500}
tr:hover{background:rgba(124,58,237,.03)}
.bar-cell{display:flex;align-items:center;gap:8px}
.bar-mini{height:6px;border-radius:3px;background:var(--border);width:100px;overflow:hidden;flex-shrink:0}
.bar-mini .fill{height:100%;border-radius:3px;background:linear-gradient(90deg,var(--accent),#a855f7)}
.perf-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:32px}
.perf{background:var(--s1);border:1px solid var(--border);border-radius:14px;padding:20px;text-align:center}
.perf .ring{width:80px;height:80px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 12px;position:relative}
.perf .ring::before{content:'';position:absolute;inset:0;border-radius:50%;border:3px solid var(--border)}
.perf .ring-val{font-family:'JetBrains Mono',monospace;font-size:20px;font-weight:700}
.perf .ring-lbl{font-size:11px;color:var(--t3)}
.perf h4{font-size:13px;font-weight:600;margin-bottom:4px}
.perf p{font-size:11px;color:var(--t3)}
.footer{text-align:center;padding:32px;color:var(--t3);font-size:11px;border-top:1px solid var(--border)}
.footer a{color:var(--accent)}
@media print{body{background:#fff;color:#1a1a2e}.kpi,.country-card,.table-wrap,.perf{border-color:#e2e8f0;background:#fafbfc}}
</style>
<!-- DOCTRINE-60-UX-ENRICH direct-inject-20260424-142458 -->
<style id="doctrine60-ux-direct">
/* DOCTRINE-60-UX-ENRICH injected-direct */
body::before {
content: '';
position: fixed;
top: 0; left: 0; width: 100vw; height: 100vh;
background: radial-gradient(circle at 50% 50%, rgba(100,180,255,0.08), transparent 60%);
pointer-events: none;
z-index: -1;
}
.card, .kpi, .panel, .btn {
transition: all 0.3s cubic-bezier(0.2,0,0.1,1);
}
.card:hover, .kpi:hover, .panel:hover {
box-shadow: 0 4px 20px rgba(100,180,255,0.2);
border-color: rgba(100,180,255,0.5);
}
@keyframes pulseD60 {
0%,100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.05); }
}
.pulse, .live-indicator, .active, .online {
animation: pulseD60 3s ease-in-out infinite;
}
.modal, .chat, .speech, .overlay {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.enter-stagger {
animation: enterStagD60 0.5s cubic-bezier(0.2,0,0.1,1) forwards;
}
@keyframes enterStagD60 {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div class="top">
<div class="logo">
<div class="logo-mark">M</div>
<div>
<h1>Med<span>Reach</span></h1>
<div class="logo-sub">HCP Reach Report — Maghreb 2026</div>
</div>
</div>
<div class="nav-top" style="display:flex;gap:8px;margin-right:20px"><a href="medreach-dashboard.html" style="padding:8px 16px;border-radius:8px;font-size:13px;font-weight:500;color:#fff;text-decoration:none;background:#7c3aed;border:1px solid #7c3aed">📊 Reach Report</a><a href="medreach-campaign.html" style="padding:8px 16px;border-radius:8px;font-size:13px;font-weight:500;color:#94a3b8;text-decoration:none;border:1px solid #1e293b">🚀 Projection Campagne</a></div><div class="meta">
<span class="dot"></span> Live Data
<span>|</span>
<span>Généré le 30 mars 2026</span>
<span>|</span>
<span>WEVAL Consulting × Ethica Pharma</span>
</div>
</div>
<div class="container">
<div class="section-title">Vue d'ensemble</div>
<h2>Reach HCP Qualifié — 3 Pays</h2>
<div class="subtitle">Base vérifiée Google 100% • Enrichissement continu • 32 spécialités</div>
<div class="kpi-row">
<div class="kpi"><div class="val green">130 600</div><div class="lbl">HCPs Total</div><div class="bar"><div class="bar-fill" style="width:100%;background:var(--teal)"></div></div></div>
<div class="kpi"><div class="val blue">107 604</div><div class="lbl">Avec Email</div><div class="bar"><div class="bar-fill" style="width:82.4%;background:var(--blue)"></div></div></div>
<div class="kpi"><div class="val purple">32</div><div class="lbl">Spécialités</div><div class="bar"><div class="bar-fill" style="width:100%;background:var(--accent)"></div></div></div>
<div class="kpi"><div class="val amber">82.4%</div><div class="lbl">Couverture Email</div><div class="bar"><div class="bar-fill" style="width:82.4%;background:var(--amber)"></div></div></div>
<div class="kpi"><div class="val green">3</div><div class="lbl">Pays Couverts</div><div class="bar"><div class="bar-fill" style="width:100%;background:var(--teal)"></div></div></div>
</div>
<div class="section-title">Par Pays</div>
<div class="countries">
<div class="country-card tn">
<div class="flag">🇹🇳</div>
<h3>Tunisie</h3>
<div class="stats">
<div class="stat"><b>17 329</b>HCPs</div>
<div class="stat"><b>14 396</b>Emails (83%)</div>
<div class="stat"><b>83%</b>Couverture email</div>
<div class="stat"><b>30</b>Spécialités</div>
</div>
</div>
<div class="country-card dz">
<div class="flag">🇩🇿</div>
<h3>Algérie</h3>
<div class="stats">
<div class="stat"><b>91 985</b>HCPs</div>
<div class="stat"><b>76 826</b>Emails (84%)</div>
<div class="stat"><b>84%</b>Couverture email</div>
<div class="stat"><b>30</b>Spécialités</div>
</div>
</div>
<div class="country-card ma">
<div class="flag">🇲🇦</div>
<h3>Maroc</h3>
<div class="stats">
<div class="stat"><b>19 407</b>HCPs</div>
<div class="stat"><b>14 503</b>Emails (75%)</div>
<div class="stat"><b>75%</b>Couverture email</div>
<div class="stat"><b>30</b>Spécialités</div>
</div>
</div>
</div>
<div class="section-title">Reach par Spécialité × Pays</div>
<div class="table-wrap">
<h3>🎯 Spécialités Périmètre Ethica (18 Marques)</h3>
<table>
<thead><tr><th>Spécialité</th><th>🇹🇳 Tunisie</th><th>🇩🇿 Algérie</th><th>🇲🇦 Maroc</th><th>Total</th><th>Distribution</th></tr></thead>
<tbody>
<tr><td>Généralistes</td><td>1 865</td><td>8 709</td><td>2 036</td><td><b>12 610</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:100%"></div></div><span style="color:var(--t3);font-size:11px">9.7%</span></div></td></tr>
<tr><td>Pédiatres</td><td>1 449</td><td>6 384</td><td>1 421</td><td><b>9 254</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:73%"></div></div><span style="color:var(--t3);font-size:11px">7.1%</span></div></td></tr>
<tr><td>Dentistes</td><td>1 401</td><td>4 850</td><td>1 887</td><td><b>8 138</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:65%"></div></div><span style="color:var(--t3);font-size:11px">6.2%</span></div></td></tr>
<tr><td>Cardiologues</td><td>1 528</td><td>4 242</td><td>1 785</td><td><b>7 555</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:60%"></div></div><span style="color:var(--t3);font-size:11px">5.8%</span></div></td></tr>
<tr><td>Gynécologues</td><td>1 237</td><td>5 052</td><td>1 425</td><td><b>7 714</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:61%"></div></div><span style="color:var(--t3);font-size:11px">5.9%</span></div></td></tr>
<tr><td>Pharmaciens</td><td>1 147</td><td>4 620</td><td>1 152</td><td><b>6 919</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:55%"></div></div><span style="color:var(--t3);font-size:11px">5.3%</span></div></td></tr>
<tr><td>Gastro-entérologues</td><td>1 372</td><td>3 573</td><td>1 463</td><td><b>6 408</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:51%"></div></div><span style="color:var(--t3);font-size:11px">4.9%</span></div></td></tr>
<tr><td>Allergologues</td><td>1 341</td><td>3 588</td><td>1 410</td><td><b>6 339</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:50%"></div></div><span style="color:var(--t3);font-size:11px">4.9%</span></div></td></tr>
<tr><td>ORL</td><td>1 165</td><td>3 708</td><td>1 226</td><td><b>6 099</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:48%"></div></div><span style="color:var(--t3);font-size:11px">4.7%</span></div></td></tr>
<tr><td>Orthopédistes</td><td>1 184</td><td>3 592</td><td>1 164</td><td><b>5 940</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:47%"></div></div><span style="color:var(--t3);font-size:11px">4.5%</span></div></td></tr>
<tr><td>Pneumologues</td><td>1 244</td><td>3 314</td><td>1 261</td><td><b>5 819</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:46%"></div></div><span style="color:var(--t3);font-size:11px">4.5%</span></div></td></tr>
<tr><td>Rhumatologues</td><td>1 170</td><td>3 304</td><td>1 198</td><td><b>5 672</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:45%"></div></div><span style="color:var(--t3);font-size:11px">4.3%</span></div></td></tr>
<tr><td>Dermatologues</td><td>172</td><td>2 252</td><td>207</td><td><b>2 631</b></td><td><div class="bar-cell"><div class="bar-mini"><div class="fill" style="width:21%"></div></div><span style="color:var(--t3);font-size:11px">2.0%</span></div></td></tr>
</tbody>
</table>
</div>
<div class="section-title">Performance Infrastructure</div>
<div class="perf-grid">
<div class="perf">
<div class="ring" style="border:3px solid var(--teal)"><span class="ring-val green">97%</span></div>
<h4>Délivrabilité Inbox</h4>
<p>PMTA + IP dédiées</p>
</div>
<div class="perf">
<div class="ring" style="border:3px solid var(--blue)"><span class="ring-val blue">28-35%</span></div>
<h4>Taux d'Ouverture</h4>
<p>vs 18-22% benchmark pharma</p>
</div>
<div class="perf">
<div class="ring" style="border:3px solid var(--accent)"><span class="ring-val purple">4-6%</span></div>
<h4>Taux de Clic</h4>
<p>CTA optimisés santé</p>
</div>
<div class="perf">
<div class="ring" style="border:3px solid var(--amber)"><span class="ring-val amber">&lt;2%</span></div>
<h4>Bounce Rate</h4>
<p>Vérification MX continue</p>
</div>
</div>
<div class="section-title">Conformité & Sécurité</div>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:32px">
<div style="background:var(--s1);border:1px solid var(--border);border-radius:14px;padding:20px">
<div style="font-size:20px;margin-bottom:8px">🇲🇦</div>
<h4 style="font-size:13px;margin-bottom:6px">Loi 09-08 (CNDP)</h4>
<p style="font-size:11px;color:var(--t3)">Consentement explicite, traçabilité opt-in/opt-out, droit à l'oubli</p>
</div>
<div style="background:var(--s1);border:1px solid var(--border);border-radius:14px;padding:20px">
<div style="font-size:20px;margin-bottom:8px">🇹🇳</div>
<h4 style="font-size:13px;margin-bottom:6px">Loi 63-2004 (INPDP)</h4>
<p style="font-size:11px;color:var(--t3)">Protection des données personnelles, consentement spécifique par finalité</p>
</div>
<div style="background:var(--s1);border:1px solid var(--border);border-radius:14px;padding:20px">
<div style="font-size:20px;margin-bottom:8px">🇩🇿</div>
<h4 style="font-size:13px;margin-bottom:6px">Loi 18-07 (ANPDP)</h4>
<p style="font-size:11px;color:var(--t3)">Protection des données à caractère personnel, autorisation préalable</p>
</div>
</div>
</div>
<div class="footer">
MedReach by <a href="https://weval-consulting.com">WEVAL Consulting</a> — Sovereign AI & Digital Marketing • Casablanca / Paris<br>
Données au 30 mars 2026 • Base enrichie en continu • 130 600 HCPs qualifiés
</div>
<!-- WAVE 162 — Unified Pipeline Overlay -->
<div id="unifiedLiveOverlay" style="position:fixed;bottom:12px;right:12px;width:280px;max-height:calc(100vh - 120px);overflow-y:auto;background:linear-gradient(135deg,rgba(10,14,26,0.94),rgba(30,30,60,0.92));border:1px solid rgba(6,182,212,0.4);border-radius:10px;padding:10px;backdrop-filter:blur(14px);z-index:9999;font:600 9px Nunito,system-ui;color:#e2e8f0;box-shadow:0 4px 30px rgba(0,0,0,0.5)">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;padding-bottom:5px;border-bottom:1px solid rgba(100,116,139,0.3)">
<div style="font:900 10px Orbitron,system-ui;color:#06b6d4">🔴 <b id=closeLive style=cursor:pointer;margin-right:6px;color:gray onclick=unifiedLiveOverlay.remove()>x</b>UNIFIED LIVE</div>
<div id="ulo-ts" style="font-size:8px;color:#64748b"></div>
</div>
<div id="ulo-body">Loading...</div>
</div>
<script>
(function(){
const U='/api/weval-unified-pipeline.php';
async function tick(){
try{
const r=await fetch(U,{cache:'no-cache'});
if(!r.ok) return;
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); let d=null; {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}}}}
const body=document.getElementById('ulo-body');
const ts=document.getElementById('ulo-ts');
if(!body) return;
const h=d.l99.health||'?';
const hc={GREEN:'#10b981',YELLOW:'#f59e0b',RED:'#ef4444'}[h]||'#64748b';
let html='<div style="background:'+hc+'15;border-left:3px solid '+hc+';padding:5px;margin-bottom:5px;border-radius:3px"><b style="color:'+hc+'">● '+h+'</b> L99 <b>'+d.l99.pass+'/'+d.l99.total+'</b><br><span style="color:#94a3b8">Disk '+d.system.disk_pct+'% Docker '+d.system.docker_count+' Crons '+d.system.cron_count+'</span></div>';
html+='<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-bottom:5px"><div style="background:rgba(6,182,212,0.1);border:1px solid rgba(6,182,212,0.3);border-radius:4px;padding:4px"><div style="font:800 8px Orbitron;color:#06b6d4">SOVEREIGN</div><b>'+d.providers.count+'</b> providers<br><b>'+d.ollama.models+'</b> Ollama<br><b>'+d.qdrant.collections.length+'</b> Qdrant</div><div style="background:rgba(139,92,246,0.1);border:1px solid rgba(139,92,246,0.3);border-radius:4px;padding:4px"><div style="font:800 8px Orbitron;color:#8b5cf6">PAPERCLIP</div><b>'+d.goals.length+'</b> goals<br><b>'+d.projects.length+'</b> projects<br><b>'+d.routines.length+'</b> routines</div></div>';
html+='<div style="background:rgba(245,158,11,0.1);border:1px solid rgba(245,158,11,0.3);border-radius:4px;padding:4px;margin-bottom:5px"><div style="font:800 8px Orbitron;color:#f59e0b">ETHICA</div><b>'+(d.ethica.hcps_validated/1000).toFixed(0)+'K</b> HCPs '+d.ethica.coverage.join(' ')+'</div>';
const rpa=d.routines_per_agent||{};
const top=Object.entries(rpa).sort((a,b)=>b[1]-a[1]).slice(0,5);
if(top.length){
html+='<div style="font:800 8px Orbitron;color:#10b981;margin:4px 0">TOP AGENTS</div>';
top.forEach(([n,c])=>{html+='<div style="display:flex;justify-content:space-between;padding:1px 3px;background:rgba(16,185,129,0.05);border-radius:2px;margin-bottom:1px"><span>'+n+'</span><b style="color:#10b981">'+c+'</b></div>';});
}
html+='<div style="margin-top:5px;padding-top:4px;border-top:1px solid rgba(100,116,139,0.3);font-size:8px;color:#64748b;text-align:center"><a href="/wevia-master.html" style="color:#06b6d4">Master</a> · <a href="/agents-archi.html" style="color:#06b6d4">Archi</a> · <a href="/wevia-meeting-rooms.html" style="color:#06b6d4">Rooms</a> · <a href="https://paperclip.weval-consulting.com" style="color:#06b6d4" target="_blank">Paperclip</a></div>';
body.innerHTML=html;
if(ts) ts.textContent=new Date().toLocaleTimeString('fr-FR',{hour:'2-digit',minute:'2-digit',second:'2-digit'});
}catch(e){}
}
setTimeout(tick,1500);setInterval(tick,30000);
})();
</script>
<!-- CARTO_REMOVED -->
<!-- === 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 === -->
<script src="/api/archi-meta-badge.js" defer></script>
<script src="/api/a11y-auto-enhancer.js" defer></script>
<!-- WTP_UDOCK_V1 (Opus 21-avr t33b5) --><script src="/wtp-unified-dock.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
<!-- DOCTRINE-60-UX-JS --><script id="doctrine60-ux-js-direct">
// DOCTRINE-60-UX-JS staggered entrance
(function(){
if (!('IntersectionObserver' in window)) return;
const obs = new IntersectionObserver((entries) => {
entries.forEach((e, i) => {
if (e.isIntersecting) {
setTimeout(() => e.target.classList.add('enter-stagger'), i * 80);
obs.unobserve(e.target);
}
});
});
document.querySelectorAll('.card, .kpi, .panel').forEach(el => obs.observe(el));
})();
</script>
</body>
</html>