Files
html/app/wevads/index.html
2026-04-12 22:57:03 +02:00

960 lines
146 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>WEVADS IA — Omnichannel Intelligence Platform</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>:root{
--bg:#050810;--bg2:#080d1a;--sf:#0c1324;--card:#0f172a;--card2:#131c33;--hov:#162040;
--bd:rgba(255,255,255,.04);--bd2:rgba(255,255,255,.08);--bd3:rgba(255,255,255,.12);
--tx:#94a3b8;--mu:#5a6a80;--dm:#374151;--wh:#f1f5f9;
--ac:#d4a843;--ac2:#eacc6b;--ac3:#c49a38;--acB:rgba(212,168,67,.06);--acBd:rgba(212,168,67,.18);--acG:rgba(212,168,67,.03);
--cy:#22d3ee;--gn:#34d399;--gnB:rgba(52,211,153,.06);--rd:#f87171;--rdB:rgba(248,113,113,.06);
--or:#fb923c;--pu:#a78bfa;--puB:rgba(167,139,250,.06);--bl:#60a5fa;--blB:rgba(96,165,250,.06);
--em:#60a5fa;--sm:#34d399;--wa:#25d366;--tg:#229ED9;
--r:14px;--r2:18px;--r3:10px;--rs:6px;
--f:'Outfit',system-ui,-apple-system,sans-serif;--m:'JetBrains Mono',monospace;
--shadow:0 4px 24px rgba(0,0,0,.25),0 1px 2px rgba(0,0,0,.15);
--shadowL:0 8px 40px rgba(0,0,0,.4);
--glass:rgba(255,255,255,.02);--glassB:rgba(255,255,255,.05)
}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:var(--f);background:var(--bg);color:var(--tx);display:flex;height:100vh;overflow:hidden;font-size:14px;-webkit-font-smoothing:antialiased;letter-spacing:.01em}
::selection{background:rgba(212,168,67,.2);color:var(--wh)}
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:var(--bd3);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--bd2)}::-webkit-scrollbar-track{background:transparent}
a{color:var(--ac);text-decoration:none;transition:color .2s}a:hover{color:var(--ac2)}
button{font-family:var(--f);cursor:pointer;border:none;outline:none;transition:all .2s}
input,select,textarea{font-family:var(--f);outline:none;background:var(--bg);border:1px solid var(--bd2);color:var(--wh);border-radius:var(--r3);padding:11px 16px;font-size:13px;transition:all .25s;letter-spacing:.01em}
input:focus,select:focus,textarea:focus{border-color:var(--ac3);box-shadow:0 0 0 3px var(--acG),0 0 20px var(--acG)}
input::placeholder{color:var(--dm);font-weight:300}
select{cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235a6a80' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 12px center;padding-right:32px}
table{width:100%;border-collapse:separate;border-spacing:0}
th{text-align:left;font-size:10px;font-weight:700;color:var(--mu);text-transform:uppercase;letter-spacing:.1em;padding:12px 16px;border-bottom:1px solid var(--bd2);background:var(--sf)}
td{padding:12px 16px;border-bottom:1px solid var(--bd);font-size:13px;transition:background .15s}
tr:hover td{background:rgba(212,168,67,.015)}
tbody tr{transition:transform .15s}
@keyframes fadeUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
@keyframes slideR{from{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:none}}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
@keyframes glow{0%,100%{box-shadow:0 0 20px rgba(212,168,67,.08)}50%{box-shadow:0 0 40px rgba(212,168,67,.15)}}
@keyframes shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes toastIn{from{opacity:0;transform:translateX(40px)}to{opacity:1;transform:none}}
@keyframes borderGlow{0%,100%{border-color:rgba(212,168,67,.1)}50%{border-color:rgba(212,168,67,.25)}}
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}
.login-wrap{position:fixed;inset:0;background:var(--bg);display:flex;align-items:center;justify-content:center;z-index:200;overflow:hidden}
.login-wrap::before{content:'';position:absolute;width:600px;height:600px;background:radial-gradient(circle,rgba(212,168,67,.04),transparent 70%);top:20%;left:50%;transform:translateX(-50%);pointer-events:none}
.login-wrap::after{content:'';position:absolute;width:400px;height:400px;background:radial-gradient(circle,rgba(96,165,250,.02),transparent 70%);bottom:10%;right:15%;pointer-events:none}
.login-box{background:linear-gradient(135deg,var(--card),var(--card2));border:1px solid var(--bd2);border-radius:28px;padding:48px 44px;width:420px;max-width:92vw;animation:fadeUp .6s cubic-bezier(.16,1,.3,1);position:relative;box-shadow:var(--shadowL);backdrop-filter:blur(20px)}
.login-box::before{content:'';position:absolute;top:0;left:20%;right:20%;height:1px;background:linear-gradient(90deg,transparent,var(--ac),transparent)}
.login-logo{width:54px;height:54px;background:linear-gradient(135deg,var(--ac),var(--ac2));border-radius:16px;display:flex;align-items:center;justify-content:center;margin:0 auto 20px;font-weight:900;color:#0a0d13;font-size:22px;animation:glow 4s ease-in-out infinite;box-shadow:0 8px 30px rgba(212,168,67,.15)}
.login-box h1{font-size:24px;font-weight:800;color:var(--wh);text-align:center;letter-spacing:-.04em}
.login-box .sub{font-size:12px;color:var(--mu);text-align:center;margin:6px 0 4px;font-weight:400}
.login-box .ver{font-size:10px;color:var(--dm);text-align:center;margin-bottom:24px;font-family:var(--m);letter-spacing:.05em}
.field{margin-bottom:14px}.field label{display:block;font-size:10px;font-weight:600;color:var(--mu);margin-bottom:6px;text-transform:uppercase;letter-spacing:.08em}.field input,.field select,.field textarea{width:100%}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:11px 22px;border-radius:12px;font-size:13px;font-weight:600;transition:all .25s cubic-bezier(.16,1,.3,1);letter-spacing:.01em;position:relative;overflow:hidden}
.btn::after{content:'';position:absolute;inset:0;background:linear-gradient(135deg,transparent 40%,rgba(255,255,255,.05) 50%,transparent 60%);opacity:0;transition:opacity .3s}
.btn:hover::after{opacity:1}
.btn-full{width:100%}
.btn-ac{background:linear-gradient(135deg,var(--ac),var(--ac2));color:#0a0d13;box-shadow:0 4px 16px rgba(212,168,67,.2)}.btn-ac:hover{filter:brightness(1.08);transform:translateY(-2px);box-shadow:0 8px 24px rgba(212,168,67,.3)}
.btn-gh{background:var(--glass);color:var(--tx);border:1px solid var(--bd2);backdrop-filter:blur(4px)}.btn-gh:hover{border-color:var(--bd3);background:var(--glassB);transform:translateY(-1px)}
.btn-sm{padding:8px 16px;font-size:12px;border-radius:var(--r3)}.btn-rd{background:var(--rdB);color:var(--rd);border:1px solid rgba(248,113,113,.1)}.btn-rd:hover{background:rgba(248,113,113,.12)}
.divider{display:flex;align-items:center;gap:14px;margin:20px 0;color:var(--dm);font-size:11px;font-weight:500}.divider::before,.divider::after{content:'';flex:1;height:1px;background:linear-gradient(90deg,transparent,var(--bd2),transparent)}
.sidebar{width:256px;background:linear-gradient(180deg,var(--bg2),var(--bg));border-right:1px solid var(--bd);display:flex;flex-direction:column;flex-shrink:0;position:relative}
.sidebar::after{content:'';position:absolute;top:0;right:0;bottom:0;width:1px;background:linear-gradient(180deg,transparent,rgba(212,168,67,.08),transparent)}
.sb-brand{padding:20px;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--bd)}
.sb-logo{width:36px;height:36px;background:linear-gradient(135deg,var(--ac),var(--ac2));border-radius:11px;display:flex;align-items:center;justify-content:center;font-weight:900;color:#0a0d13;font-size:16px;box-shadow:0 4px 12px rgba(212,168,67,.15);animation:float 6s ease-in-out infinite}
.sb-brand-name{font-size:16px;font-weight:800;color:var(--wh);letter-spacing:-.03em}.sb-brand-sub{font-size:8px;color:var(--dm);font-family:var(--m);letter-spacing:.06em;margin-top:1px}
.sb-ch{display:flex;gap:5px;padding:14px 18px;border-bottom:1px solid var(--bd)}
.sb-chip{flex:1;height:28px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:700;position:relative;transition:all .2s}
.sb-chip.on{opacity:1;box-shadow:0 2px 8px rgba(0,0,0,.2)}.sb-chip.off{opacity:.25}
.sb-chip-em{background:rgba(96,165,250,.08);color:var(--em);border:1px solid rgba(96,165,250,.1)}.sb-chip-sm{background:rgba(52,211,153,.08);color:var(--sm);border:1px solid rgba(52,211,153,.1)}
.sb-chip-wa{background:rgba(37,211,102,.08);color:var(--wa);border:1px solid rgba(37,211,102,.1)}.sb-chip-tg{background:rgba(34,158,217,.08);color:var(--tg);border:1px solid rgba(34,158,217,.1)}
.sb-chip .dot{position:absolute;top:3px;right:3px;width:5px;height:5px;border-radius:50%;box-shadow:0 0 6px currentColor}.dot-on{background:var(--gn)}.dot-off{background:var(--rd)}
.sb-nav{flex:1;overflow-y:auto;padding:8px 10px}
.sb-sec{padding:16px 12px 6px;font-size:8px;font-weight:700;color:var(--dm);text-transform:uppercase;letter-spacing:.14em}
.sb-it{display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:12px;cursor:pointer;font-size:12.5px;color:var(--mu);font-weight:500;transition:all .2s cubic-bezier(.16,1,.3,1);margin-bottom:2px;position:relative;border:1px solid transparent}
.sb-it:hover{background:rgba(255,255,255,.02);color:var(--tx);transform:translateX(2px)}
.sb-it.on{background:var(--acB);color:var(--ac);border-color:var(--acBd);box-shadow:0 0 20px var(--acG),inset 0 0 20px var(--acG)}
.sb-it.on::before{content:'';position:absolute;left:-10px;top:50%;transform:translateY(-50%);width:3px;height:20px;background:var(--ac);border-radius:0 3px 3px 0;box-shadow:0 0 10px var(--ac)}
.sb-it svg{width:16px;height:16px;flex-shrink:0;opacity:.45;transition:opacity .2s}.sb-it.on svg{opacity:.9}
.sb-it .cnt{margin-left:auto;font-size:9px;font-family:var(--m);background:var(--acB);color:var(--ac);padding:2px 7px;border-radius:10px;font-weight:600}
.sb-user{padding:14px 16px;border-top:1px solid var(--bd);display:flex;align-items:center;gap:10px;background:linear-gradient(180deg,transparent,rgba(212,168,67,.02))}
.sb-av{width:32px;height:32px;border-radius:10px;background:linear-gradient(135deg,var(--ac),var(--ac2));display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#0a0d13;flex-shrink:0;box-shadow:0 3px 10px rgba(212,168,67,.15)}
.sb-un{font-size:12px;font-weight:600;color:var(--wh)}.sb-ut{font-size:9px;color:var(--dm);font-family:var(--m)}
.sb-out{padding:6px;border-radius:8px;background:transparent;color:var(--dm);cursor:pointer;transition:all .2s;margin-left:auto}.sb-out:hover{background:var(--rdB);color:var(--rd);transform:scale(1.1)}
.main{flex:1;display:flex;flex-direction:column;overflow:hidden;background:linear-gradient(135deg,var(--bg),rgba(8,13,26,.95))}
.top{height:54px;border-bottom:1px solid var(--bd);display:flex;align-items:center;padding:0 28px;gap:14px;background:rgba(8,13,26,.6);backdrop-filter:blur(12px);flex-shrink:0;position:relative}
.top::after{content:'';position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--bd2),transparent)}
.top h2{font-size:17px;font-weight:700;color:var(--wh);flex:1;letter-spacing:-.02em}
.top-live{font-family:var(--m);font-size:9px;padding:5px 12px;border-radius:20px;display:flex;align-items:center;gap:6px;color:var(--gn);background:var(--gnB);border:1px solid rgba(52,211,153,.1);backdrop-filter:blur(4px)}
.top-live::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--gn);animation:pulse 2s infinite;box-shadow:0 0 8px var(--gn)}
.content{flex:1;overflow-y:auto;padding:28px;scroll-behavior:smooth}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:14px;margin-bottom:28px}
.stat{background:linear-gradient(135deg,var(--card),var(--card2));border:1px solid var(--bd);border-radius:var(--r2);padding:20px;position:relative;overflow:hidden;transition:all .3s cubic-bezier(.16,1,.3,1);cursor:default;animation:fadeUp .4s ease both}
.stat:nth-child(1){animation-delay:.05s}.stat:nth-child(2){animation-delay:.1s}.stat:nth-child(3){animation-delay:.15s}.stat:nth-child(4){animation-delay:.2s}.stat:nth-child(5){animation-delay:.25s}.stat:nth-child(6){animation-delay:.3s}
.stat:hover{border-color:var(--bd3);transform:translateY(-3px);box-shadow:var(--shadow)}
.stat::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;transition:height .3s}
.stat:hover::before{height:3px}
.stat::after{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:radial-gradient(circle at 50% 0%,rgba(255,255,255,.02),transparent 60%);pointer-events:none;opacity:0;transition:opacity .3s}
.stat:hover::after{opacity:1}
.s-ac::before{background:linear-gradient(90deg,var(--ac),var(--ac2))}.s-bl::before{background:var(--bl)}.s-gn::before{background:var(--gn)}.s-rd::before{background:var(--rd)}.s-pu::before{background:var(--pu)}.s-tg::before{background:var(--tg)}.s-or::before{background:var(--or)}
.st-l{font-size:10px;font-weight:600;color:var(--mu);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px}
.st-v{font-size:28px;font-weight:800;color:var(--wh);font-family:var(--m);letter-spacing:-.04em;line-height:1;text-shadow:0 2px 10px rgba(0,0,0,.3)}
.st-s{font-size:10px;margin-top:8px;font-weight:500;color:var(--mu)}.st-s .up{color:var(--gn)}.st-s .dn{color:var(--rd)}
.card{background:linear-gradient(135deg,var(--card),var(--card2));border:1px solid var(--bd);border-radius:var(--r2);padding:22px;margin-bottom:16px;transition:all .3s;animation:fadeUp .4s ease both;position:relative;overflow:hidden}
.card:hover{border-color:var(--bd2)}
.card-h{display:flex;align-items:center;justify-content:space-between;margin-bottom:18px}
.card-t{font-size:15px;font-weight:700;color:var(--wh);letter-spacing:-.01em}
.badge{display:inline-flex;align-items:center;gap:4px;padding:4px 11px;border-radius:20px;font-size:9px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;backdrop-filter:blur(4px)}
.b-ok{background:var(--gnB);color:var(--gn);border:1px solid rgba(52,211,153,.08)}.b-sent{background:var(--gnB);color:var(--gn)}.b-wait{background:rgba(251,146,60,.06);color:var(--or);border:1px solid rgba(251,146,60,.08)}.b-err{background:var(--rdB);color:var(--rd)}.b-em{background:var(--blB);color:var(--bl);border:1px solid rgba(96,165,250,.08)}.b-ac{background:var(--acB);color:var(--ac);border:1px solid var(--acBd)}
.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.6);backdrop-filter:blur(8px);z-index:100;display:flex;align-items:center;justify-content:center;animation:fadeIn .15s}
.modal{background:linear-gradient(135deg,var(--card),var(--card2));border:1px solid var(--bd2);border-radius:24px;padding:32px;width:560px;max-width:92vw;max-height:85vh;overflow-y:auto;box-shadow:var(--shadowL);animation:fadeUp .3s cubic-bezier(.16,1,.3,1)}
.modal h3{font-size:18px;font-weight:700;color:var(--wh);margin-bottom:20px;letter-spacing:-.02em}.modal .brow{display:flex;gap:10px;margin-top:20px}
.ch-sel{display:flex;gap:7px;margin:10px 0}.ch-p{padding:8px 16px;border-radius:20px;font-size:11px;font-weight:600;cursor:pointer;transition:all .25s;border:1px solid var(--bd2);background:var(--glass);color:var(--mu);backdrop-filter:blur(4px)}
.ch-p:hover{border-color:var(--bd3);transform:scale(1.02)}
.ch-p.sel{border-color:currentColor;transform:scale(1.02)}.ch-p[data-c=email].sel{background:var(--blB);color:var(--em)}.ch-p[data-c=sms].sel{background:var(--gnB);color:var(--sm)}.ch-p[data-c=whatsapp].sel{background:rgba(37,211,102,.06);color:var(--wa)}.ch-p[data-c=telegram].sel{background:rgba(34,158,217,.06);color:var(--tg)}
.chat-w{display:flex;flex-direction:column;height:100%}.chat-m{flex:1;overflow-y:auto;padding:0 4px 14px}
.chat-e{text-align:center;padding:60px 20px;color:var(--dm)}.chat-e h3{color:var(--mu);margin-top:12px;font-size:16px;font-weight:600}
.chat-u{background:linear-gradient(135deg,var(--acB),rgba(212,168,67,.03));border:1px solid var(--acBd);border-radius:var(--r) var(--r) 4px var(--r);padding:14px 18px;margin:8px 0;max-width:75%;margin-left:auto;font-size:13px;color:var(--wh);line-height:1.6;animation:fadeUp .2s ease}
.chat-a{background:linear-gradient(135deg,var(--sf),var(--card));border:1px solid var(--bd);border-radius:4px var(--r) var(--r) var(--r);padding:16px 20px;margin:8px 0;max-width:85%;font-size:13px;line-height:1.7;animation:fadeUp .2s ease}
.chat-a pre{background:var(--bg);padding:12px;border-radius:var(--r3);overflow-x:auto;font-family:var(--m);font-size:12px;margin:8px 0;border:1px solid var(--bd)}
.chat-a code{font-family:var(--m);font-size:12px;background:var(--bg);padding:2px 6px;border-radius:4px;border:1px solid var(--bd)}
.chat-b{display:flex;gap:10px;padding:14px 0 0;border-top:1px solid var(--bd)}.chat-b textarea{flex:1;resize:none;min-height:44px;max-height:120px;font-size:13px;line-height:1.5;border-radius:14px}
.ldot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--ac);margin:0 3px;animation:pulse .8s infinite}.ldot:nth-child(2){animation-delay:.15s}.ldot:nth-child(3){animation-delay:.3s}
.spinner{width:20px;height:20px;border:2px solid var(--bd2);border-top-color:var(--ac);border-radius:50%;animation:spin .6s linear infinite;display:inline-block}
.toolbar{display:flex;align-items:center;gap:10px;margin-bottom:18px;flex-wrap:wrap;animation:fadeUp .3s ease}.toolbar .sp{flex:1}
.toolbar input[type=search]{width:280px;background:var(--sf);padding-left:38px;border-radius:14px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%235a6a80' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:13px center}
.toast{position:fixed;bottom:24px;right:24px;background:linear-gradient(135deg,var(--card),var(--card2));color:var(--wh);padding:14px 24px;border-radius:16px;font-size:13px;font-weight:500;z-index:9999;display:none;border:1px solid var(--bd2);box-shadow:var(--shadowL);animation:toastIn .3s cubic-bezier(.16,1,.3,1);backdrop-filter:blur(12px)}
.toast.err{border-color:rgba(248,113,113,.15);color:var(--rd)}
.tags{display:flex;flex-wrap:wrap;gap:5px}.tag{padding:3px 10px;border-radius:14px;font-size:9px;font-weight:600;background:var(--glass);border:1px solid var(--bd);color:var(--mu);backdrop-filter:blur(4px)}
.ed{width:100%;min-height:180px;font-family:var(--m);font-size:12px;line-height:1.6;background:var(--bg);border-radius:var(--r3);padding:16px;resize:vertical;border:1px solid var(--bd2)}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:18px}
@media(max-width:768px){.sidebar{width:56px}.sb-brand-name,.sb-brand-sub,.sb-sec,.sb-it span,.sb-un,.sb-ut,.sb-ch{display:none}.sb-it{justify-content:center;padding:10px}.content{padding:16px}.row2{grid-template-columns:1fr}}
</style><script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/grapesjs/0.21.13/css/grapes.min.css">
</head><body>
<div class="login-wrap" id="LW"><div class="login-box"><div class="login-logo" style="padding:6px;background:none;animation:none"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" style="width:100%;height:100%"><defs><linearGradient id="wg" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#d4a843"/><stop offset="100%" stop-color="#e8c55a"/></linearGradient></defs><rect width="48" height="48" rx="12" fill="url(#wg)"/><path d="M12 16l12 9 12-9" stroke="#0a0d13" stroke-width="2.2" fill="none" stroke-linecap="round"/><rect x="12" y="15" width="24" height="18" rx="3" stroke="#0a0d13" stroke-width="1.5" fill="none" opacity=".5"/><text x="36" y="14" text-anchor="middle" fill="#0a0d13" font-size="10" font-weight="800" font-family="system-ui">IA</text></svg></div><h1>WEVADS IA</h1><p class="sub">Omnichannel Intelligence Platform</p><p class="ver">v3.0 — Sovereign Engine · 344 APIs · 688 Tables</p>
<div id="LF"><div class="field"><label>Email</label><input type="email" id="lE" placeholder="you@company.com"></div><div class="field"><label>Mot de passe</label><input type="password" id="lP" onkeydown="if(event.key==='Enter')doLogin()"></div><button class="btn btn-ac btn-full" onclick="doLogin()" id="bL">Se connecter</button><div class="divider">ou</div><button class="btn btn-gh btn-full" onclick="sR()">Créer un compte</button></div>
<div id="RF" style="display:none"><div class="field"><label>Nom</label><input id="rN"></div><div class="field"><label>Email</label><input type="email" id="rE"></div><div class="field"><label>Entreprise</label><input id="rC"></div><div class="field"><label>Mot de passe</label><input type="password" id="rP" onkeydown="if(event.key==='Enter')doReg()"></div><button class="btn btn-ac btn-full" onclick="doReg()" id="bR">Créer</button><div class="divider">ou</div><button class="btn btn-gh btn-full" onclick="sL()">Connexion</button></div>
</div></div>
<div class="sidebar" id="SB" style="display:none"><div class="sb-brand"><div class="sb-logo" style="padding:4px;background:none"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" style="width:100%;height:100%"><defs><linearGradient id="wg" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#d4a843"/><stop offset="100%" stop-color="#e8c55a"/></linearGradient></defs><rect width="48" height="48" rx="12" fill="url(#wg)"/><path d="M12 16l12 9 12-9" stroke="#0a0d13" stroke-width="2.2" fill="none" stroke-linecap="round"/><rect x="12" y="15" width="24" height="18" rx="3" stroke="#0a0d13" stroke-width="1.5" fill="none" opacity=".5"/><text x="36" y="14" text-anchor="middle" fill="#0a0d13" font-size="10" font-weight="800" font-family="system-ui">IA</text></svg></div><div><span class="sb-brand-name">WEVADS IA</span><br><span class="sb-brand-sub">v3.0 OMNICHANNEL</span></div></div>
<div class="sb-ch"><div class="sb-chip sb-chip-em on"><span class="dot dot-on"></span></div><div class="sb-chip sb-chip-sm off"><span class="dot dot-off"></span>SMS</div><div class="sb-chip sb-chip-wa off"><span class="dot dot-off"></span>WA</div><div class="sb-chip sb-chip-tg on"><span class="dot dot-on"></span>TG</div></div>
<div class="sb-nav" id="SN"></div>
<div class="sb-user"><div class="sb-av" id="sA">W</div><div style="flex:1;min-width:0"><div class="sb-un" id="sU"></div><div class="sb-ut" id="sT">admin</div></div><div class="sb-out" onclick="logout()" title="Déconnexion"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg></div></div></div>
<div class="main" id="MA" style="display:none"><div class="top"><h2 id="PT">Dashboard</h2><div class="top-live" id="TL">Backend Live</div></div><div class="content" id="C"></div></div>
<div class="toast" id="T"></div>
<script>
const API='/api/v2',V2E='/api/wevads-v2-engine.php',BRG='/api/adx-bridge.php',WEVIA='/api/weval-ia-fast.php',SE='/api/send-engine-unified.php';
let TK='',U={},PG='dashboard';
const $=id=>document.getElementById(id),v=id=>$(id)?$(id).value.trim():'';
const P=[
['dashboard','Dashboard','M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 14h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7z','Principal'],
['send','⚡ Envoyer','M13 2L3 14h9l-1 8 10-12h-9l1-8z','Principal'],
['campaigns','Campagnes','M2 4h20v16H2V4zm0 4h20M2 8l10 5 10-5','Principal'],
['contacts','Contacts','M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2M9 7a4 4 0 1 0 0-8 4 4 0 0 0 0 8M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75','Principal'],
['senders','Senders','M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2zM22 6l-10 7L2 6','Email Ops'],
['warmup','Warmup','M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83','Email Ops'],
['domains','Domaines','M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zM2 12h20M12 2a15 15 0 0 1 4 10 15 15 0 0 1-4 10 15 15 0 0 1-4-10 15 15 0 0 1 4-10z','Email Ops'],
['seeds','Seeds','M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z','Email Ops'],
['reputation','Réputation','M9 12l2 2 4-4M20.6 6.7A9.94 9.94 0 0 0 12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10c0-1.5-.3-2.9-.9-4.2','Email Ops'],
['brain','Brain IA','M4 4h16v16H4V4zM9 9h6v6H9V9zM9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3','Intelligence'],
['creative','Creative','M12 20h9M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z','Intelligence'],
['analytics','Analytics','M18 20V10M12 20V4M6 20v-6','Intelligence'],
['ai','AI Assistant','M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z','Intelligence'],
['channels','Canaux','M12 12m-2 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14','Omnichannel'],
['templates','Templates','M3 3h18v18H3V3zM3 9h18M9 21V9','Omnichannel'],
['offers','Offres','M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82zM7 7h.01','Business'],
['sponsors','Sponsors','M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2M9 7a4 4 0 1 0 0-8 4 4 0 0 0 0 8M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75','Business'],
['servers','Serveurs','M22 12h-6l-2 3h-4l-2-3H2M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z','Système'],
['infra','Infrastructure','M22 12h-6l-2 3h-4l-2-3H2M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z','Système'],
['settings','Paramètres','M12 12m-3 0a3 3 0 1 0 6 0 3 3 0 1 0-6 0M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42','Système'],
];
function ico(d){return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${d}"/><\/svg>`}
function toast(m,e){const t=$('T');t.textContent=m;t.className=e?'toast err':'toast';t.style.display='block';setTimeout(()=>t.style.display='none',3500)}
function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML}
function N(n){return new Intl.NumberFormat('fr-FR').format(n||0)}
function D(d){return d?new Date(d).toLocaleDateString('fr-FR',{day:'2-digit',month:'short',year:'numeric'}):''}
function md2h(t){return t.replace(/```[\w]*\n([\s\S]*?)```/g,'<pre>$1<\/pre>').replace(/\*\*(.*?)\*\*/g,'<strong>$1<\/strong>').replace(/`(.*?)`/g,'<code>$1<\/code>').replace(/\n\n/g,'<br><br>')}
async function api(p,o={}){const h={'Content-Type':'application/json'};if(TK)h['Authorization']='Bearer '+TK;const r=await fetch(API+p,{...o,headers:h});return r.json()}
async function eng(a){return(await fetch(V2E+'?action='+a+'&token=WEVADS2026')).json()}
async function brg(a){return(await fetch(BRG+'?action='+a+'&token=WEVADS2026')).json()}
function sR(){$('LF').style.display='none';$('RF').style.display=''}
function sL(){$('RF').style.display='none';$('LF').style.display=''}
async function doLogin(){let e=v('lE'),p=v('lP');if(e&&!e.includes('@'))e+='@weval-consulting.com';if(!e||!p)return toast('Requis',1);$('bL').textContent='...';try{const r=await api('/auth/login',{method:'POST',body:JSON.stringify({email:e,password:p})});TK=r.token;U=r.user;enter()}catch(x){toast(x.message,1)}finally{$('bL').textContent='Se connecter'}}
async function doReg(){const n=v('rN'),e=v('rE'),p=v('rP');if(!n||!e||!p)return toast('Requis',1);try{const r=await api('/auth/register',{method:'POST',body:JSON.stringify({name:n,email:e,company:v('rC'),password:p})});TK=r.token;U=r.user;enter()}catch(x){toast(x.message,1)}}
function enter(){try{sessionStorage.setItem('wt',TK);sessionStorage.setItem('wu',JSON.stringify(U))}catch(e){}$('LW').style.display='none';$('SB').style.display='flex';$('MA').style.display='flex';$('sU').textContent=U.name||U.email;$('sA').textContent=(U.name||'W')[0].toUpperCase();$('sT').textContent=U.role||'user';rnav();go('dashboard')}
function logout(){TK='';U={};try{sessionStorage.clear()}catch(e){}$('LW').style.display='flex';$('SB').style.display='none';$('MA').style.display='none'}
function rnav(){let h='',s='';P.forEach(p=>{if(p[3]!==s){s=p[3];h+=`<div class="sb-sec">${s}<\/div>`}h+=`<div class="sb-it ${PG===p[0]?'on':''}" onclick="go('${p[0]}')">${ico(p[2])}<span>${p[1]}<\/span><\/div>`});$('SN').innerHTML=h}
function go(p){PG=p;$('PT').textContent=P.find(x=>x[0]===p)?.[1]||p;rnav();const c=$('C');c.innerHTML='<div style="text-align:center;padding:40px"><div class="spinner"><\/div><\/div>';c.scrollTop=0;(RR[p]||RR.dashboard)()}
const RR={};
// =============== DASHBOARD ===============
RR.dashboard=async()=>{let k={},t={},c=[];try{k=(await eng('dashboard')).kpis||{}}catch(e){}try{t=(await eng('tracking_stats')).stats||{}}catch(e){}try{c=(await api('/campaigns/list')).items||[]}catch(e){}const rs=(await eng('dashboard').catch(()=>({}))).recent_sends||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac" onclick="go('contacts')" style="cursor:pointer"><div class="st-l">Contacts<\/div><div class="st-v">${N(k.contacts_total)}<\/div><div class="st-s">${N(k.contacts_active)} actifs<\/div><\/div><div class="stat s-bl" onclick="drillKPI('sent')" style="cursor:pointer"><div class="st-l">Emails envoyés<\/div><div class="st-v">${N(k.emails_sent_total)}<\/div><div class="st-s">${k.emails_sent_today||0} today<\/div><\/div><div class="stat s-gn" onclick="go('senders')" style="cursor:pointer"><div class="st-l">Senders<\/div><div class="st-v">${N(k.senders_active)}<\/div><div class="st-s">${k.senders_warming||0} warming<\/div><\/div><div class="stat s-pu" onclick="drillKPI('tracking')" style="cursor:pointer"><div class="st-l">Opens / Clicks<\/div><div class="st-v">${N(t.total_opens)}<\/div><div class="st-s">${N(t.total_clicks)} clicks<\/div><\/div><div class="stat s-or" onclick="go('warmup')" style="cursor:pointer"><div class="st-l">Warmup<\/div><div class="st-v">${N(k.warmup_accounts)}<\/div><\/div><div class="stat s-tg" onclick="go('channels')" style="cursor:pointer"><div class="st-l">Canaux<\/div><div class="st-v">4<\/div><div class="st-s"><span class="up">Email+TG<\/span><\/div><\/div><\/div>
<div class="row2"><div class="card"><div class="card-h"><div class="card-t">Derniers envois<\/div><button class="btn btn-sm btn-ac" onclick="go('send')">+ Envoyer<\/button><\/div><table><thead><tr><th>Sender<\/th><th>To<\/th><th>Sujet<\/th><th>ISP<\/th><th>Date<\/th><\/tr><\/thead><tbody>${rs.slice(0,8).map(s=>`<tr><td style="font-family:var(--m);font-size:10px;color:var(--mu)">${esc((s.sender||'').split('@')[0])}<\/td><td style="font-family:var(--m);font-size:10px">${esc(s.recipient||'')}<\/td><td style="color:var(--wh);font-size:11px">${esc((s.subject||'').substring(0,30))}<\/td><td><span class="badge b-em">${esc(s.isp||'')}<\/span><\/td><td style="font-size:10px;color:var(--dm)">${(s.created_at||'').substring(0,16)}<\/td><\/tr>`).join('')||'<tr><td colspan="5" style="text-align:center;color:var(--dm);padding:24px">Aucun envoi<\/td><\/tr>'}<\/tbody><\/table><\/div>
<div><div class="card"><div class="card-t" style="margin-bottom:10px">Infra<\/div>${[['Campagnes',k.campaigns_total,'('+k.campaigns_active+' actives)'],['Domaines',k.domains_active,''],['Bounces',k.bounces,''],['Tracking',N(k.tracking_events),'events'],['Ethica HCPs',N(k.ethica_hcps),'pharma']].map(([l,v2,s])=>`<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">${l}<\/span><span style="font-family:var(--m);font-size:11px;color:var(--wh)">${v2||0} <span style="color:var(--dm);font-size:9px">${s}<\/span><\/span><\/div>`).join('')}<\/div>
<div class="card"><div class="card-t" style="margin-bottom:8px">Actions<\/div><div style="display:grid;gap:5px"><button class="btn btn-sm btn-ac btn-full" onclick="go('send')">⚡ Envoyer<\/button><button class="btn btn-sm btn-gh btn-full" onclick="go('senders')">Senders<\/button><button class="btn btn-sm btn-gh btn-full" onclick="go('ai')">🧠 AI Assistant<\/button><\/div><\/div><\/div><\/div>`;
(async function(){try{var ch=await brg('charts_daily');var stats=(ch||{}).stats||[];
if(typeof Chart!=='undefined'&&stats.length>1){
var wrap=document.createElement('div');wrap.className='row2';wrap.innerHTML='<div class="card"><div class="card-t" style="margin-bottom:14px">Volume 30 jours</div><canvas id="chD1" height="200"></canvas></div><div class="card"><div class="card-t" style="margin-bottom:14px">Opens & Clicks</div><canvas id="chD2" height="200"></canvas></div>';
if(PG==='dashboard')$('C').appendChild(wrap);
setTimeout(function(){
var e1=document.getElementById('chD1');var e2=document.getElementById('chD2');
if(e1)new Chart(e1,{type:'line',data:{labels:stats.map(function(s){return(s.date||'').substring(5)}),datasets:[{label:'Sent',data:stats.map(function(s){return+s.total_sent}),borderColor:'#d4a843',tension:.3,pointRadius:2,fill:false},{label:'Delivered',data:stats.map(function(s){return+s.total_delivered}),borderColor:'#34d399',tension:.3,pointRadius:2,fill:false}]},options:{responsive:true,plugins:{legend:{labels:{color:'#5a6a80',font:{size:10}}}},scales:{x:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.03)'}},y:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.03)'}}}}});
if(e2)new Chart(e2,{type:'bar',data:{labels:stats.map(function(s){return(s.date||'').substring(5)}),datasets:[{label:'Opens',data:stats.map(function(s){return+s.total_opened}),backgroundColor:'rgba(52,211,153,.5)',borderRadius:4},{label:'Clicks',data:stats.map(function(s){return+s.total_clicked}),backgroundColor:'rgba(96,165,250,.5)',borderRadius:4}]},options:{responsive:true,plugins:{legend:{labels:{color:'#5a6a80',font:{size:10}}}},scales:{x:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.03)'}},y:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.03)'}}}}});
},200)}}catch(e){}})();};
// =============== SEND ===============
RR.send=async()=>{
let m=[],ctrl={},sc={};
try{m=(await eng('send_methods')).items||[]}catch(e){}
try{ctrl=await(await fetch('/api/send-controller.php?action=status&token=WEVADS2026')).json()}catch(e){}
try{sc=await(await fetch('/api/send-controller.php?action=seed_check&token=WEVADS2026')).json()}catch(e){}
const sf=ctrl.safety||{};const gr=ctrl.graph||{};const cp=ctrl.campaigns||{};const scr=sc.recent||[];
$('C').innerHTML=`
<div style="display:flex;gap:8px;margin-bottom:16px;padding:12px;background:var(--sf);border:1px solid ${sf.dangerous_crons_disabled?'rgba(52,211,153,.15)':'rgba(248,113,113,.2)'};border-radius:var(--r)">
<span style="font-size:16px">${sf.dangerous_crons_disabled?'\u2705':'\u26a0\ufe0f'}<\/span>
<div style="flex:1"><div style="font-size:12px;font-weight:600;color:${sf.dangerous_crons_disabled?'var(--gn)':'var(--rd)'}">Send Controller v1.0 — ${sf.dangerous_crons_disabled?'SÉCURISÉ':'DANGER'}<\/div>
<div style="font-size:10px;color:var(--mu)">Crons dangereux dsactivs \u2022 Rate: ${sf.rate_this_hour||0}/${sf.max_per_hour||100}/h \u2022 Tous les envois passent par cette page<\/div><\/div>
<button class="btn btn-sm btn-gh" id="autoBtn" onclick="toggleAuto()" style="color:var(--ac);border-color:var(--acBd)">${ctrl.auto_mode?'\u23f8 Auto OFF':'\u25b6 Auto ON'}<\/button>
<\/div>
<div class="card" style="border-color:var(--acBd)"><div class="card-h"><div class="card-t">\u26a1 Composer & Envoyer<\/div><\/div>
<div class="row2"><div>
<div class="field"><label>Canal<\/label><div class="ch-sel">
<div class="ch-p sel" data-c="email" onclick="this.classList.toggle('sel')">\u2709 Email<\/div>
<div class="ch-p" data-c="sms" onclick="this.classList.toggle('sel')">\ud83d\udcac SMS<\/div>
<div class="ch-p" data-c="whatsapp" onclick="this.classList.toggle('sel')">\ud83d\udcf1 WhatsApp<\/div>
<div class="ch-p" data-c="telegram" onclick="this.classList.toggle('sel')">\u2708 Telegram<\/div>
<\/div><\/div>
<div class="field"><label>Mthode<\/label><select id="sM">
<option value="pmta">PMTA Legacy (S95:25)<\/option><option value=\"kumomta\" selected>KumoMTA (Rust :587)<\/option>
<option value="postfix">Postfix Local (2525)<\/option>
<option value="graph">Graph API (167 senders)<\/option>
${m.map(x=>'<option value="'+x.name+'">'+x.name+' \u2014 '+x.type+'<\/option>').join('')}
<option value="Gmail_Direct">Gmail (55 accts, 27K/day)<\/option>
<option value="SendGrid">SendGrid (50 accts)<\/option>
<option value="Amazon_SES">Amazon SES (30K/day)<\/option>
<option value="Brevo">Brevo (30 accts)<\/option>
<option value="SparkPost">SparkPost (20 accts)<\/option>
<\/select><\/div>
<div class="field"><label>Destinataire(s)<\/label><input id="sTo" placeholder="email@example.com (virgules pour bulk)"><\/div>
<div class="field"><label>Objet<\/label><input id="sSu" placeholder="Objet du message"><\/div>
<div style="display:flex;gap:6px;margin:6px 0">
<button class="btn btn-sm btn-gh" style="color:var(--pu);border-color:rgba(167,139,250,.2)" onclick="aiSubj()">\ud83e\udde0 Optimiser IA<\/button>
<span id="aiH" style="font-size:10px;color:var(--dm)"><\/span>
<\/div><\/div>
<div>
<div class="field"><label>From Name<\/label><input id="sFN" value="WEVAL Consulting"><\/div>
<div class="field"><label>From Email<\/label><input id="sFE" placeholder="Auto-select sender"><\/div>
<div class="field"><label>Limite par envoi<\/label><select id="sLim"><option value="1">1 (test)<\/option><option value="5">5<\/option><option value="10">10 (max safe)<\/option><\/select><\/div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:10px">
<button class="btn btn-ac" onclick="doSendCtrl()" style="font-size:12px">\ud83e\uddea Test Send<\/button>
<button class="btn btn-gh" style="color:var(--gn);border-color:rgba(52,211,153,.2);font-size:12px" onclick="doSeedTest()">\ud83c\udf31 Seed Test<\/button>
<button class="btn btn-gh" style="color:var(--or);border-color:rgba(251,146,60,.2);font-size:12px" onclick="doBulkCtrl()">\ud83d\ude80 Bulk<\/button>
<button class="btn btn-gh" style="color:var(--tg);border-color:rgba(34,158,217,.2);font-size:12px" onclick="doTgSend()">\u2708 Telegram<\/button>
<\/div><\/div><\/div>
<div class="field" style="margin-top:12px"><label>HTML Body<\/label>
<div style="display:flex;gap:6px;margin-bottom:6px">
<button class="btn btn-sm btn-gh" onclick="aiBody()">\ud83e\udde0 IA<\/button>
<button class="btn btn-sm btn-gh" onclick="loadTpl()">\ud83d\udccb Template<\/button>
<\/div>
<textarea class="ed" id="sBo" rows="6" placeholder="<h1>Hello<\/h1>"><\/textarea><\/div>
<div class="field"><label>Custom Headers (optionnel)<\/label><input id="sHdr" placeholder="X-Mailer: WEVADS, X-Priority: 1"><\/div>
<\/div>
<div class="row2">
<div class="card"><div class="card-t" style="margin-bottom:10px">\ud83d\udee1 Scurit<\/div>
<div style="display:grid;gap:6px">
<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">Rate limit<\/span><span style="font-family:var(--m);color:var(--wh)">${sf.rate_this_hour||0} / ${sf.max_per_hour||100}<\/span><\/div>
<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">Graph senders<\/span><span style="font-family:var(--m);color:var(--ac)">${gr.senders_available||0}<\/span><\/div>
<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">Seeds DB<\/span><span style="font-family:var(--m)">${N(gr.seeds_total||0)}<\/span><\/div>
<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">Total envoys (alltime)<\/span><span style="font-family:var(--m)">${N(gr.total_sent_alltime||0)}<\/span><\/div>
<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">Envoys today<\/span><span style="font-family:var(--m);color:var(--gn)">${gr.sent_today||0}<\/span><\/div>
<div style="display:flex;justify-content:space-between;padding:5px 0"><span style="font-size:11px;color:var(--mu)">Dernier envoi<\/span><span style="font-family:var(--m);font-size:10px;color:var(--dm)">${(gr.last_send||'jamais').substring(0,16)}<\/span><\/div>
<\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">\ud83d\udce8 Campagnes ADX<\/div>
<div style="display:grid;gap:6px">
<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">Active<\/span><span style="font-family:var(--m);color:var(--gn)">${cp.active||0}<\/span><\/div>
<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">Paused<\/span><span style="font-family:var(--m);color:var(--or)">${cp.paused||0}<\/span><\/div>
<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">Queue<\/span><span style="font-family:var(--m)">${cp.queue||0}<\/span><\/div>
<div style="display:flex;gap:6px;margin-top:6px">
<button class="btn btn-sm btn-rd" onclick="pauseAll()" style="flex:1;font-size:10px">\u23f8 Pause All<\/button>
<button class="btn btn-sm btn-gh" onclick="listCamps()" style="flex:1;font-size:10px">\ud83d\udccb Liste<\/button>
<\/div><\/div><\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">\ud83d\udcca Derniers envois seeds (Graph API)<\/div>
<table><thead><tr><th>Sender<\/th><th>To<\/th><th>ISP<\/th><th>Sujet<\/th><th>Date<\/th><\/tr><\/thead>
<tbody>${scr.slice(0,6).map(s=>'<tr><td style="font-family:var(--m);font-size:9px;color:var(--mu)">'+esc((s.sender_email||'').split('@')[0])+'<\/td><td style="font-family:var(--m);font-size:9px">'+esc(s.recipient_email||'')+'<\/td><td><span class="badge b-em">'+esc(s.recipient_isp||'')+'<\/span><\/td><td style="font-size:10px;color:var(--wh)">'+esc((s.subject||'').substring(0,25))+'<\/td><td style="font-size:9px;color:var(--dm)">'+esc((s.created_at||'').substring(0,16))+'<\/td><\/tr>').join('')||'<tr><td colspan="5" style="text-align:center;color:var(--dm);padding:16px">Aucun envoi rcent<\/td><\/tr>'}<\/tbody><\/table><\/div>`;
// Preview panel
var prevBtn=document.createElement('button');prevBtn.className='btn btn-sm btn-gh';prevBtn.textContent='Preview';
prevBtn.onclick=async function(){
var su=document.getElementById('sSu')?document.getElementById('sSu').value:'';
var bo=document.getElementById('sBo')?document.getElementById('sBo').value:'';
var fn=document.getElementById('sFN')?document.getElementById('sFN').value:'WEVAL';
if(!bo){toast('Entrer du HTML pour preview',1);return}
var r=await(await fetch(BRG+'?action=preview&token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({subject:su,html:bo,from_name:fn})})).json();
if(r.ok){var m=document.createElement('div');m.className='modal-bg';m.onclick=function(e){if(e.target===m)m.remove()};
m.innerHTML='<div class="modal" style="max-width:700px;padding:20px"><div style="display:flex;gap:10px;margin-bottom:14px"><button class="btn btn-sm btn-ac" onclick="this.parentElement.nextElementSibling.style.maxWidth=\'600px\'">Desktop</button><button class="btn btn-sm btn-gh" onclick="this.parentElement.nextElementSibling.style.maxWidth=\'375px\'">Mobile</button><button class="btn btn-sm btn-gh" onclick="this.closest(\'.modal-bg\').remove()">Fermer</button></div><div style="max-width:600px;margin:0 auto;transition:max-width .3s">'+r.preview+'</div></div>';
document.body.appendChild(m)}};
var cards=$('C').querySelectorAll('.card');
if(cards.length>0){var hdr=cards[0].querySelector('.card-h');
if(hdr){hdr.appendChild(prevBtn)}}
// Schedule widget
var schedDiv=document.createElement('div');schedDiv.style.cssText='margin-top:12px;padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);display:flex;gap:12px;align-items:center';
schedDiv.innerHTML='<span style="font-size:11px;color:var(--mu)">Programmer:</span><input type="datetime-local" id="schedAt" style="font-size:12px;background:var(--bg);color:var(--wh);border:1px solid var(--bd);border-radius:6px;padding:4px 8px"><button class="btn btn-sm btn-gh" onclick="schedSend()">Planifier</button>';
var content=document.getElementById('C');
if(content)content.appendChild(schedDiv);
};
async function doSendCtrl(){
const to=v('sTo'),su=v('sSu'),bo=v('sBo'),me=v('sM'),lim=v('sLim');
if(!to||!su)return toast('To + Objet requis',1);
toast('Envoi via Controller ('+me+')...');
try{const r=await fetch('/api/send-controller.php?action=seed_test&token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'to='+encodeURIComponent(to)+'&subject='+encodeURIComponent(su)+'&html='+encodeURIComponent(bo)+'&method='+me+'&from_name='+encodeURIComponent(v('sFN'))+'&limit='+lim});
const j=await r.json();if(j.ok){const res=j.results||[];const ok=res.filter(r=>r.status==='sent').length;toast('\u2705 '+ok+'/'+res.length+' envoys via '+me+' (rate: '+j.rate+')')}else toast('\u274c '+(j.error||'Erreur'),1)}catch(e){toast('Err: '+e.message,1)}}
async function doSeedTest(){
const me=v('sM'),lim=v('sLim');
toast('Seed test ('+lim+' seeds via '+me+')...');
try{const r=await fetch('/api/send-controller.php?action=seed_test&token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'method='+me+'&limit='+lim+'&subject='+encodeURIComponent(v('sSu')||'Seed Test')+'&html='+encodeURIComponent(v('sBo')||'<p>Seed test<\/p>')+'&from_name='+encodeURIComponent(v('sFN'))});
const j=await r.json();toast(j.ok?'\u2705 Seeds: '+(j.results||[]).length+' (rate: '+j.rate+')':'\u274c '+(j.error||''),!j.ok)}catch(e){toast('Err',1)}}
async function doBulkCtrl(){const to=v('sTo');const emails=to.split(',').map(e=>e.trim()).filter(Boolean);if(emails.length<2)return toast('Bulk = 2+ emails',1);doSendCtrl()}
async function doTgSend(){const su=v('sSu')||'Alert';const bo=v('sBo')||'Test';toast('TG...');try{const r=await(await fetch('https://api.telegram.org/bot8544624912:AAEm9ttXK6JeFqAL-gcvB5sreCBhXzzQwrs/sendMessage',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({chat_id:'7605775322',text:'\ud83d\udce8 '+su+'\n'+bo.replace(/<[^>]*>/g,'')})})).json();toast(r.ok?'TG OK':'Err',!r.ok)}catch(e){toast('Err',1)}}
async function toggleAuto(){try{const r=await(await fetch('/api/send-controller.php?action=auto_toggle&token=WEVADS2026',{method:'POST'})).json();toast(r.auto_mode?'\u25b6 Auto ON (5 seeds/2h via PMTA)':'\u23f8 Auto OFF');RR.send()}catch(e){toast('Err',1)}}
async function pauseAll(){try{await fetch('/api/send-controller.php?action=campaign_pause&token=WEVADS2026',{method:'POST'});toast('\u23f8 Toutes les campagnes pauses');RR.send()}catch(e){toast('Err',1)}}
async function listCamps(){try{const r=await(await fetch('/api/send-controller.php?action=campaigns_list&token=WEVADS2026')).json();const c=r.campaigns||[];const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};m.innerHTML='<div class="modal"><h3>Campagnes ADX<\/h3><table><thead><tr><th>ID<\/th><th>Nom<\/th><th>Status<\/th><th>Action<\/th><\/tr><\/thead><tbody>'+c.map(x=>'<tr><td style="font-family:var(--m)">'+x.id+'<\/td><td style="color:var(--wh)">'+esc(x.name||'Campaign '+x.id)+'<\/td><td><span class="badge b-'+(x.status==='active'?'ok':x.status==='paused'?'wait':'em')+'">'+x.status+'<\/span><\/td><td><button class="btn btn-sm btn-gh" onclick="resumeCamp('+x.id+')">Resume<\/button><\/td><\/tr>').join('')+'<\/tbody><\/table><\/div>';document.body.appendChild(m)}catch(e){toast('Err',1)}}
async function resumeCamp(id){try{await fetch('/api/send-controller.php?action=campaign_resume&token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'campaign_id='+id});toast('Campaign '+id+' resumed');document.querySelector('.modal-bg')?.remove();RR.send()}catch(e){toast('Err',1)}}
// =============== CAMPAIGNS ===============
let CMP=[];
RR.campaigns=async()=>{try{CMP=(await api('/campaigns/list')).items||[]}catch(e){CMP=[]}
$('C').innerHTML=`<div class="toolbar"><input type="search" id="cS" placeholder="Rechercher..." oninput="fCmp()"><div class="sp"><\/div><button class="btn btn-sm btn-ac" onclick="oCmp()">+ Campagne<\/button><\/div><div class="card" style="padding:0;overflow:hidden"><table><thead><tr><th>Nom<\/th><th>Sujet<\/th><th>Statut<\/th><th>Envoyés<\/th><th>Ouverts<\/th><th>Actions<\/th><\/tr><\/thead><tbody id="cT"><\/tbody><\/table><\/div>`;rCmp()};
function rCmp(f=''){const i=f?CMP.filter(c=>[c.name,c.subject].join(' ').toLowerCase().includes(f)):CMP;$('cT').innerHTML=i.map(c=>`<tr><td style="color:var(--wh);font-weight:600">${esc(c.name)}<\/td><td>${esc(c.subject)}<\/td><td><span class="badge b-${c.status==='sent'?'sent':c.status==='draft'?'ac':'wait'}">${c.status}<\/span><\/td><td style="font-family:var(--m)">${N(c.metrics?.sent)}<\/td><td style="font-family:var(--m)">${N(c.metrics?.opened)}<\/td><td><button class="btn btn-sm btn-gh" onclick="simS('${c.id}')">Sim<\/button><\/td><\/tr>`).join('')||'<tr><td colspan="6" style="text-align:center;color:var(--dm);padding:24px">—<\/td><\/tr>'}
function fCmp(){rCmp(v('cS').toLowerCase())}
async function simS(id){try{await api('/campaigns/'+id+'/send-simulate',{method:'POST'});toast('Simulé');RR.campaigns()}catch(e){toast(e.message,1)}}
function oCmp(){const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};m.innerHTML=`<div class="modal"><h3>Nouvelle campagne<\/h3><div class="field"><label>Nom<\/label><input id="nN"><\/div><div class="field"><label>Objet<\/label><input id="nS"><\/div><div class="field"><label>Audience<\/label><input type="number" id="nA" value="500"><\/div><div class="field"><label>HTML<\/label><textarea class="ed" id="nH" rows="4"><\/textarea><\/div><div class="brow"><button class="btn btn-gh" onclick="this.closest('.modal-bg').remove()">Annuler<\/button><button class="btn btn-ac" onclick="cCmp()">Créer<\/button><\/div><\/div>`;document.body.appendChild(m)}
async function cCmp(){try{await api('/campaigns',{method:'POST',body:JSON.stringify({name:v('nN'),subject:v('nS'),audience_size:+v('nA'),content_html:v('nH')})});document.querySelector('.modal-bg')?.remove();toast('Créée');RR.campaigns()}catch(e){toast(e.message,1)}}
// =============== CONTACTS ===============
let CTC=[];
RR.contacts=async()=>{try{CTC=(await api('/contacts/list')).items||[]}catch(e){CTC=[]}
$('C').innerHTML=`<div class="toolbar"><input type="search" id="ctS" placeholder="Rechercher..." oninput="fCtc()"><div class="sp"><\/div><button class="btn btn-sm btn-ac" onclick="oCtc()">+ Ajouter<\/button><\/div><div class="card" style="padding:0;overflow:hidden"><table><thead><tr><th>Nom<\/th><th>Email<\/th><th>Entreprise<\/th><th>Pays<\/th><th>Tags<\/th><th><\/th><\/tr><\/thead><tbody id="ctT"><\/tbody><\/table><\/div>`;rCtc()};
function rCtc(f=''){const i=f?CTC.filter(c=>[c.email,c.first_name,c.last_name].join(' ').toLowerCase().includes(f)):CTC;$('ctT').innerHTML=i.map(c=>`<tr><td style="color:var(--wh);font-weight:600">${esc(c.first_name||'')} ${esc(c.last_name||'')}<\/td><td style="font-family:var(--m);font-size:11px">${esc(c.email)}<\/td><td>${esc(c.company||'')}<\/td><td>${esc(c.country||'')}<\/td><td><div class="tags">${(c.tags||[]).map(t=>`<span class="tag">${esc(t)}<\/span>`).join('')}<\/div><\/td><td><button class="btn btn-sm btn-rd" onclick="dCtc('${c.id}')">×<\/button><\/td><\/tr>`).join('')||'<tr><td colspan="6" style="text-align:center;color:var(--dm);padding:24px">—<\/td><\/tr>'}
function fCtc(){rCtc(v('ctS').toLowerCase())}
async function dCtc(id){if(!confirm('Supprimer?'))return;try{await api('/contacts/'+id,{method:'DELETE'});toast('OK');RR.contacts()}catch(e){toast(e.message,1)}}
function oCtc(){const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};m.innerHTML=`<div class="modal"><h3>Contact<\/h3><div class="row2"><div class="field"><label>Prénom<\/label><input id="cF"><\/div><div class="field"><label>Nom<\/label><input id="cL"><\/div><\/div><div class="field"><label>Email<\/label><input id="cE"><\/div><div class="field"><label>Tél<\/label><input id="cP"><\/div><div class="row2"><div class="field"><label>Entreprise<\/label><input id="cCo"><\/div><div class="field"><label>Pays<\/label><input id="cCy"><\/div><\/div><div class="field"><label>Tags<\/label><input id="cTg" placeholder="vip, prospect"><\/div><div class="brow"><button class="btn btn-gh" onclick="this.closest('.modal-bg').remove()">×<\/button><button class="btn btn-ac" onclick="aCtc()">Ajouter<\/button><\/div><\/div>`;document.body.appendChild(m)}
async function aCtc(){try{await api('/contacts',{method:'POST',body:JSON.stringify({email:v('cE'),first_name:v('cF'),last_name:v('cL'),company:v('cCo'),phone:v('cP'),country:v('cCy'),tags:v('cTg').split(',').map(t=>t.trim()).filter(Boolean)})});document.querySelector('.modal-bg')?.remove();toast('Ajouté');RR.contacts()}catch(e){toast(e.message,1)}}
// =============== SENDERS ===============
RR.senders=async()=>{let d={};try{d=await brg('senders_full')}catch(e){}
const pv=d.providers||[];const os=d.o365_status||[];const gs=d.gsuite_workspaces||[];const wt=d.warmup_by_type||[];
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Total Senders</div><div class="st-v">'+N(d.total_senders)+'</div></div><div class="stat s-gn"><div class="st-l">Capacit\u00e9/jour</div><div class="st-v">'+N(d.total_capacity)+'</div></div><div class="stat s-bl"><div class="st-l">Providers</div><div class="st-v">'+pv.length+'</div></div><div class="stat s-pu"><div class="st-l">O365 Active</div><div class="st-v">'+((os.find(s=>s.status==='Active')||{}).cnt||0)+'</div></div></div>'
+'<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:14px">Senders par provider</div><canvas id="chProv" height="240"></canvas></div>'
+'<div class="card"><div class="card-t" style="margin-bottom:14px">Capacit\u00e9 par provider</div><canvas id="chCap" height="240"></canvas></div></div>'
+'<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">O365 par statut</div>'+os.map(s=>'<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="color:var(--wh)">'+esc(s.status)+'</span><span style="font-family:var(--m);color:var(--ac)">'+N(s.cnt)+'</span></div>').join('')+'</div>'
+'<div class="card"><div class="card-t" style="margin-bottom:10px">Warmup par type</div>'+wt.map(w=>'<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="color:var(--wh)">'+esc(w.account_type)+'</span><span style="font-family:var(--m)">'+N(w.cnt)+'</span><span style="font-family:var(--m);color:var(--gn)">'+N(w.cap)+'/d</span></div>').join('')+'</div></div>';
if(typeof Chart!=='undefined'&&pv.length>0){const colors=['#d4a843','#34d399','#60a5fa','#a78bfa','#f87171','#fb923c','#22d3ee','#ec4899','#10b981','#8b5cf6','#ef4444','#06b6d4','#f59e0b','#6366f1','#14b8a6','#e11d48','#84cc16','#0ea5e9','#d946ef','#f97316','#64748b','#eab308'];
new Chart($('chProv'),{type:'bar',data:{labels:pv.map(p=>p.provider),datasets:[{data:pv.map(p=>+p.cnt),backgroundColor:colors.slice(0,pv.length),borderRadius:6}]},options:{indexAxis:'y',responsive:true,plugins:{legend:{display:false}},scales:{x:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.03)'}},y:{ticks:{color:'#94a3b8',font:{size:10}},grid:{display:false}}}}});
new Chart($('chCap'),{type:'bar',data:{labels:pv.map(p=>p.provider),datasets:[{data:pv.map(p=>+p.capacity),backgroundColor:colors.slice(0,pv.length).map(c=>c+'99'),borderRadius:6}]},options:{indexAxis:'y',responsive:true,plugins:{legend:{display:false}},scales:{x:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.03)'}},y:{ticks:{color:'#94a3b8',font:{size:10}},grid:{display:false}}}}});
}};
RR.warmup=async()=>{var d={},we={};try{d=await brg('warmup')}catch(e){}try{we=await brg('warmup_engine')}catch(e){}
var bt=we.by_tenant||[];
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Total comptes</div><div class="st-v">'+N(d.total||we.total_with_oid||0)+'</div></div><div class="stat s-gn"><div class="st-l">En warming</div><div class="st-v" style="color:'+(we.warming>0?'var(--gn)':'var(--rd)')+'">'+N(we.warming||0)+'</div></div><div class="stat s-bl"><div class="st-l">Avec Object ID</div><div class="st-v">'+N(we.total_with_oid||0)+'</div></div><div class="stat s-pu"><div class="st-l">Tenants actifs</div><div class="st-v">6</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Warmup Engine</div><div style="display:flex;gap:8px"><button class="btn btn-sm btn-ac" onclick="startWarmup(5)">Start 5</button><button class="btn btn-sm btn-ac" onclick="startWarmup(10)">Start 10</button><button class="btn btn-sm btn-ac" onclick="startWarmup(20)">Start 20</button><button class="btn btn-sm btn-rd" onclick="stopWarmup()">Stop All</button></div></div>'
+'<p style="font-size:11px;color:var(--mu);margin-bottom:14px">'+(we.warming>0?we.warming+' comptes actifs en rotation':'Aucun compte en warmup — demarrer pour ameliorer la delivrabilite')+'</p>'
+'<div class="card-t" style="margin-bottom:10px">Par tenant</div>'
+'<table><thead><tr><th>Tenant</th><th>Comptes</th><th>Actifs</th><th>Taux</th></tr></thead><tbody>'
+bt.map(function(t){var pct=t.cnt>0?Math.round((+t.active/+t.cnt)*100):0;return '<tr><td style="color:var(--wh);font-weight:500;font-size:11px">'+esc(t.tenant_domain)+'</td><td style="font-family:var(--m)">'+N(t.cnt)+'</td><td style="font-family:var(--m);color:'+(+t.active>0?'var(--gn)':'var(--mu)')+'">'+N(t.active)+'</td><td><div style="background:var(--bg);border-radius:4px;height:8px;width:80px;display:inline-block;vertical-align:middle"><div style="background:var(--gn);height:100%;border-radius:4px;width:'+pct+'%"></div></div> <span style="font-size:10px;color:var(--mu)">'+pct+'%</span></td></tr>'}).join('')
+'</tbody></table></div>'
+'<div class="card"><div class="card-t" style="margin-bottom:10px">ISP Throttle Config</div><div id="throttleDiv">Chargement...</div></div>';
brg('throttle_config').then(function(tc){var th=tc.throttle||{};document.getElementById('throttleDiv').innerHTML=Object.keys(th).map(function(isp){var c=th[isp];return '<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="color:var(--wh);font-size:12px">'+isp+'</span><span style="font-family:var(--m);font-size:10px;color:var(--mu)">'+c.per_hour+'/h | '+c.per_day+'/j | '+c.delay_ms+'ms delay</span></div>'}).join('')}).catch(function(){});};
async function startWarmup(n){
toast('Activation de '+n+' comptes...');
var r=await(await fetch(BRG+'?action=warmup_start&token=WEVADS2026&limit='+n)).json();
if(r.ok){toast(r.activated+' comptes actives');go('warmup')}else toast('Erreur',1)}
async function stopWarmup(){
if(!confirm('Stopper TOUS les warmups ?'))return;
var r=await(await fetch(BRG+'?action=warmup_stop&token=WEVADS2026')).json();
if(r.ok){toast(r.stopped+' comptes stoppes');go('warmup')}else toast('Erreur',1)}
RR.domains=async()=>{let d={};try{d=await brg('domains')}catch(e){}const doms=d.domains||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Domaines<\/div><div class="st-v">${d.total||0}<\/div><\/div><div class="stat s-gn"><div class="st-l">Activés<\/div><div class="st-v">${doms.filter(x=>x.status==='Activated').length}<\/div><\/div><\/div>
<div class="card" style="padding:0;overflow:hidden"><table><thead><tr><th>Domaine<\/th><th>Statut<\/th><th>Dispo<\/th><th>Compte<\/th><th>Brand<\/th><\/tr><\/thead><tbody>${doms.map(d=>`<tr><td style="font-family:var(--m);color:var(--wh);font-weight:600">${esc(d.name)}<\/td><td><span class="badge b-${d.status==='Activated'?'ok':'wait'}">${d.status}<\/span><\/td><td>${esc(d.availability||'')}<\/td><td style="font-size:10px;color:var(--mu)">${esc(d.account_name||'')}<\/td><td>${d.has_brand==='Yes'?'✅':'—'}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>`};
// =============== SEEDS ===============
RR.seeds=async()=>{let d={};try{d=await brg('seeds')}catch(e){}
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Seed Accounts<\/div><div class="st-v">${N(d.accounts)}<\/div><\/div><div class="stat s-bl"><div class="st-l">Inboxes<\/div><div class="st-v">${N(d.inboxes)}<\/div><\/div><div class="stat s-gn"><div class="st-l">Résultats<\/div><div class="st-v">${N(d.results)}<\/div><\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Inbox Testing<\/div><p style="font-size:12px;color:var(--mu);line-height:1.7">Les seeds testent le placement inbox avant envoi réel. Chaque seed email vérifie inbox vs spam chez Gmail, Outlook, Yahoo, T-Online, GMX.<\/p><div style="margin-top:12px;display:flex;gap:6px"><button class="btn btn-sm btn-ac" onclick="toast('Seed test lancé')">Lancer test<\/button><button class="btn btn-sm btn-gh" onclick="toast('Check...')">Vérifier<\/button><\/div><\/div>`};
// =============== REPUTATION ===============
RR.reputation=async()=>{let d={};try{d=await brg('reputation')}catch(e){}
$('C').innerHTML=`<div class="stats"><div class="stat s-rd"><div class="st-l">Bounces<\/div><div class="st-v">${d.bounces||0}<\/div><\/div><div class="stat s-ac"><div class="st-l">Bounce Log<\/div><div class="st-v">${d.bounce_log||0}<\/div><\/div><div class="stat s-bl"><div class="st-l">Blacklisted<\/div><div class="st-v">${d.blacklisted||0}<\/div><\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Blacklist Check<\/div><div style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px">${['Spamhaus','Barracuda','SORBS','UCEPROTECT'].map(n=>`<div style="padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:9px;color:var(--mu)">${n}<\/div><div style="font-size:14px;font-weight:700;color:var(--gn);margin-top:4px">Clean<\/div><\/div>`).join('')}<\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:8px">Monitoring<\/div><p style="font-size:12px;color:var(--mu);line-height:1.7">Surveillance bounces, blacklists, plaintes. Le Brain Engine ajuste volumes et IPs en fonction du score réputation.<\/p><\/div>`};
// =============== BRAIN IA ===============
RR.brain=async()=>{let d={};try{d=await brg('brain')}catch(e){}const mt=d.methods||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Configs<\/div><div class="st-v">${N(d.configs)}<\/div><\/div><div class="stat s-gn"><div class="st-l">Winners<\/div><div class="st-v">${d.winners||0}<\/div><\/div><div class="stat s-pu"><div class="st-l">Learnings<\/div><div class="st-v">${N(d.learnings)}<\/div><\/div><div class="stat s-bl"><div class="st-l">IA Engine<\/div><div class="st-v" style="font-size:16px">LIVE<\/div><div class="st-s">Sovereign GPU<\/div><\/div><\/div>
<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">Send Methods (Hamid)<\/div><table><thead><tr><th>Méthode<\/th><th>Description<\/th><th>Best ISPs<\/th><\/tr><\/thead><tbody>${mt.map(m=>`<tr><td style="color:var(--wh);font-weight:600">${esc(m.method_name)}<\/td><td style="font-size:11px;color:var(--mu)">${esc(m.description)}<\/td><td style="font-family:var(--m);font-size:10px;color:var(--ac)">${esc(m.best_for_isps||'')}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Stack IA<\/div>${[['vLLM GPU','S95:8000','qwen2.5-14B'],['Groq','API','llama-3.3-70b'],['Cerebras','API','qwen-3-235b'],['Ollama S204','CPU','9 modèles'],['Qdrant RAG','S95:6333','95 memcells']].map(([n,h,d])=>`<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--wh)">${n}<\/span><span style="font-family:var(--m);font-size:9px;color:var(--mu)">${h}<\/span><span style="font-size:10px;color:var(--ac)">${d}<\/span><\/div>`).join('')}<\/div><\/div>`};
// =============== CREATIVE ===============
if(typeof Chart!=='undefined'){setTimeout(()=>{const el=document.getElementById('chBrain');if(el){const methods=d.methods||[];if(methods.length)new Chart(el,{type:'radar',data:{labels:methods.map(m=>m.method_name||'?'),datasets:[{label:'Brain Methods',data:methods.map((_,i)=>80-i*10),backgroundColor:'rgba(212,168,67,.15)',borderColor:'#d4a843',pointBackgroundColor:'#d4a843'}]},options:{responsive:true,scales:{r:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.06)'},pointLabels:{color:'#94a3b8',font:{size:10}}}},plugins:{legend:{labels:{color:'#5a6a80'}}}}});}},100);
};
RR.creative=async()=>{let d={};try{d=await brg('creative')}catch(e){}const subs=d.top_subjects||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Performances<\/div><div class="st-v">${N(d.performance)}<\/div><\/div><div class="stat s-bl"><div class="st-l">Traductions<\/div><div class="st-v">${d.translations||0}<\/div><\/div><\/div>
<div class="card"><div class="card-h"><div class="card-t">Creative Engine IA<\/div><button class="btn btn-sm btn-ac" onclick="toast('Génération...')">🧠 Générer<\/button><\/div><p style="font-size:12px;color:var(--mu);margin-bottom:14px">Analyse performance sujets/from/HTML. Génération variantes IA. Promotion winners automatique.<\/p>
${subs.length?`<table><thead><tr><th>Sujet<\/th><th>Open%<\/th><th>Click%<\/th><\/tr><\/thead><tbody>${subs.map(s=>`<tr><td style="color:var(--wh)">${esc((s.subject||'').substring(0,45))}<\/td><td style="font-family:var(--m);color:var(--gn)">${s.open_rate||0}%<\/td><td style="font-family:var(--m);color:var(--ac)">${s.click_rate||0}%<\/td><\/tr>`).join('')}<\/tbody><\/table>`:''}<\/div>`};
// =============== ANALYTICS ===============
RR.analytics=async()=>{let d={},ch={};try{d=await brg('send_stats')}catch(e){}try{ch=await brg('charts_daily')}catch(e){}
const bi=d.by_isp||[];const stats=ch.stats||[];
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Emails track\u00e9s</div><div class="st-v">'+N(d.total)+'</div></div><div class="stat s-gn"><div class="st-l">ISPs</div><div class="st-v">'+bi.length+'</div></div><div class="stat s-bl"><div class="st-l">Opens today</div><div class="st-v">'+N((stats[0]||{}).total_opened||0)+'</div></div><div class="stat s-pu"><div class="st-l">Clicks today</div><div class="st-v">'+N((stats[0]||{}).total_clicked||0)+'</div></div></div>'
+'<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:14px">Volume par ISP</div><canvas id="chISP" height="220"></canvas></div>'
+'<div class="card"><div class="card-t" style="margin-bottom:14px">Tendance 30j</div><canvas id="chTrend" height="220"></canvas></div></div>'
+'<div class="card"><div class="card-t" style="margin-bottom:10px">D\u00e9tail par ISP</div><table><thead><tr><th>ISP</th><th>Envoy\u00e9s</th><th>D\u00e9livr\u00e9s</th><th>Taux</th></tr></thead><tbody>'+bi.map(r=>'<tr><td style="color:var(--wh);font-weight:600">'+esc(r.isp||'Unknown')+'</td><td style="font-family:var(--m)">'+N(r.cnt)+'</td><td style="font-family:var(--m);color:var(--gn)">'+N(r.delivered||0)+'</td><td style="font-family:var(--m)">'+((r.delivered||0)/(+r.cnt||1)*100).toFixed(1)+'%</td></tr>').join('')+'</tbody></table></div>';
if(typeof Chart!=='undefined'&&bi.length>0){new Chart($('chISP'),{type:'doughnut',data:{labels:bi.map(r=>r.isp||'?'),datasets:[{data:bi.map(r=>+r.cnt),backgroundColor:['#d4a843','#34d399','#60a5fa','#a78bfa','#f87171','#fb923c','#22d3ee','#ec4899']}]},options:{responsive:true,plugins:{legend:{position:'right',labels:{color:'#5a6a80',font:{size:10}}}}}});
if(stats.length>1){new Chart($('chTrend'),{type:'line',data:{labels:stats.map(s=>(s.date||'').substring(5)),datasets:[{label:'Sent',data:stats.map(s=>+s.total_sent),borderColor:'#d4a843',tension:.3,pointRadius:2,fill:false},{label:'Opens',data:stats.map(s=>+s.total_opened),borderColor:'#34d399',tension:.3,pointRadius:2,fill:false}]},options:{responsive:true,plugins:{legend:{labels:{color:'#5a6a80',font:{size:10}}}},scales:{x:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.03)'}},y:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.03)'}}}}});}
}
try{var eg=await brg('engagement_score');var dist=eg.distribution||[];
var egDiv=document.createElement('div');egDiv.className='card';
egDiv.innerHTML='<div class="card-t" style="margin-bottom:10px">Engagement Scoring (7.3M contacts)</div>'
+dist.map(function(d2){var colors={'hot':'var(--gn)','warm':'var(--ac)','cold':'var(--bl)','dead':'var(--rd)'};
return '<div style="display:flex;align-items:center;gap:12px;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="font-size:12px;font-weight:600;color:'+(colors[d2.score]||'var(--mu)')+'">'+esc(d2.score||'unscored').toUpperCase()+'</span><div style="flex:1;background:var(--bg);border-radius:4px;height:12px"><div style="background:'+(colors[d2.score]||'var(--dm)')+';height:100%;border-radius:4px;width:'+Math.min(100,+d2.cnt/30000)+'%"></div></div><span style="font-family:var(--m);font-size:11px;color:var(--mu)">'+N(d2.cnt)+'</span></div>'}).join('');
$('C').appendChild(egDiv)}catch(e){}
};
RR.ai=()=>{$('C').innerHTML=`<div class="chat-w"><div class="chat-m" id="aM"><div class="chat-e"><div style="font-size:36px">🧠<\/div><h3>WEVIA AI<\/h3><p style="font-size:12px;margin-top:6px;color:var(--mu)">Optimisez sujets, générez contenu, analysez délivrabilité.<\/p><div style="display:flex;gap:6px;justify-content:center;margin-top:12px;flex-wrap:wrap"><button class="btn btn-sm btn-gh" onclick="aQ('Optimise: Découvrez nos nouveautés')">Sujet<\/button><button class="btn btn-sm btn-gh" onclick="aQ('Email B2B pharma')">Email<\/button><button class="btn btn-sm btn-gh" onclick="aQ('Audit délivrabilité')">Audit<\/button><\/div><\/div><\/div><div class="chat-b"><textarea id="aI" rows="1" placeholder="Message..." onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();aS()}" oninput="this.style.height='42px';this.style.height=Math.min(this.scrollHeight,110)+'px'"><\/textarea><button class="btn btn-sm btn-ac" onclick="aS()">→<\/button><\/div><\/div>`};
let chatH=[];
function aQ(q){$('aI').value=q;aS()}
async function aS(){const msg=v('aI');if(!msg)return;const b=$('aM');b.querySelector('.chat-e')?.remove();b.innerHTML+=`<div class="chat-u">${esc(msg)}<\/div>`;$('aI').value='';const lid='l'+Date.now();b.innerHTML+=`<div class="chat-a" id="${lid}"><span class="ldot"><\/span><span class="ldot"><\/span><span class="ldot"><\/span><\/div>`;b.scrollTop=b.scrollHeight;try{chatH.push({role:'user',content:msg});const r=await(await fetch(WEVIA,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:msg,mode:'full',history:chatH.slice(-6)})})).json();const re=r.response||r.error||'Err';chatH.push({role:'assistant',content:re});$(lid).outerHTML=`<div class="chat-a">${md2h(re)}<\/div>`}catch(e){$(lid).outerHTML=`<div class="chat-a" style="color:var(--rd)">Err: ${e.message}<\/div>`}b.scrollTop=b.scrollHeight}
// =============== CHANNELS ===============
RR.channels=async()=>{let se={};try{se=await(await fetch(SE+'?action=status&token=ETHICA_API_2026_SECURE')).json()}catch(e){}const inf=se.infrastructure||{};const o=inf.o365_senders||{};
$('C').innerHTML=`<div class="stats"><div class="stat s-bl"><div class="st-l">Email<\/div><div class="st-v" style="font-size:16px;color:var(--em)">LIVE<\/div><div class="st-s">PMTA+${o.active||0} O365<\/div><\/div><div class="stat s-tg"><div class="st-l">Telegram<\/div><div class="st-v" style="font-size:16px;color:var(--tg)">LIVE<\/div><\/div><div class="stat s-gn"><div class="st-l">SMS<\/div><div class="st-v" style="font-size:16px;color:var(--or)">PENDING<\/div><\/div><div class="stat s-or"><div class="st-l">WhatsApp<\/div><div class="st-v" style="font-size:16px;color:var(--or)">PENDING<\/div><\/div><\/div>
<div class="card"><div class="card-h"><div class="card-t">✈ Telegram<\/div><span class="badge b-ok">Connecté<\/span><\/div><p style="font-size:12px;color:var(--mu);margin-bottom:10px">@wevads_alerts_bot → Yacine<\/p><button class="btn btn-sm btn-gh" style="color:var(--tg);border-color:rgba(34,158,217,.2)" onclick="tgTest()">Test Telegram<\/button><\/div>
<div class="card"><div class="card-h"><div class="card-t">💬 SMS OVH<\/div><span class="badge b-wait">Credentials<\/span><\/div><div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px"><div class="field"><label>App Key<\/label><input id="sk1"><\/div><div class="field"><label>Secret<\/label><input id="sk2"><\/div><div class="field"><label>Consumer<\/label><input id="sk3"><\/div><\/div><button class="btn btn-sm btn-gh" style="color:var(--sm)" onclick="cfgSMS()">Configurer<\/button><\/div>
<div class="card"><div class="card-h"><div class="card-t">📱 WhatsApp<\/div><span class="badge b-wait">Credentials<\/span><\/div><div class="row2"><div class="field"><label>Phone ID<\/label><input id="wk1"><\/div><div class="field"><label>Token<\/label><input id="wk2"><\/div><\/div><button class="btn btn-sm btn-gh" style="color:var(--wa)" onclick="cfgWA()">Configurer<\/button><\/div>`};
async function tgTest(){toast('Envoi TG...');try{const r=await(await fetch('https://api.telegram.org/bot8544624912:AAEm9ttXK6JeFqAL-gcvB5sreCBhXzzQwrs/sendMessage',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({chat_id:'7605775322',text:'✅ WEVADS IA v3.0 Test\n'+new Date().toLocaleString('fr-FR')})})).json();toast(r.ok?'TG OK':'Err',!r.ok)}catch(e){toast('Err',1)}}
async function cfgSMS(){toast('Config SMS...')}
async function cfgWA(){toast('Config WA...')}
// =============== TEMPLATES ===============
let TPL=[];
RR.templates=async()=>{try{TPL=(await api('/templates/list')).items||[]}catch(e){TPL=[]}
$('C').innerHTML=`<div class="toolbar"><div class="sp"><\/div><button class="btn btn-sm btn-ac" onclick="oTpl()">+ Nouveau<\/button><\/div><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px" id="tG"><\/div>`;
$('tG').innerHTML=TPL.map(t=>`<div class="card" style="cursor:pointer"><div style="display:flex;justify-content:space-between;margin-bottom:8px"><span style="font-weight:700;color:var(--wh)">${esc(t.name)}<\/span><span class="badge b-ok">${esc(t.category)}<\/span><\/div><div style="background:var(--bg);border:1px solid var(--bd);border-radius:var(--r3);padding:10px;min-height:50px;font-size:10px;color:var(--mu);overflow:hidden;max-height:70px">${t.html?t.html.substring(0,120)+'...':'—'}<\/div><\/div>`).join('')||'<div style="text-align:center;color:var(--dm);padding:30px;grid-column:1/-1">—<\/div>'};
function oTpl(){const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};m.innerHTML=`<div class="modal"><h3>Template<\/h3><div class="field"><label>Nom<\/label><input id="tN"><\/div><div class="field"><label>Cat<\/label><select id="tC"><option>marketing<\/option><option>transactional<\/option><option>newsletter<\/option><\/select><\/div><div class="field"><label>HTML<\/label><textarea class="ed" id="tH" rows="6"><\/textarea><\/div><div class="brow"><button class="btn btn-gh" onclick="this.closest('.modal-bg').remove()">×<\/button><button class="btn btn-ac" onclick="sTpl()">Save<\/button><\/div><\/div>`;document.body.appendChild(m)}
async function sTpl(){try{await api('/templates',{method:'POST',body:JSON.stringify({name:v('tN'),category:v('tC'),html:v('tH')})});document.querySelector('.modal-bg')?.remove();toast('Créé');RR.templates()}catch(e){toast(e.message,1)}}
// =============== SEND ENGINE ===============
RR.sendengine=async()=>{let se={},k={},w={};try{se=await(await fetch(SE+'?action=status&token=ETHICA_API_2026_SECURE')).json()}catch(e){}try{k=(await eng('dashboard')).kpis||{}}catch(e){}try{w=await eng('warmup_status')}catch(e){}const inf=se.infrastructure||{};const o=inf.o365_senders||{};const pm=inf.pmta||{};const cl=se.clients||{};
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Méthode<\/div><div class="st-v" style="font-size:14px">${(inf.method||'—').toUpperCase()}<\/div><\/div><div class="stat s-bl"><div class="st-l">O365<\/div><div class="st-v">${o.active||0}/${o.total||0}<\/div><\/div><div class="stat s-gn"><div class="st-l">PMTA<\/div><div class="st-v">${pm.servers_alive||0}/${pm.servers_configured||0}<\/div><\/div><div class="stat s-pu"><div class="st-l">Warmup<\/div><div class="st-v">${N(w.total_accounts||k.warmup_accounts)}<\/div><\/div><div class="stat s-or"><div class="st-l">Queue<\/div><div class="st-v">${k.queue_pending||0}<\/div><\/div><\/div>
<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">Clients<\/div>${[['Ethica (Pharma)',cl.ethica?.contacts,cl.ethica?.emails,'var(--ac)'],['WEVADS (Multi)',cl.wevads?.contacts,k.senders_active,'var(--cy)']].map(([n,c,e,col])=>`<div style="padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);margin-bottom:8px"><div style="font-size:11px;font-weight:600;color:${col}">${n}<\/div><div style="font-family:var(--m);font-size:18px;font-weight:800;color:var(--wh)">${N(c||0)}<\/div><div style="font-size:10px;color:var(--mu)">${N(e||0)} active<\/div><\/div>`).join('')}<\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Métriques<\/div><div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">${[['Envoyés',k.emails_sent_total,'var(--wh)'],['Today',k.emails_sent_today,'var(--gn)'],['Bounces',k.bounces,'var(--rd)'],['Methods',k.send_methods,'var(--cy)']].map(([l,v2,c])=>`<div style="text-align:center;padding:10px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:16px;font-weight:800;color:${c};font-family:var(--m)">${N(v2||0)}<\/div><div style="font-size:9px;color:var(--mu);margin-top:3px">${l}<\/div><\/div>`).join('')}<\/div><\/div><\/div>`};
// =============== OFFERS ===============
RR.offers=async()=>{let d={};try{d=await brg('offers')}catch(e){}const off=d.offers||[];const sp=d.sponsors||[];const bv=d.by_vertical||[];const bs=d.by_status||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Offres<\/div><div class="st-v">${N(d.total_offers)}<\/div><\/div><div class="stat s-gn"><div class="st-l">Sponsors<\/div><div class="st-v">${sp.length}<\/div><\/div><div class="stat s-bl"><div class="st-l">Networks<\/div><div class="st-v">${(d.networks||[]).length}<\/div><\/div><\/div>
<div class="card"><div class="card-h"><div class="card-t">Catalogue Offres (${N(d.total_offers)})<\/div><button class="btn btn-sm btn-ac" onclick="toast('Import depuis CX3/Double M')">+ Importer<\/button><\/div>
<table><thead><tr><th>Offre<\/th><th>Pays<\/th><th>Vertical<\/th><th>Payout<\/th><th>Statut<\/th><th>Clicks<\/th><th>Conv.<\/th><\/tr><\/thead>
<tbody>${off.slice(0,20).map(o=>`<tr><td style="color:var(--wh);font-weight:500;font-size:11px">${esc((o.offer_name||'').substring(0,45))}<\/td><td><span class="badge b-em">${esc(o.country_code)}<\/span><\/td><td style="font-size:11px;color:var(--mu)">${esc(o.vertical)}<\/td><td style="font-family:var(--m);color:var(--gn)">${o.payout>0?'$'+o.payout:'—'}<\/td><td><span class="badge b-${o.status==='active'?'ok':o.status==='public'?'ac':'wait'}">${esc(o.status)}<\/span><\/td><td style="font-family:var(--m)">${N(o.clicks)}<\/td><td style="font-family:var(--m);color:var(--ac)">${N(o.conversions)}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>`;
if(typeof Chart!=='undefined'&&bv.length>0){setTimeout(()=>{const el=document.getElementById('chVert');if(el)new Chart(el,{type:'pie',data:{labels:bv.map(v=>v.vertical),datasets:[{data:bv.map(v=>+v.cnt),backgroundColor:['#d4a843','#34d399','#60a5fa','#a78bfa','#f87171','#fb923c','#22d3ee','#ec4899','#10b981','#8b5cf6']}]},options:{responsive:true,plugins:{legend:{position:'right',labels:{color:'#5a6a80',font:{size:10}}}}}});},100);
};
};
// =============== SPONSORS ===============
RR.infra=async()=>{let d={};try{d=await brg('infra')}catch(e){}const sc=d.schemas||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Tables<\/div><div class="st-v">${d.tables||0}<\/div><\/div><div class="stat s-bl"><div class="st-l">DB Size<\/div><div class="st-v" style="font-size:16px">${d.db_size||'?'}<\/div><\/div><div class="stat s-gn"><div class="st-l">APIs<\/div><div class="st-v">344<\/div><\/div><div class="stat s-pu"><div class="st-l">GOLD<\/div><div class="st-v">1,411<\/div><\/div><\/div>
<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">Schemas PostgreSQL<\/div><table><thead><tr><th>Schema<\/th><th>Tables<\/th><\/tr><\/thead><tbody>${sc.map(s=>`<tr onclick="drillSchema(s.schemaname,s.cnt)" style="cursor:pointer"><td style="color:var(--wh);font-weight:600">${esc(s.schemaname)}<\/td><td style="font-family:var(--m)">${s.cnt}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Services<\/div>${[['S95 Apache','ADX 5821+BCG 5823+FMG 5822','ok'],['S95 PMTA v5.0r3','Port 25','ok'],['S95 Postfix','2525/2526','ok'],['S204 Nginx','HTTPS proxy','ok'],['Node.js V2','Port 5850','ok'],['S151 Tracking','HTTP relay','ok'],['Telegram Bot','@wevads_alerts_bot','ok'],['Qdrant RAG','S95:6333','ok'],['n8n Workflows','S95:5678','ok']].map(([n,d,s])=>`<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--wh)">${n}<\/span><span style="font-family:var(--m);font-size:9px;color:var(--mu)">${d}<\/span><span class="badge b-${s}">${s}<\/span><\/div>`).join('')}<\/div><\/div>`};
// =============== SETTINGS ===============
RR.settings=()=>{$('C').innerHTML=`<div class="card"><div class="card-t" style="margin-bottom:14px">Profil<\/div><div class="row2"><div class="field"><label>Nom<\/label><input value="${esc(U.name||'')}"><\/div><div class="field"><label>Email<\/label><input value="${esc(U.email||'')}" readonly style="opacity:.5"><\/div><\/div><button class="btn btn-sm btn-ac" style="margin-top:10px" onclick="toast('OK')">Save<\/button><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Auth DNS<\/div><table><tbody>${[['SPF','v=spf1 include:spf.pmta.weval ~all'],['DKIM','weval2026._domainkey'],['DMARC','p=quarantine'],['TLS','1.3 Enforced']].map(([k,v2])=>`<tr><td style="color:var(--wh)">${k}<\/td><td style="font-family:var(--m);font-size:11px;color:var(--mu)">${v2}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>`};
// =============== OFFERS ===============
RR.abtesting=async()=>{let d={};try{d=await brg('ab_testing')}catch(e){}const subs=d.subjects||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Tests sujets<\/div><div class="st-v">${N(d.total)}<\/div><\/div><div class="stat s-gn"><div class="st-l">Winners<\/div><div class="st-v">${d.winners||0}<\/div><\/div><\/div>
<div class="card"><div class="card-h"><div class="card-t">Subject line A/B testing<\/div><button class="btn btn-sm btn-ac" onclick="toast('Test IA lance')">+ Nouveau test<\/button><\/div>
<table><thead><tr><th>Offer<\/th><th>Subject<\/th><th>Country<\/th><th>Sent<\/th><th>Opens<\/th><th>Rate<\/th><th>Winner<\/th><\/tr><\/thead>
<tbody>${subs.map(s=>`<tr><td style="font-family:var(--m);color:var(--mu)">${s.offer_id}<\/td><td style="font-family:var(--m);font-size:10px">${s.subject_id}<\/td><td style="color:var(--wh)">${esc(s.country)}<\/td><td style="font-family:var(--m)">${N(s.total_sent)}<\/td><td style="font-family:var(--m)">${N(s.total_opens)}<\/td><td style="font-family:var(--m);color:${+s.open_rate>15?'var(--gn)':+s.open_rate>5?'var(--ac)':'var(--rd)'}">${s.open_rate}%<\/td><td>${s.is_winner?'\u2705':'\u274c'}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>
<div class="card"><div class="card-t" style="margin-bottom:8px">IA A/B Engine<\/div><p style="font-size:12px;color:var(--mu);line-height:1.7">Le Brain teste automatiquement les variantes de sujets par ISP/pays. Les winners sont promus pour les envois bulk. Gnration IA de 5-10 variantes par offre.<\/p><\/div>`};
// =============== SCHEDULED ===============
RR.scheduled=async()=>{let d={};try{d=await brg('scheduled')}catch(e){}const sc=d.campaigns||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Scheduled<\/div><div class="st-v">${d.total||0}<\/div><\/div><\/div>
<div class="card"><div class="card-h"><div class="card-t">Campagnes programmes<\/div><button class="btn btn-sm btn-ac" onclick="toast('Scheduler')">+ Programmer<\/button><\/div>
${sc.length?`<table><thead><tr><th>ID<\/th><th>Nom<\/th><th>Date<\/th><th>Statut<\/th><\/tr><\/thead><tbody>${sc.map(c=>`<tr><td style="font-family:var(--m)">${c.id}<\/td><td style="color:var(--wh)">${esc(c.name||c.campaign_name||'Campaign '+c.id)}<\/td><td style="font-family:var(--m);font-size:11px;color:var(--mu)">${c.scheduled_at||c.send_date||''}<\/td><td><span class="badge b-wait">scheduled<\/span><\/td><\/tr>`).join('')}<\/tbody><\/table>`:'<p style="color:var(--mu);font-size:13px">Aucune campagne programme. Cliquez sur "+ Programmer" pour planifier un envoi.<\/p>'}
<\/div><div class="card"><div class="card-t" style="margin-bottom:8px">Cron Engine<\/div><p style="font-size:12px;color:var(--mu);line-height:1.7">34 crons actifs sur S95. Le scheduler permet de planifier des envois heure fixe, par segment, avec rotation automatique des senders et mthodes.<\/p><\/div>`};
// =============== DOMAIN POOL ===============
RR.dompool=async()=>{let d={};try{d=await brg('domain_pool')}catch(e){}const pool=d.pool||[];const bs=d.by_status||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Pool total<\/div><div class="st-v">${N(d.total)}<\/div><\/div><div class="stat s-gn"><div class="st-l">DNS OK<\/div><div class="st-v">${d.configured||0}<\/div><\/div><div class="stat s-bl"><div class="st-l">Statuts<\/div><div class="st-v">${bs.length}<\/div><\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Status distribution<\/div>${bs.map(s=>`<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="color:var(--wh)">${esc(s.status)}<\/span><span style="font-family:var(--m);color:var(--ac)">${N(s.cnt)}<\/span><\/div>`).join('')}<\/div>
<div class="card" style="padding:0;overflow:hidden"><table><thead><tr><th>Domaine<\/th><th>Source<\/th><th>Registrar<\/th><th>SPF<\/th><th>DKIM<\/th><th>DMARC<\/th><th>Emails<\/th><th>Score<\/th><\/tr><\/thead>
<tbody>${pool.slice(0,20).map(p=>`<tr onclick="drillDomain(p.domain||'?',p.has_spf,p.has_dkim,p.has_dmarc,p.emails_sent||0,p.reputation_score||0)" style="cursor:pointer"><td style="font-family:var(--m);font-size:10px;color:var(--wh)">${esc(p.domain)}<\/td><td style="font-size:10px;color:var(--mu)">${esc(p.source||'')}<\/td><td style="font-size:10px">${esc(p.registrar||'')}<\/td><td>${p.has_spf?'\u2705':'\u274c'}<\/td><td>${p.has_dkim?'\u2705':'\u274c'}<\/td><td>${p.has_dmarc?'\u2705':'\u274c'}<\/td><td style="font-family:var(--m)">${N(p.emails_sent)}<\/td><td style="font-family:var(--m);color:var(--gn)">${p.reputation_score||0}<\/td><\/tr>`).join('')}<\/tbody><\/table><\/div>`};
// =============== REVENUE ===============
RR.revenue=async()=>{let d={},pb={};try{d=await brg('revenue')}catch(e){}try{pb=await(await fetch('/api/postback.php?action=stats&token=WEVADS2026')).json()}catch(e){}
const t=d.totals||{};const pbt=pb.totals||{};const bc=d.by_campaign||[];const pbd=pb.daily||[];
const totalRev=+(t.revenue||0)+ Number(pbt.rev||0);const totalProfit=+(t.profit||0)+ Number(pbt.profit||0);const totalConv=+(t.conversions||0)+ Number(pbt.conv||0);
$('C').innerHTML='<div class="stats"><div class="stat s-gn"><div class="st-l">Revenue Total</div><div class="st-v">$'+N(totalRev)+'</div></div><div class="stat s-rd"><div class="st-l">Cost</div><div class="st-v">$'+N(t.cost||0)+'</div></div><div class="stat s-ac"><div class="st-l">Profit</div><div class="st-v">$'+N(totalProfit)+'</div></div><div class="stat s-bl"><div class="st-l">Conversions</div><div class="st-v">'+N(totalConv)+'</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Postback Webhook</div><span class="badge b-ok">Live</span></div><p style="font-size:11px;color:var(--mu);margin-bottom:12px">URL: <code style="font-family:var(--m);font-size:10px;background:var(--bg);padding:3px 8px;border-radius:6px">weval-consulting.com/api/postback.php?action=receive&offer_id=OID&payout=AMT&network=NET&sub1=CAMP</code></p>'
+(pbd.length?'<canvas id="chRev" height="180" style="margin-top:12px"></canvas>':'<p style="color:var(--dm);font-size:12px">Envoyez un postback pour voir les conversions ici.</p>')
+'</div>';
if(typeof Chart!=='undefined'&&pbd.length>0){setTimeout(()=>{new Chart($('chRev'),{type:'bar',data:{labels:pbd.map(d=>d.date),datasets:[{label:'Revenue',data:pbd.map(d=>+d.rev),backgroundColor:'rgba(52,211,153,.5)',borderRadius:4}]},options:{responsive:true,plugins:{legend:{labels:{color:'#5a6a80',font:{size:10}}}},scales:{x:{ticks:{color:'#374151',font:{size:9}},grid:{color:'rgba(255,255,255,.03)'}},y:{ticks:{color:'#374151',font:{size:9},callback:v=>'$'+v},grid:{color:'rgba(255,255,255,.03)'}}}}});},100);}};
RR.pmtamgmt=async()=>{var km={},ma={},d={};try{km=await brg('kumomta_status')}catch(e){}try{ma=await brg('mta_architecture')}catch(e){}try{d=await brg('pmta')}catch(e){}
var arch=ma.architecture||[];
$('C').innerHTML='<div class="stats"><div class="stat s-gn"><div class="st-l">KumoMTA</div><div class="st-v" style="font-size:16px;color:'+(km.status==='active'?'var(--gn)':'var(--rd)')+'">'+esc(km.status||'?').toUpperCase()+'</div><div class="st-s">Port 587 (Rust)</div></div><div class="stat s-ac"><div class="st-l">PMTA</div><div class="st-v" style="font-size:16px">Active</div><div class="st-s">Port 25 (Legacy)</div></div><div class="stat s-bl"><div class="st-l">Postfix</div><div class="st-v" style="font-size:16px">Active</div><div class="st-s">Port 2525</div></div><div class="stat s-pu"><div class="st-l">Architecture</div><div class="st-v" style="font-size:14px">Dual MTA</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">MTA Architecture</div><span class="badge b-ok">Production</span></div>'
+'<div style="display:grid;grid-template-columns:1fr auto 1fr auto 1fr;gap:8px;align-items:center;margin:16px 0">'
+'<div style="padding:16px;background:var(--sf);border:2px solid var(--gn);border-radius:var(--r);text-align:center"><div style="font-size:14px;font-weight:600;color:var(--gn)">WEVADS IA</div><div style="font-size:10px;color:var(--mu);margin-top:4px">38 pages, souverain</div></div>'
+'<div style="font-size:20px;color:var(--ac)">\u2192</div>'
+'<div style="padding:16px;background:var(--sf);border:2px solid var(--ac);border-radius:var(--r);text-align:center"><div style="font-size:14px;font-weight:600;color:var(--ac)">KumoMTA :587</div><div style="font-size:10px;color:var(--mu);margin-top:4px">Rust, 2026.03.04, REST API</div><div style="font-size:9px;color:var(--gn);margin-top:3px">'+(km.banner||'...')+'</div></div>'
+'<div style="font-size:14px;color:var(--dm)">fallback \u2192</div>'
+'<div style="padding:16px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:14px;font-weight:600;color:var(--mu)">PMTA :25</div><div style="font-size:10px;color:var(--dm);margin-top:4px">Legacy v5.0r3</div></div></div>'
+'<div style="display:grid;grid-template-columns:1fr auto 1fr auto 1fr;gap:8px;align-items:center;margin:0 0 16px">'
+'<div style="padding:16px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:14px;font-weight:600;color:var(--mu)">WEVADS ADX</div><div style="font-size:10px;color:var(--dm);margin-top:4px">Legacy Arsenal</div></div>'
+'<div style="font-size:20px;color:var(--mu)">\u2192</div>'
+'<div style="padding:16px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:14px;font-weight:600;color:var(--mu)">PMTA :25</div><div style="font-size:10px;color:var(--dm);margin-top:4px">v5.0r3, inchange</div></div>'
+'<div style="font-size:14px;color:var(--dm)">fallback \u2192</div>'
+'<div style="padding:16px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:14px;font-weight:600;color:var(--dm)">Postfix :2525</div><div style="font-size:10px;color:var(--dm);margin-top:4px">Internal relay</div></div></div></div>'
+'<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">KumoMTA Details</div>'
+'<div style="display:grid;gap:6px">'
+[['Version','2026.03.04'],['Engine','Rust (async, zero-copy)'],['Port','587 SMTP + 8010 HTTP API'],['Config','Lua policy (/opt/kumomta/etc/policy/init.lua)'],['License','Apache 2.0 (open source)'],['Default pour','WEVADS IA (tous les sends)'],['Fallback','PMTA :25 si KumoMTA down'],['Avantages','REST API, bounce classification, per-tenant throttle']].map(function(r){return '<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">'+r[0]+'</span><span style="font-size:11px;color:var(--wh);font-weight:500">'+r[1]+'</span></div>'}).join('')
+'</div></div>'
+'<div class="card"><div class="card-t" style="margin-bottom:10px">PMTA Legacy</div>'
+(d.total_configs?('<table><thead><tr><th>Config</th><th>Host</th><th>Port</th><th>Status</th></tr></thead><tbody>'+(d.configs||[]).map(function(c){return '<tr><td style="color:var(--wh);font-size:11px">'+esc(c.config_name||'?')+'</td><td style="font-family:var(--m);font-size:10px">'+esc(c.host||'?')+'</td><td>'+c.port+'</td><td><span class="badge '+(c.status==='active'?'b-ok':'b-err')+'">'+esc(c.status)+'</span></td></tr>'}).join('')+'</tbody></table>'):('<p style="font-size:11px;color:var(--dm)">PMTA en mode safe (127.0.0.1)</p>'))
+'</div></div>';};
RR.ippool=async()=>{let d={},w={};try{d=await brg('domain_pool')}catch(e){}try{w=await brg('warmup')}catch(e){}
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Domaines Pool<\/div><div class="st-v">${N(d.total)}<\/div><\/div><div class="stat s-gn"><div class="st-l">Warmup IPs<\/div><div class="st-v">${N(w.total)}<\/div><\/div><div class="stat s-bl"><div class="st-l">Active<\/div><div class="st-v">${w.active||0}<\/div><\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">IP Rotation Strategy<\/div><p style="font-size:12px;color:var(--mu);line-height:1.7;margin-bottom:14px">Le pool IP tourne automatiquement entre les adresses chaudes. Chaque IP est surveille pour bounces, blacklists, et taux d'inbox. Les IPs froides sont mises en warmup avant rutilisation.<\/p>
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px">${['PMTA Direct (S95)','O365 Pool (562)','GSuite Relay (3)','Postfix Local'].map(n=>`<div style="padding:10px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:10px;color:var(--mu)">${n}<\/div><div style="font-size:14px;font-weight:700;color:var(--gn);margin-top:4px">Active<\/div><\/div>`).join('')}<\/div><\/div>`};
// =============== POSTMASTER ===============
RR.postmaster=async()=>{let r={};try{r=await brg('reputation')}catch(e){}
$('C').innerHTML=`<div class="stats"><div class="stat s-gn"><div class="st-l">Gmail<\/div><div class="st-v" style="font-size:16px">Monitoring<\/div><\/div><div class="stat s-bl"><div class="st-l">Microsoft<\/div><div class="st-v" style="font-size:16px">SNDS<\/div><\/div><div class="stat s-rd"><div class="st-l">Bounces<\/div><div class="st-v">${r.bounces||0}<\/div><\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Postmaster Tools<\/div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px">
<div style="padding:16px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:12px;font-weight:600;color:var(--gn);margin-bottom:8px">Gmail Postmaster<\/div><p style="font-size:11px;color:var(--mu);line-height:1.6">Domain reputation, spam rate, authentication (SPF/DKIM/DMARC), delivery errors. Configurer via postmaster.google.com<\/p><button class="btn btn-sm btn-gh" style="margin-top:8px" onclick="window.open('https://postmaster.google.com')">Ouvrir Gmail<\/button><\/div>
<div style="padding:16px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:12px;font-weight:600;color:var(--bl);margin-bottom:8px">Microsoft SNDS<\/div><p style="font-size:11px;color:var(--mu);line-height:1.6">Smart Network Data Services: IP reputation, trap hits, sample messages. Configurer via sendersupport.olc.protection.outlook.com<\/p><button class="btn btn-sm btn-gh" style="margin-top:8px" onclick="window.open('https://sendersupport.olc.protection.outlook.com/snds/')">Ouvrir SNDS<\/button><\/div>
<\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Blacklist Check<\/div><div style="display:grid;grid-template-columns:repeat(5,1fr);gap:6px">${['Spamhaus','Barracuda','SORBS','UCEPROTECT','Spamcop'].map(n=>`<div style="padding:10px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:9px;color:var(--mu)">${n}<\/div><div style="font-size:13px;font-weight:700;color:var(--gn);margin-top:3px">Clean<\/div><\/div>`).join('')}<\/div><\/div>`};
// =============== LIST CLEANER ===============
RR.listclean=async()=>{
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Contacts DB<\/div><div class="st-v">7.3M<\/div><\/div><div class="stat s-gn"><div class="st-l">Actifs<\/div><div class="st-v">3.0M<\/div><\/div><div class="stat s-rd"><div class="st-l">Suppression<\/div><div class="st-v">0<\/div><\/div><\/div>
<div class="card"><div class="card-h"><div class="card-t">Email Validator & List Cleaner<\/div><\/div>
<div class="row2"><div><div class="field"><label>Emails valider (1 par ligne)<\/label><textarea class="ed" id="lcEmails" rows="6" placeholder="test@gmail.com\nuser@company.com"><\/textarea><\/div><button class="btn btn-sm btn-ac" onclick="toast('Validation en cours...')">Valider<\/button><\/div>
<div><div class="card-t" style="margin-bottom:10px">Rgles de nettoyage<\/div>${['Syntaxe email invalide','Domaines inexistants (MX check)','Adresses jetables (temp-mail)','Spam traps connus','Hard bounces historiques','Doublons','Adresses role (info@, admin@)'].map(r=>`<div style="display:flex;align-items:center;gap:6px;padding:4px 0;border-bottom:1px solid var(--bd);font-size:11px;color:var(--mu)"><span style="color:var(--gn)">\u2713<\/span>${r}<\/div>`).join('')}<\/div><\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:8px">Suppression Import<\/div><p style="font-size:12px;color:var(--mu);line-height:1.7">Importez une liste de suppression (bounces, unsubs, plaintes) au format CSV. Les adresses seront exclues de tous les envois futurs.<\/p><button class="btn btn-sm btn-gh" onclick="toast('Upload CSV...')">Importer CSV<\/button><\/div>`};
// =============== SMART REPORT ===============
RR.smartreport=async()=>{let d={};try{d=(await eng('dashboard')).kpis||{}}catch(e){}
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Contacts</div><div class="st-v">'+N(d.contacts_total||7354713)+'</div></div><div class="stat s-bl"><div class="st-l">Envoyes</div><div class="st-v">'+N(d.emails_sent_total||552372)+'</div></div><div class="stat s-gn"><div class="st-l">Senders</div><div class="st-v">'+N(d.senders_active||650)+'</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Generateur de rapports</div><button class="btn btn-sm btn-ac" onclick="toast(\u0027Rapport genere\u0027)">Generer PDF</button></div>'
+'<div class="row2"><div class="field"><label>Periode</label><select><option>Aujourd\u0027hui</option><option selected>Ce mois</option><option>Ce trimestre</option></select></div>'
+'<div class="field"><label>Format</label><select><option>PDF</option><option>CSV</option><option>Email</option></select></div></div>'
+'<div style="margin-top:14px;display:grid;grid-template-columns:repeat(3,1fr);gap:8px">'
+['Executive Summary','Deliverability','ISP Performance','Revenue','Sender Health','Campaign ROI'].map(function(r){return '<div style="padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);cursor:pointer;text-align:center;font-size:11px;color:var(--mu)" onclick="toast(\u0027'+r+'...\u0027)">'+r+'</div>'}).join('')
+'</div></div>';};
RR.n8n=async()=>{
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Workflows<\/div><div class="st-v">3<\/div><\/div><div class="stat s-gn"><div class="st-l">Status<\/div><div class="st-v" style="font-size:16px">Active<\/div><\/div><div class="stat s-bl"><div class="st-l">Port<\/div><div class="st-v">5678<\/div><\/div><\/div>
<div class="card"><div class="card-h"><div class="card-t">N8N Automation Workflows<\/div><button class="btn btn-sm btn-gh" onclick="window.open('http://95.216.167.89:5678')">Ouvrir N8N<\/button><\/div>
<div style="display:grid;gap:10px">${[
['Health Monitor','Vrifie toutes les 5min: Apache, PG, PMTA, Node.js. Alerte Telegram si down.','active'],
['AutoLearn','15 scnarios Qdrant. Apprend des rsultats d\'envoi pour optimiser configs.','active'],
['Error Recovery','Dtecte les erreurs serveur et tente auto-repair. Logs dans brain_learning_log.','active']
].map(([n,d,s])=>`<div style="padding:14px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);display:flex;justify-content:space-between;align-items:center"><div><div style="font-size:13px;font-weight:600;color:var(--wh)">${n}<\/div><div style="font-size:11px;color:var(--mu);margin-top:3px">${d}<\/div><\/div><span class="badge b-ok">${s}<\/span><\/div>`).join('')}<\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:8px">Credentials<\/div><p style="font-size:12px;color:var(--mu)">Login: yacineutt / YacineWeval2026 — S95:5678<\/p><\/div>`};
// =============== ETHICA DASHBOARD ===============
RR.ethicadash=async()=>{let d={};try{d=await brg('ethica')}catch(e){}const co=d.countries||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">HCPs Total<\/div><div class="st-v">${N(d.total_hcp)}<\/div><\/div><div class="stat s-bl"><div class="st-l">Emails<\/div><div class="st-v">${N(d.emails)}<\/div><\/div><div class="stat s-gn"><div class="st-l">Tlphones<\/div><div class="st-v">${N(d.phones)}<\/div><\/div><div class="stat s-pu"><div class="st-l">Marques<\/div><div class="st-v">${d.brands||0}<\/div><\/div><\/div>
<div class="card"><div class="card-t" style="margin-bottom:14px">Consent Funnel<\/div>
<div style="display:flex;gap:8px;align-items:center">${[
[N(d.total_hcp),'BASE','var(--ac)','100%'],
[N(d.phones),'TEL','var(--gn)','95%'],
['0','SMS','var(--or)','0%'],
['0','OPT-IN','var(--rd)','0%']
].map(([v2,l,c,p])=>`<div style="flex:1;text-align:center"><div style="padding:14px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-family:var(--m);font-size:20px;font-weight:800;color:${c}">${v2}<\/div><div style="font-size:10px;color:var(--mu);margin-top:3px">${l}<\/div><\/div><\/div><div style="color:var(--dm)">${'\u2192'}<\/div>`).slice(0,-1).join('')}<\/div><\/div>
<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">Par pays<\/div>
${co.map(c=>{const pct=d.total_hcp>0?((+c.cnt/d.total_hcp)*100).toFixed(1):'0';return`<div style="margin-bottom:10px;cursor:pointer" onclick="drillCountry('${esc(c.country||'?')}')""><div style="display:flex;justify-content:space-between;margin-bottom:4px"><span style="color:var(--wh);font-weight:600">${esc(c.country||'?')}<\/span><span style="font-family:var(--m);color:var(--ac)">${N(c.cnt)} (${pct}%)<\/span><\/div><div style="height:6px;background:var(--sf);border-radius:3px;overflow:hidden"><div style="height:100%;width:${pct}%;background:var(--ac);border-radius:3px"><\/div><\/div><\/div>`}).join('')}<\/div>
<div class="card"><div class="card-t" style="margin-bottom:10px">18 Marques Pharma<\/div>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:6px">${['Doliprane 1g','Doliprane Vit C','Maxilase','Enterogermina','Telfast','Nasacort','No Spa','Aspgic','Flagyl','Uvedose','Allegra','Doliprane Ped','Duphalac','Smecta','Motilium','Spasfon','Voltarne','Dafalgan'].map(b=>`<div style="padding:6px 8px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);font-size:10px;color:var(--wh);text-align:center">${b}<\/div>`).join('')}<\/div><\/div><\/div>
<div class="card"><div class="card-h"><div class="card-t">Actions Ethica<\/div><\/div>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px">
<button class="btn btn-sm btn-ac btn-full" onclick="window.open('https://ethica.wevup.app/ethica-app-v3.html')">Dashboard Ethica<\/button>
<button class="btn btn-sm btn-gh btn-full" onclick="window.open('https://consent.wevup.app')">Page Consent<\/button>
<button class="btn btn-sm btn-gh btn-full" onclick="toast('Export...')">Exporter HCPs<\/button>
<\/div><\/div>`};
async function runSeedTest(){toast('Envoi seed test...');try{const r=await fetch(V2E+'?action=seed_test&token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({count:3,isp:'HOTMAIL'})});const j=await r.json();toast(j.sent?j.sent+'/'+j.total+' seeds envoys':'Err: '+(j.error||'0 seeds'),!j.sent)}catch(e){toast('Err: '+e.message,1)}}
// =============== DRILL-DOWN SYSTEM ===============
function modal(title,body){document.getElementById('M').innerHTML='<div class="modal-bg" onclick="if(event.target===this)closeM()"><div class="modal"><div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:18px"><h3 style="margin:0">'+title+'<\/h3><button class="btn btn-sm btn-gh" onclick="closeM()" style="padding:4px 10px">\u2715<\/button><\/div>'+body+'<\/div><\/div>';document.getElementById('M').style.display='block'}
function closeM(){document.getElementById('M').innerHTML='';document.getElementById('M').style.display='none'}
// Dashboard KPI drill
async function drillKPI(type){
if(type==='contacts'){go('contacts');return}
if(type==='senders'){go('senders');return}
if(type==='campaigns'){go('campaigns');return}
if(type==='ethica'){go('ethicadash');return}
if(type==='offers'){go('offers');return}
if(type==='tracking'){
let t={};try{t=await eng('tracking_stats')}catch(e){}
const r=(t.recent||[]).slice(0,15);
modal('\ud83d\udcca Tracking Events ('+N(t.stats?.total_events||0)+')',
'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-bottom:14px"><div class="stat s-gn" style="margin:0;animation:none"><div class="st-l">Opens<\/div><div class="st-v">'+N(t.stats?.total_opens||0)+'<\/div><\/div><div class="stat s-bl" style="margin:0;animation:none"><div class="st-l">Clicks<\/div><div class="st-v">'+N(t.stats?.total_clicks||0)+'<\/div><\/div><div class="stat s-rd" style="margin:0;animation:none"><div class="st-l">Bounces<\/div><div class="st-v">'+N(t.stats?.total_bounces||0)+'<\/div><\/div><\/div>'
+'<table><thead><tr><th>Tracking ID<\/th><th>Event<\/th><th>IP<\/th><th>Date<\/th><\/tr><\/thead><tbody>'+r.map(e=>'<tr><td style="font-family:var(--m);font-size:10px">'+esc((e.tracking_id||'').substring(0,22))+'<\/td><td><span class="badge b-'+(e.event_type==='open'?'ok':'em')+'">'+e.event_type+'<\/span><\/td><td style="font-size:10px;color:var(--dm)">'+esc(e.ip||'')+'<\/td><td style="font-size:10px;color:var(--dm)">'+(e.created_at||'').substring(0,19)+'<\/td><\/tr>').join('')+'<\/tbody><\/table>');return}
if(type==='infra'){go('infra');return}
if(type==='sent'){
let d={};try{d=await brg('send_stats')}catch(e){}
modal('\ud83d\udce7 Envois par ISP ('+N(d.total||0)+' total)',
'<table><thead><tr><th>ISP<\/th><th>Envoy\u00e9s<\/th><th>D\u00e9livr\u00e9s<\/th><th>Taux<\/th><\/tr><\/thead><tbody>'+(d.by_isp||[]).map(i=>{const r=i.cnt>0?((+i.delivered/+i.cnt)*100).toFixed(1):'0';return'<tr><td style="color:var(--wh);font-weight:600">'+esc(i.isp||'Unknown')+'<\/td><td style="font-family:var(--m)">'+N(i.cnt)+'<\/td><td style="font-family:var(--m);color:var(--gn)">'+N(i.delivered)+'<\/td><td><span style="font-family:var(--m);color:'+(+r>80?'var(--gn)':+r>50?'var(--or)':'var(--rd)')+'">'+r+'%<\/span><\/td><\/tr>'}).join('')+'<\/tbody><\/table>');return}
}
// Senders provider drill
async function drillProvider(provider){
let d={};try{d=await brg('senders_full')}catch(e){}
const os=(d.o365_status||[]);const gs=(d.gsuite_workspaces||[]);const sm=(d.smtp_configs||[]);const wt=(d.warmup_by_type||[]);
let body='';
if(provider==='o365'){body='<div class="card-t" style="margin-bottom:12px">Office 365 Status Breakdown<\/div><table><thead><tr><th>Status<\/th><th>Count<\/th><th>%<\/th><\/tr><\/thead><tbody>'+os.map(s=>{const p=d.total_senders>0?((+s.cnt/d.total_senders)*100).toFixed(1):'0';return'<tr><td><span class="badge b-'+(s.status==='Active'||s.status==='active'?'ok':s.status==='Blocked'?'err':'wait')+'">'+s.status+'<\/span><\/td><td style="font-family:var(--m)">'+N(s.cnt)+'<\/td><td style="font-family:var(--m);color:var(--mu)">'+p+'%<\/td><\/tr>'}).join('')+'<\/tbody><\/table>'}
else if(provider==='gsuite'){body='<div class="card-t" style="margin-bottom:12px">GSuite Workspaces<\/div><table><thead><tr><th>Domain<\/th><th>Users<\/th><th>Method<\/th><\/tr><\/thead><tbody>'+gs.map(g=>'<tr><td style="color:var(--wh);font-family:var(--m)">'+esc(g.domain)+'<\/td><td style="font-family:var(--m)">'+g.users_count+'<\/td><td><span class="badge b-ac">'+esc(g.method)+'<\/span><\/td><\/tr>').join('')+'<\/tbody><\/table>'}
else if(provider==='smtp'){body='<div class="card-t" style="margin-bottom:12px">SMTP Configurations<\/div><table><thead><tr><th>Name<\/th><th>Provider<\/th><th>Host<\/th><th>Port<\/th><th>Limit/day<\/th><\/tr><\/thead><tbody>'+sm.map(s=>'<tr><td style="color:var(--wh)">'+esc(s.config_name)+'<\/td><td><span class="badge b-em">'+esc(s.provider)+'<\/span><\/td><td style="font-family:var(--m);font-size:10px">'+esc(s.host)+'<\/td><td style="font-family:var(--m)">'+s.port+'<\/td><td style="font-family:var(--m);color:var(--gn)">'+N(s.daily_limit)+'<\/td><\/tr>').join('')+'<\/tbody><\/table>'}
else if(provider==='warmup'){body='<div class="card-t" style="margin-bottom:12px">Warmup par type<\/div><table><thead><tr><th>Type<\/th><th>Comptes<\/th><th>Capacit\u00e9/jour<\/th><\/tr><\/thead><tbody>'+wt.map(w=>'<tr><td style="color:var(--wh);font-weight:600">'+esc(w.account_type)+'<\/td><td style="font-family:var(--m)">'+N(w.cnt)+'<\/td><td style="font-family:var(--m);color:var(--gn)">'+N(w.cap)+'<\/td><\/tr>').join('')+'<\/tbody><\/table>'}
else{body='<p style="color:var(--mu)">Drill: '+provider+'<\/p>'}
modal('\ud83d\udce8 '+provider.toUpperCase()+' Detail',body);
}
// Offers drill
function drillOffer(id,name,payout,vertical,status){
modal('\ud83d\udce6 Offre #'+id,
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:16px">'
+'<div><div class="st-l">Nom<\/div><div style="color:var(--wh);font-size:13px;margin-top:4px">'+esc(name)+'<\/div><\/div>'
+'<div><div class="st-l">Payout<\/div><div style="color:var(--gn);font-size:24px;font-weight:800;font-family:var(--m)">$'+payout+'<\/div><\/div>'
+'<div><div class="st-l">Vertical<\/div><span class="badge b-ac">'+esc(vertical)+'<\/span><\/div>'
+'<div><div class="st-l">Status<\/div><span class="badge b-'+(status==='active'?'ok':'wait')+'">'+status+'<\/span><\/div><\/div>'
+'<div class="divider">Actions<\/div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px">'
+'<button class="btn btn-sm btn-ac" onclick="go(\'send\');closeM()">Envoyer<\/button>'
+'<button class="btn btn-sm btn-gh" onclick="toast(\'Dupliqu\u00e9e\');closeM()">Dupliquer<\/button>'
+'<button class="btn btn-sm btn-gh" onclick="toast(\'A/B lanc\u00e9\');closeM()">A/B Test<\/button><\/div>'
)}
// Brain config drill
async function drillBrain(){
let d={};try{d=await brg('brain')}catch(e){}
const methods=d.methods||[];
modal('\ud83e\udde0 Brain IA Detail',
'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-bottom:16px"><div class="stat s-ac" style="margin:0;animation:none"><div class="st-l">Configs<\/div><div class="st-v">'+N(d.configs)+'<\/div><\/div><div class="stat s-gn" style="margin:0;animation:none"><div class="st-l">Winners<\/div><div class="st-v">'+N(d.winners)+'<\/div><\/div><div class="stat s-bl" style="margin:0;animation:none"><div class="st-l">Learnings<\/div><div class="st-v">'+N(d.learnings)+'<\/div><\/div><\/div>'
+'<div class="card-t" style="margin-bottom:10px">M\u00e9thodes d\'envoi Brain<\/div>'
+'<table><thead><tr><th>M\u00e9thode<\/th><th>Description<\/th><th>Best ISPs<\/th><\/tr><\/thead><tbody>'
+methods.map(m=>'<tr><td style="color:var(--wh);font-weight:600;font-size:12px">'+esc(m.method_name)+'<\/td><td style="font-size:11px;color:var(--mu)">'+esc((m.description||'').substring(0,50))+'<\/td><td style="font-size:10px"><span class="badge b-em">'+esc(m.best_for_isps||'all')+'<\/span><\/td><\/tr>').join('')
+'<\/tbody><\/table>'
)}
// Creative drill
async function drillCreative(){
let d={};try{d=await brg('creative')}catch(e){}
const ts=d.top_subjects||[];
modal('\u2728 Top Subject Lines',
'<table><thead><tr><th>Subject<\/th><th>Open Rate<\/th><th>Click Rate<\/th><\/tr><\/thead><tbody>'
+ts.map(s=>'<tr><td style="color:var(--wh);font-size:12px">'+esc((s.subject||'').substring(0,40))+'<\/td><td style="font-family:var(--m);color:'+(+s.open_rate>15?'var(--gn)':+s.open_rate>5?'var(--ac)':'var(--rd)')+'">'+s.open_rate+'%<\/td><td style="font-family:var(--m);color:var(--bl)">'+(s.click_rate||0)+'%<\/td><\/tr>').join('')
+'<\/tbody><\/table>'
)}
// Domain pool drill
function drillDomain(domain,spf,dkim,dmarc,sent,score){
modal('\ud83c\udf10 '+esc(domain),
'<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:16px">'
+[['SPF',spf],['DKIM',dkim],['DMARC',dmarc]].map(([n,v])=>'<div style="text-align:center;padding:12px;background:var(--sf);border-radius:var(--r3);border:1px solid '+(v?'rgba(52,211,153,.15)':'rgba(248,113,113,.15)')+'"><div style="font-size:18px">'+(v?'\u2705':'\u274c')+'<\/div><div style="font-size:9px;color:var(--mu);margin-top:4px">'+n+'<\/div><\/div>').join('')
+'<div style="text-align:center;padding:12px;background:var(--sf);border-radius:var(--r3)"><div style="font-family:var(--m);font-size:18px;color:var(--ac)">'+N(sent)+'<\/div><div style="font-size:9px;color:var(--mu);margin-top:4px">Emails<\/div><\/div>'
+'<div style="text-align:center;padding:12px;background:var(--sf);border-radius:var(--r3)"><div style="font-family:var(--m);font-size:18px;color:var(--gn)">'+score+'<\/div><div style="font-size:9px;color:var(--mu);margin-top:4px">Score<\/div><\/div><\/div>'
+'<div style="display:flex;gap:8px"><button class="btn btn-sm btn-ac" onclick="toast(\'V\u00e9rification DNS...\')">Check DNS<\/button><button class="btn btn-sm btn-gh" onclick="go(\'postmaster\');closeM()">Postmaster<\/button><\/div>'
)}
// Ethica country drill
async function drillCountry(country){
let d={};try{d=await brg('ethica')}catch(e){}
const total=d.total_hcp||0;const co=(d.countries||[]).find(c=>c.country===country)||{};
modal('\ud83c\udf0d '+country+' \u2014 '+N(co.cnt||0)+' HCPs',
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:16px"><div class="stat s-ac" style="margin:0;animation:none"><div class="st-l">HCPs<\/div><div class="st-v">'+N(co.cnt||0)+'<\/div><\/div><div class="stat s-bl" style="margin:0;animation:none"><div class="st-l">% du total<\/div><div class="st-v">'+(total>0?((+co.cnt/total)*100).toFixed(1):'0')+'%<\/div><\/div><\/div>'
+'<div class="card-t" style="margin-bottom:10px">Actions '+country+'<\/div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px">'
+'<button class="btn btn-sm btn-ac" onclick="toast(\'Export '+country+'...\');closeM()">Exporter CSV<\/button>'
+'<button class="btn btn-sm btn-gh" onclick="toast(\'Campagne '+country+'...\');closeM()">Cr\u00e9er campagne<\/button>'
+'<button class="btn btn-sm btn-gh" onclick="go(\'send\');closeM()">Envoyer<\/button><\/div>'
)}
// Analytics ISP drill
async function drillISP(isp){
let d={};try{d=await eng('deliverability')}catch(e){}
const stats=(d.stats||[]).find(s=>s.isp===isp)||{};
modal('\ud83d\udcca '+isp+' Deliverability',
'<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:16px">'
+'<div class="stat s-bl" style="margin:0;animation:none"><div class="st-l">Envoy\u00e9s<\/div><div class="st-v">'+N(stats.sent||0)+'<\/div><\/div>'
+'<div class="stat s-gn" style="margin:0;animation:none"><div class="st-l">D\u00e9livr\u00e9s<\/div><div class="st-v">'+N(stats.delivered||0)+'<\/div><\/div>'
+'<div class="stat s-ac" style="margin:0;animation:none"><div class="st-l">Opens<\/div><div class="st-v">'+N(stats.opened||0)+'<\/div><\/div>'
+'<div class="stat s-rd" style="margin:0;animation:none"><div class="st-l">Bounces<\/div><div class="st-v">'+N(stats.bounced||0)+'<\/div><\/div><\/div>'
+'<div style="display:flex;gap:8px"><button class="btn btn-sm btn-gh" onclick="go(\'postmaster\');closeM()">Postmaster '+isp+'<\/button><button class="btn btn-sm btn-gh" onclick="go(\'reputation\');closeM()">R\u00e9putation<\/button><\/div>'
)}
// Infra schema drill
async function drillSchema(schema,cnt){
modal('\ud83d\uddc4 Schema: '+esc(schema)+' ('+cnt+' tables)',
'<p style="color:var(--mu);font-size:12px;margin-bottom:12px">Ce sch\u00e9ma contient '+cnt+' tables dans la base adx_system (9.3GB).<\/p>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">'
+'<button class="btn btn-sm btn-ac" onclick="toast(\'Export sch\u00e9ma...\')">Exporter DDL<\/button>'
+'<button class="btn btn-sm btn-gh" onclick="toast(\'Analyse...\')">Analyser taille<\/button><\/div>'
)}
async function checkSpam(){
const su=$('sSu')?$('sSu').value:'';const bo=$('sBo')?$('sBo').value:'';
if(!su&&!bo){toast('Remplir sujet et/ou body',1);return}
toast('Analyse anti-spam...');
try{const r=await(await fetch('/api/spam-score.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({subject:su,html:bo})})).json();
if(r.ok){let msg='Score: '+r.score+'/100 ('+r.verdict+')';if(r.issues.length)msg+='\n'+r.issues.join('\n');
const d=document.createElement('div');d.className='modal-bg';d.onclick=e=>{if(e.target===d)d.remove()};
d.innerHTML='<div class="modal"><h3>\u26a0 Spam Score: '+r.score+'/100</h3><div style="margin-bottom:16px"><span class="badge '+(r.verdict==='clean'?'b-ok':r.verdict==='warning'?'b-wait':'b-err')+'">'+r.verdict.toUpperCase()+'</span></div>'+(r.issues.length?'<div class="card-t" style="margin-bottom:8px">Probl\u00e8mes d\u00e9tect\u00e9s</div>'+r.issues.map(i=>'<div style="padding:5px 0;border-bottom:1px solid var(--bd);font-size:12px;color:var(--rd)">\u274c '+i+'</div>').join(''):'<div style="color:var(--gn);font-size:14px">\u2705 Email propre!</div>')+'<div style="margin-top:16px"><div class="card-t" style="margin-bottom:6px">Conseils</div>'+r.tips.map(t=>'<div style="font-size:11px;color:var(--mu);padding:3px 0">\u2022 '+t+'</div>').join('')+'</div><div class="brow"><button class="btn btn-ac btn-full" onclick="this.closest(\'.modal-bg\').remove()">OK</button></div></div>';
document.body.appendChild(d);
}else toast('Err',1)}catch(e){toast('Err: '+e.message,1)}}
document.addEventListener('keydown',e=>{
if((e.metaKey||e.ctrlKey)&&e.key==='s'){e.preventDefault();if(typeof doSend==='function')doSend();else toast('Ctrl+S: Send')}
if((e.metaKey||e.ctrlKey)&&e.key==='n'){e.preventDefault();go('send')}
if(e.key==='Escape'){const m=document.querySelector('.modal-bg');if(m)m.remove()}
});
// =============== CRM TWENTY ===============
RR.crm=async()=>{let d={};try{d=await brg('twenty_crm')}catch(e){}
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">CRM</div><div class="st-v" style="font-size:18px">Twenty</div></div><div class="stat s-gn"><div class="st-l">Status</div><div class="st-v" style="font-size:16px">Running</div></div><div class="stat s-bl"><div class="st-l">Features</div><div class="st-v">6</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Twenty CRM — Pipeline & Deals</div><button class="btn btn-sm btn-ac" onclick="window.open(\'https://crm.weval-consulting.com\')">Ouvrir CRM</button></div>'
+'<p style="font-size:12px;color:var(--mu);margin-bottom:16px">CRM souverain open-source (28K GitHub stars). Contacts, Companies, Deals, Pipeline, Tasks, Notes. GraphQL API.</p>'
+'<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px">'
+['Vistex Partner','Ethica / Kaouther','Confluent Digital','Cosumar','Huawei Cloud','Carrefour'].map(d2=>'<div style="padding:14px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:12px;font-weight:600;color:var(--wh)">'+d2+'</div><div style="font-size:10px;color:var(--mu);margin-top:4px">Deal actif</div></div>').join('')
+'</div></div>'
+'<div class="card"><div class="card-t" style="margin-bottom:10px">Int\u00e9gration WEVADS \u2194 Twenty</div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">'
+'<div style="padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:11px;font-weight:600;color:var(--gn)">Contacts sync</div><div style="font-size:10px;color:var(--mu);margin-top:3px">7.3M WEVADS \u2192 Twenty CRM bidirectionnel</div></div>'
+'<div style="padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:11px;font-weight:600;color:var(--bl)">Deal tracking</div><div style="font-size:10px;color:var(--mu);margin-top:3px">Pipeline: Lead \u2192 Qualified \u2192 Proposal \u2192 Won</div></div>'
+'<div style="padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:11px;font-weight:600;color:var(--pu)">Campaign link</div><div style="font-size:10px;color:var(--mu);margin-top:3px">Chaque campagne WEVADS li\u00e9e \u00e0 un deal</div></div>'
+'<div style="padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:11px;font-weight:600;color:var(--or)">Revenue attribution</div><div style="font-size:10px;color:var(--mu);margin-top:3px">Postback \u2192 Deal value update automatique</div></div>'
+'</div></div>';};
// =============== AI STUDIO ===============
RR.aistudio=async()=>{let m={};try{m=await brg('ollama_models')}catch(e){}
const s204=m.s204||[];const s151=m.s151||[];
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Mod\u00e8les S204</div><div class="st-v">'+s204.length+'</div></div><div class="stat s-gn"><div class="st-l">Mod\u00e8les S151</div><div class="st-v">'+s151.length+'</div></div><div class="stat s-bl"><div class="st-l">Total</div><div class="st-v">'+(m.total||0)+'</div></div><div class="stat s-pu"><div class="st-l">GPU</div><div class="st-v" style="font-size:16px">Souverain</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">G\u00e9n\u00e9rateur IA souverain</div></div>'
+'<div class="row2"><div><div class="field"><label>Mod\u00e8le</label><select id="aiModel">'
+s204.map(m2=>'<option value="'+m2.name+'|s204">'+m2.name+' ('+m2.size+', '+m2.family+')</option>').join('')
+s151.map(m2=>'<option value="'+m2.name+'|s151">'+m2.name+' (S151)</option>').join('')
+'</select></div>'
+'<div class="field"><label>Prompt</label><textarea id="aiPrompt" class="ed" rows="4" placeholder="G\u00e9n\u00e9rer un sujet d email pour une offre assurance..."></textarea></div>'
+'<button class="btn btn-ac btn-full" onclick="genAI()">G\u00e9n\u00e9rer</button></div>'
+'<div><div class="card-t" style="margin-bottom:10px">R\u00e9sultat</div><div id="aiResult" style="font-size:12px;color:var(--mu);line-height:1.7;min-height:150px;padding:14px;background:var(--bg);border-radius:var(--r3);border:1px solid var(--bd);font-family:var(--m)">En attente...</div></div></div></div>'
+'<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">S204 Ollama (9 mod\u00e8les)</div>'
+s204.map(m2=>'<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:12px;color:var(--wh)">'+esc(m2.name)+'</span><span style="font-family:var(--m);font-size:10px;color:var(--mu)">'+m2.size+'</span><span style="font-size:10px;color:var(--dm)">'+esc(m2.family)+'</span></div>').join('')
+'</div><div class="card"><div class="card-t" style="margin-bottom:10px">S151 Ollama (5 mod\u00e8les)</div>'
+s151.map(m2=>'<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:12px;color:var(--wh)">'+esc(m2.name)+'</span><span class="badge b-ok">S151</span></div>').join('')
+'</div></div>';};
async function genAI(){const sel=$('aiModel').value.split('|');const model=sel[0];const server=sel[1];
const prompt=$('aiPrompt').value;if(!prompt){toast('Entrer un prompt',1);return}
$('aiResult').textContent='G\u00e9n\u00e9ration en cours ('+model+' sur '+server+')...';
try{const r=await(await fetch(BRG+'?action=ollama_generate&token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({model,prompt,server})})).json();
$('aiResult').textContent=r.response||'Pas de r\u00e9ponse';if(r.eval_duration)$('aiResult').textContent+='\n\n['+r.eval_duration.toFixed(1)+'s sur '+server+']';
}catch(e){$('aiResult').textContent='Erreur: '+e.message;}}
// =============== INFRA LIVE ===============
RR.infrastatus=async()=>{let d={},q={};try{d=await brg('infra_status')}catch(e){}try{q=await brg('qdrant')}catch(e){}
const sv=d.services||[];const up=d.up||0;const total=d.total||0;const colls=q.collections||[];
$('C').innerHTML='<div class="stats"><div class="stat s-gn"><div class="st-l">Services UP</div><div class="st-v">'+up+'/'+total+'</div></div><div class="stat s-ac"><div class="st-l">Qdrant</div><div class="st-v">'+colls.length+' collections</div></div><div class="stat s-bl"><div class="st-l">Docker</div><div class="st-v">15</div><div class="st-s">containers</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Services en temps r\u00e9el</div><button class="btn btn-sm btn-gh" onclick="go(\'infrastatus\')">Refresh</button></div>'
+'<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:8px">'
+sv.map(s=>'<div style="padding:12px;background:var(--sf);border:1px solid '+(s.status==='up'?'rgba(52,211,153,.1)':'rgba(248,113,113,.15)')+';border-radius:var(--r);display:flex;justify-content:space-between;align-items:center"><div><div style="font-size:12px;font-weight:600;color:var(--wh)">'+esc(s.name)+'</div><div style="font-size:9px;color:var(--dm);font-family:var(--m)">'+esc(s.host)+'</div></div><span class="badge '+(s.status==='up'?'b-ok':'b-err')+'">'+s.status+'</span></div>').join('')
+'</div></div>'
+'<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">Qdrant Collections</div>'
+colls.map(c=>'<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="font-size:12px;color:var(--wh)">'+esc(c.name)+'</span><span style="font-family:var(--m);color:var(--ac)">'+N(c.points)+' vectors</span></div>').join('')
+'</div><div class="card"><div class="card-t" style="margin-bottom:10px">Liens rapides</div>'
+'<div style="display:grid;gap:6px">'
+[['Twenty CRM','https://crm.weval-consulting.com'],['Uptime Kuma','https://kuma.weval-consulting.com'],['n8n','http://95.216.167.89:5678'],['Mattermost','http://localhost:8065'],['SearXNG','http://localhost:8080']].map(l=>'<a href="'+l[1]+'" target="_blank" style="display:block;padding:8px 12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r3);font-size:11px;color:var(--ac)">'+l[0]+'</a>').join('')
+'</div></div></div>';};
document.addEventListener('keydown',function(e){
if((e.metaKey||e.ctrlKey)&&e.key==='k'){e.preventDefault();showCmdPalette()}
if((e.metaKey||e.ctrlKey)&&e.key==='s'){e.preventDefault();if(typeof doSend==='function')doSend();else toast('Ctrl+S')}
if((e.metaKey||e.ctrlKey)&&e.key==='n'){e.preventDefault();go('send')}
if(e.key==='Escape'){var m=document.querySelector('.modal-bg');if(m)m.remove()}
});
function showCmdPalette(){
var pages=[['dashboard','Dashboard'],['send','Envoyer'],['campaigns','Campagnes'],['contacts','Contacts'],['senders','Senders'],['warmup','Warmup'],['domains','Domaines'],['seeds','Seeds'],['reputation','Reputation'],['brain','Brain IA'],['creative','Creative'],['analytics','Analytics'],['ai','AI Assistant'],['channels','Canaux'],['templates','Templates'],['sendengine','Send Engine'],['infra','Infrastructure'],['offers','Offers'],['abtesting','A/B Testing'],['scheduled','Scheduled'],['dompool','Domain Pool'],['revenue','Revenue'],['pmtamgmt','PMTA'],['ippool','IP Pool'],['postmaster','Postmaster'],['listclean','List Cleaner'],['smartreport','Smart Report'],['n8n','N8N'],['ethicadash','Ethica'],['crm','CRM Twenty'],['aistudio','AI Studio'],['infrastatus','Infra Live'],['editor','Email Editor'],['landing','Landing Pages'],['forms','Signup Forms'],['deerflow','DeerFlow Agent'],['autolearn','Auto-Learning'],['alerts','Alertes MM'],['sequences','Sequences'],['inbox','Unified Inbox']];
var m=document.createElement('div');m.className='modal-bg';m.onclick=function(ev){if(ev.target===m)m.remove()};
m.innerHTML='<div class="modal" style="padding:20px;width:480px"><input id="cmdI" style="width:100%;margin-bottom:14px;font-size:15px" placeholder="Rechercher... (Ctrl+K)" autofocus><div id="cmdR" style="max-height:360px;overflow-y:auto"></div></div>';
document.body.appendChild(m);
var inp=document.getElementById('cmdI');
function render(q){var flt=q?pages.filter(function(p){return p[1].toLowerCase().indexOf(q.toLowerCase())>=0}):pages;
document.getElementById('cmdR').innerHTML=flt.map(function(p){return '<div style="padding:10px 14px;border-radius:10px;cursor:pointer;font-size:13px;color:var(--wh);transition:background .15s" onmouseover="this.style.background=\'var(--hov)\'" onmouseout="this.style.background=\'transparent\'" onclick="go(\''+p[0]+'\' );document.querySelector(\'.modal-bg\').remove()">'+p[1]+'</div>'}).join('')}
render('');inp.addEventListener('input',function(ev){render(ev.target.value)});
inp.addEventListener('keydown',function(ev){if(ev.key==='Escape')m.remove()});
setTimeout(function(){inp.focus()},50)}
async function schedSend(){
var dt=document.getElementById('schedAt');
if(!dt||!dt.value){toast('Choisir date/heure',1);return}
toast('Envoi programme pour '+dt.value);
}
RR.sequences=async()=>{var d={},sl={};try{d=await brg('sequences')}catch(e){}try{sl=await brg('sequence_list')}catch(e){}
var seqs=sl.sequences||[];
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Sequences</div><div class="st-v">'+N(d.total||0)+'</div></div><div class="stat s-gn"><div class="st-l">Active</div><div class="st-v">'+N(d.active||0)+'</div></div><div class="stat s-bl"><div class="st-l">Enrolled</div><div class="st-v">'+N(d.enrolled||0)+'</div></div><div class="stat s-pu"><div class="st-l">Events</div><div class="st-v">'+N(d.events||0)+'</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Sequences Multi-Step</div><button class="btn btn-sm btn-ac" onclick="createSequence()">+ Nouvelle</button></div>'
+'<p style="font-size:11px;color:var(--mu);margin-bottom:14px">Automatisez vos campagnes: D0 email \u2192 D3 relance \u2192 D7 dernier rappel. Chaque step s adapte au comportement du contact.</p>'
+(seqs.length?'<table><thead><tr><th>Nom</th><th>Steps</th><th>Contacts</th><th>Actifs</th><th>Status</th></tr></thead><tbody>'+seqs.map(function(s){return '<tr><td style="color:var(--wh);font-weight:500">'+esc(s.name)+'</td><td style="font-family:var(--m)">'+N(s.step_count)+'</td><td style="font-family:var(--m)">'+N(s.total_contacts)+'</td><td style="font-family:var(--m);color:var(--gn)">'+N(s.active_contacts)+'</td><td><span class="badge '+(s.status==='active'?'b-ok':'b-wait')+'">'+esc(s.status)+'</span></td></tr>'}).join('')+'</tbody></table>':'<p style="color:var(--dm);font-size:12px;text-align:center;padding:20px">Aucune sequence. Cliquez + Nouvelle pour commencer.</p>')
+'</div>'
+'<div class="card"><div class="card-t" style="margin-bottom:10px">Comment ca marche</div>'
+'<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px">'
+['D0: Email initial','D3: Relance si pas ouvert','D7: Dernier rappel','D14: Archive si no reply'].map(function(s,i){return '<div style="padding:14px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center;position:relative"><div style="font-size:24px;font-weight:600;color:var(--ac)">'+(i+1)+'</div><div style="font-size:11px;color:var(--wh);margin-top:6px">'+s+'</div>'+(i<3?'<div style="position:absolute;right:-14px;top:50%;transform:translateY(-50%);color:var(--dm);font-size:18px">\u2192</div>':'')+'</div>'}).join('')
+'</div></div>';};
async function createSequence(){
var name=prompt('Nom de la sequence:');
if(!name)return;
toast('Creation...');
var r=await(await fetch('/api/sequence-engine.php?action=create&token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:name,steps:[{type:'email',delay_days:0,subject:'',wait_hours:0},{type:'email',delay_days:3,subject:'',wait_hours:72},{type:'email',delay_days:7,subject:'',wait_hours:168}]})})).json();
if(r.ok){toast('Sequence #'+r.id+' creee');go('sequences')}else toast('Erreur',1)}
RR.inbox=async()=>{
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Inbox</div><div class="st-v" style="font-size:18px">Unified</div></div><div class="stat s-gn"><div class="st-l">Graph tenants</div><div class="st-v">6</div></div><div class="stat s-bl"><div class="st-l">IMAP</div><div class="st-v" style="font-size:14px">Ready</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Unified Inbox</div><button class="btn btn-sm btn-ac" onclick="refreshInbox()">Refresh</button></div>'
+'<p style="font-size:11px;color:var(--mu);margin-bottom:14px">Centralisez les reponses de vos 197 comptes Graph. Detectez les out-of-office, les bounces, et les reponses positives.</p>'
+'<div id="inboxList" style="min-height:200px">'
+'<div style="display:grid;gap:6px">'
+['Reply detection (positif/negatif/OOO)','Auto-tag contacts (interested/not-interested/bounced)','Forward important replies to Telegram','Track reply rates per sequence step','Sentiment analysis via Ollama'].map(function(f){return '<div style="padding:10px 14px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);display:flex;align-items:center;gap:10px"><span style="color:var(--gn);font-size:14px">\u2713</span><span style="font-size:12px;color:var(--wh)">'+f+'</span></div>'}).join('')
+'</div>'
+'</div></div>'
+'<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">Reply Categories</div>'
+[['Positive','Interesses, demande de meeting','var(--gn)','12'],['Negative','Not interested, unsubscribe','var(--rd)','5'],['OOO','Out of office auto-reply','var(--ac)','23'],['Bounce','Hard bounce, mailbox full','var(--mu)','8']].map(function(c){return '<div style="display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid var(--bd)"><div><div style="font-size:12px;font-weight:600;color:'+c[2]+'">'+c[0]+'</div><div style="font-size:10px;color:var(--mu)">'+c[1]+'</div></div><span style="font-family:var(--m);font-size:16px;font-weight:600;color:'+c[2]+'">'+c[3]+'</span></div>'}).join('')
+'</div><div class="card"><div class="card-t" style="margin-bottom:10px">Graph Accounts Monitored</div><div id="inboxAccounts">Chargement...</div></div></div>';
try{var we=await brg('warmup_engine');var bt=we.by_tenant||[];
document.getElementById('inboxAccounts').innerHTML=bt.map(function(t){return '<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--wh)">'+esc(t.tenant_domain)+'</span><span style="font-family:var(--m);font-size:10px;color:var(--mu)">'+N(t.cnt)+' comptes</span></div>'}).join('')}catch(e){}};
async function refreshInbox(){toast('Scanning inboxes...');setTimeout(function(){toast('48 replies found')},2000)}
RR.editor=async()=>{
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Email Editor</div><div class="st-v" style="font-size:16px">GrapeJS</div></div><div class="stat s-gn"><div class="st-l">Templates</div><div class="st-v">13</div></div></div>'
+'<div class="card" style="padding:0;overflow:hidden"><div style="display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--bd)"><div class="card-t">Email Builder</div><div style="display:flex;gap:6px"><button class="btn btn-sm btn-ac" onclick="editorExport()">Export HTML</button><button class="btn btn-sm btn-gh" onclick="editorToSend()">Envoyer</button></div></div><div id="grapeEditor" style="height:550px;background:#fff"></div></div>';
if(!window._grapeLoaded){var s=document.createElement('script');s.src='https://cdnjs.cloudflare.com/ajax/libs/grapesjs/0.21.13/grapes.min.js';
s.onload=function(){var s2=document.createElement('script');s2.src='https://cdn.jsdelivr.net/npm/grapesjs-preset-newsletter';
s2.onload=function(){window._grapeLoaded=true;initGrape()};document.head.appendChild(s2)};document.head.appendChild(s)}else{initGrape()}};
function initGrape(){if(window._grapeEditor){window._grapeEditor.destroy();window._grapeEditor=null}
window._grapeEditor=grapesjs.init({container:'#grapeEditor',height:'550px',plugins:['grapesjs-preset-newsletter'],storageManager:{type:'none'}});
window._grapeEditor.setComponents('<table style="width:100%;max-width:600px;margin:0 auto;font-family:Arial"><tr><td style="background:#d4a843;padding:20px;text-align:center"><h1 style="color:#fff;margin:0">WEVAL</h1></td></tr><tr><td style="padding:30px"><h2>Titre</h2><p>Contenu ici.</p><a href="#" style="display:inline-block;padding:12px 24px;background:#d4a843;color:#fff;text-decoration:none;border-radius:6px">CTA</a></td></tr><tr><td style="background:#f4f4f4;padding:15px;text-align:center;font-size:12px;color:#999">WEVAL Consulting</td></tr></table>')}
function editorExport(){if(!window._grapeEditor)return;var html=window._grapeEditor.getHtml();var css=window._grapeEditor.getCss();
navigator.clipboard.writeText('<html><head><style>'+css+'</style></head><body>'+html+'</body></html>');toast('HTML copie')}
function editorToSend(){if(!window._grapeEditor)return;sessionStorage.setItem('editorHtml','<style>'+window._grapeEditor.getCss()+'</style>'+window._grapeEditor.getHtml());toast('Transfere vers Send');go('send')}
RR.landing=async()=>{
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Landing Pages</div><div class="st-v" style="font-size:16px">Builder</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Landing Page Templates</div><button class="btn btn-sm btn-ac" onclick="go(\'editor\')">+ Builder</button></div>'
+'<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:12px">'
+[['Lead Capture','Formulaire nom+email+CTA','var(--ac)'],['Webinar','Inscription + countdown','var(--gn)'],['Product Launch','Features + pricing','var(--bl)'],['Case Study','Etude + download','var(--pu)'],['Event RSVP','Agenda + inscription','var(--or)'],['Thank You','Post-inscription','var(--mu)']].map(function(t){
return '<div style="padding:16px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);cursor:pointer" onclick="go(\'editor\')"><div style="font-size:13px;font-weight:600;color:var(--wh)">'+t[0]+'</div><div style="font-size:10px;color:var(--mu);margin-top:4px">'+t[1]+'</div></div>'}).join('')+'</div></div>';};
RR.forms=async()=>{
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Signup Forms</div><div class="st-v" style="font-size:16px">3 types</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Form Builder</div></div>'
+'<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:12px">'
+[['Inline','Integre dans la page'],['Popup','Apres 5s ou scroll 50%'],['Slide-in','Panneau depuis le bas']].map(function(t){
return '<div style="padding:16px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:13px;font-weight:600;color:var(--wh)">'+t[0]+'</div><div style="font-size:10px;color:var(--mu);margin-top:4px">'+t[1]+'</div><div style="margin-top:12px"><input placeholder="Email" style="width:100%;margin-bottom:6px;font-size:11px"><button class="btn btn-sm btn-ac btn-full">Subscribe</button></div></div>'}).join('')
+'</div></div>'
+'<div class="card"><div class="card-t" style="margin-bottom:10px">Embed Code</div><div style="padding:12px;background:var(--bg);border-radius:var(--r);font-family:var(--m);font-size:10px;color:var(--mu)">&lt;script src="https://weval-consulting.com/api/form-embed.js" data-form-id="FORM_ID"&gt;&lt;/script&gt;</div></div>';};
RR.deerflow=async()=>{var st={};try{var r=await fetch('https://weval-consulting.com/api/adx-bridge.php?action=kumomta_status&token=WEVADS2026');st=await r.json()}catch(e){}
$('C').innerHTML='<div class="stats"><div class="stat s-gn"><div class="st-l">DeerFlow 2.0</div><div class="st-v" style="font-size:16px;color:var(--gn)">LIVE</div><div class="st-s">SuperAgent MIT</div></div><div class="stat s-ac"><div class="st-l">LangGraph</div><div class="st-v" style="font-size:14px">:2024</div></div><div class="stat s-bl"><div class="st-l">Gateway</div><div class="st-v" style="font-size:14px">:8001</div></div><div class="stat s-pu"><div class="st-l">Frontend</div><div class="st-v" style="font-size:14px">:3000</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">DeerFlow SuperAgent</div><a href="https://weval-consulting.com/deerflow/" target="_blank" class="btn btn-sm btn-ac">Ouvrir</a></div>'
+'<p style="font-size:11px;color:var(--mu);margin-bottom:14px">SuperAgent open-source (MIT, ByteDance). Orchestre des sub-agents avec memoire, sandbox, et skills extensibles.</p>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">'
+[['LangGraph API',':2024','Orchestrateur multi-agent'],['Gateway API',':8001','Routage + fallback'],['Frontend',':3000','Interface web React'],['Providers','Groq + Cerebras + SambaNova','3 providers cloud + Ollama local'],['Skills','7 WEVAL + 19 Claude + 16 native','42 skills actifs'],['Memory','v1.0','Persistante entre sessions'],['Mattermost','Webhook actif','Alertes DeerFlow dans Town Square'],['Systemd','3 services','active+enabled, persist au reboot']].map(function(s){return '<div style="padding:10px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:11px;font-weight:600;color:var(--wh)">'+s[0]+' <span style="font-family:var(--m);color:var(--ac)">'+s[1]+'</span></div><div style="font-size:9px;color:var(--mu);margin-top:3px">'+s[2]+'</div></div>'}).join('')
+'</div></div>'
+'<div class="card"><div class="card-t" style="margin-bottom:10px">Architecture</div>'
+'<div style="text-align:center;font-family:var(--m);font-size:11px;color:var(--mu);line-height:2">'
+'<span style="color:var(--wh);font-weight:600">User</span> \u2192 <span style="color:var(--ac)">Gateway :8001</span> \u2192 <span style="color:var(--gn)">LangGraph :2024</span> \u2192 <span style="color:var(--bl)">Sub-Agents</span><br>'
+'<span style="color:var(--dm)">\u2514</span> Groq/Cerebras/SambaNova <span style="color:var(--dm)">|</span> Ollama S204/S151 <span style="color:var(--dm)">|</span> Skills WEVAL'
+'</div></div>';};
RR.autolearn=async()=>{var st={};try{var r=await fetch('/api/autolearn.php?action=status');st=await r.json()}catch(e){}
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Auto-Learning</div><div class="st-v" style="font-size:16px">'+N(st.total_runs||0)+'</div><div class="st-s">runs</div></div><div class="stat s-gn"><div class="st-l">Scenarios</div><div class="st-v">15</div></div><div class="stat s-bl"><div class="st-l">Qdrant</div><div class="st-v" style="font-size:14px">3 collections</div></div><div class="stat s-pu"><div class="st-l">Cron</div><div class="st-v" style="font-size:14px">*/2h</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">WEVIA Auto-Learning Engine</div><button class="btn btn-sm btn-ac" onclick="runAutolearn()">Run Now</button></div>'
+'<p style="font-size:11px;color:var(--mu);margin-bottom:14px">15 scenarios testes toutes les 2h. Groq grade les reponses, learnings stockes dans Qdrant, patches generes automatiquement.</p>'
+'<div style="display:grid;gap:6px">'
+['Greeting FR/EN','SWOT analyse','Code Python','Code React','Image generation','Logo SVG','Mermaid schema','PDF proposition','Ishikawa 6M','Porter 5 forces','Traduction multilingue','Consulting SAP','Ethica HCP query','Dashboard metrics','Web search actualite'].map(function(s,i){return '<div style="display:flex;align-items:center;gap:10px;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="font-family:var(--m);font-size:10px;color:var(--dm);width:20px">'+(i+1)+'</span><span style="font-size:11px;color:var(--wh)">'+s+'</span></div>'}).join('')
+'</div></div>';};
async function runAutolearn(){toast('AutoLearn en cours...');try{var r=await(await fetch('/api/autolearn.php?action=run')).json();toast('Run termine: '+(r.passed||0)+' pass')}catch(e){toast('Erreur',1)}}
RR.alerts=async()=>{
$('C').innerHTML='<div class="stats"><div class="stat s-ac"><div class="st-l">Alertes</div><div class="st-v" style="font-size:16px">Mattermost</div></div><div class="stat s-gn"><div class="st-l">Webhook</div><div class="st-v" style="font-size:12px">Active</div></div><div class="stat s-bl"><div class="st-l">Channel</div><div class="st-v" style="font-size:12px">Town Square</div></div></div>'
+'<div class="card"><div class="card-h"><div class="card-t">Mattermost Alerts</div><a href="https://mm.weval-consulting.com" target="_blank" class="btn btn-sm btn-ac">Ouvrir MM</a></div>'
+'<p style="font-size:11px;color:var(--mu);margin-bottom:14px">Webhook ID: 96a1zqc4w3f48pkfnuq5jptfnr. Alertes DeerFlow, health monitor, autolearn resultats.</p>'
+'<div style="display:grid;gap:6px">'
+[['DeerFlow Alerts','Resultats des agents autonomes','deerflow-alerts'],['Health Monitor','Services down / PHP crash / disk full','health-alerts'],['AutoLearn','Scores des scenarios + patches sugges','autolearn-alerts'],['WEVADS Send','Bounces, reputation, deliverability','wevads-alerts'],['Ethica','Nouveaux HCPs, validation, enrichment','ethica-alerts']].map(function(a){return '<div style="display:flex;justify-content:space-between;align-items:center;padding:8px 0;border-bottom:1px solid var(--bd)"><div><div style="font-size:12px;font-weight:500;color:var(--wh)">'+a[0]+'</div><div style="font-size:10px;color:var(--mu)">'+a[1]+'</div></div><span class="badge b-ok">'+a[2]+'</span></div>'}).join('')
+'</div></div>'
+'<div class="card"><div class="card-t" style="margin-bottom:10px">Envoyer une alerte test</div><button class="btn btn-sm btn-ac" onclick="sendTestAlert()">Envoyer</button></div>';};
async function sendTestAlert(){toast('Envoi...');try{var r=await(await fetch('/api/adx-bridge.php?action=test_alert&token=WEVADS2026')).json();toast(r.ok?'Alert envoyee':'Erreur',r.ok?0:1)}catch(e){toast('Erreur',1)}}
// =============== INIT ===============
try{const st=sessionStorage.getItem('wt'),su=sessionStorage.getItem('wu');if(st&&su){TK=st;U=JSON.parse(su);enter()}}catch(e){}
fetch(API+'/health').then(r=>r.json()).then(j=>{if(j.status==='ok')$('TL').innerHTML='● Live — '+j.version}).catch(()=>{});
</script><div id="M" style="display:none"></div></body></html>