437 lines
25 KiB
HTML
437 lines
25 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>WEVAL Enterprise 3D</title>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;900&family=JetBrains+Mono:wght@400;700&display=swap');
|
|
*{margin:0;padding:0;box-sizing:border-box}body{background:#080810;overflow:hidden;font-family:'Nunito',sans-serif}canvas{display:block}
|
|
#tip{position:fixed;pointer-events:none;display:none;z-index:99;border-radius:14px;padding:14px 18px;color:#e0e8ff;max-width:260px;backdrop-filter:blur(12px);border:2px solid}
|
|
#tip .tn{font-weight:900;font-size:1.05rem;color:#fff}
|
|
#tip .tt{font-size:.65rem;text-transform:uppercase;letter-spacing:2px;margin:3px 0 6px}
|
|
#tip .td{font-size:.82rem;color:#8a98c0;line-height:1.35}
|
|
#tip .tp{font-family:'JetBrains Mono',monospace;font-size:.7rem;color:#53d8fb;border-top:1px solid #ffffff10;padding-top:5px;margin-top:5px}
|
|
#tip .st{font-size:.7rem;margin-top:4px;font-weight:700}
|
|
#hud{position:fixed;top:0;left:0;right:0;padding:10px 24px;display:flex;justify-content:space-between;align-items:center;z-index:10;background:linear-gradient(180deg,#080810ee 60%,transparent)}
|
|
.logo{font-size:1.15rem;font-weight:900;letter-spacing:1px}.logo b{color:#53d8fb}.logo i{color:#e94560;font-style:normal}
|
|
.hr{display:flex;gap:18px;font-size:.72rem;color:#4a5878}.hr b{color:#53d8fb}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="c"></canvas>
|
|
<div id="tip"><div class="tn"></div><div class="tt"></div><div class="td"></div><div class="tp"></div><div class="st"></div></div>
|
|
<div id="hud"><div class="logo"><i>WEVAL</i> <b>Enterprise</b> 3D</div><div class="hr"><span>Agents <b>31</b></span><span>Actifs <b id="ac">0</b></span><span>Tasks <b id="tc">0</b></span></div></div>
|
|
<script>
|
|
const C=document.getElementById('c'),X=C.getContext('2d');
|
|
let W,H,mx=-1,my=-1,hov=null,fr=0,tasks=0;
|
|
const dp=Math.min(devicePixelRatio,2);
|
|
function resize(){W=innerWidth;H=innerHeight;C.width=W*dp;C.height=H*dp;X.scale(dp,dp);layout()}
|
|
addEventListener('resize',resize);
|
|
|
|
// ═══ PYRAMID LEVELS ═══
|
|
const LVLS=[
|
|
{y:.08,rows:[{id:'ceo',label:'Direction',clr:'#e94560',w:.12}]},
|
|
{y:.22,rows:[{id:'consult',label:'Consulting',clr:'#7c3aed',w:.22},{id:'strat',label:'Stratégie',clr:'#3b82f6',w:.22}]},
|
|
{y:.38,rows:[{id:'dev',label:'Développement',clr:'#10b981',w:.25},{id:'infra',label:'Infrastructure',clr:'#f59e0b',w:.18},{id:'sec',label:'Sécurité',clr:'#ef4444',w:.14}]},
|
|
{y:.54,rows:[{id:'sales',label:'Prospection',clr:'#3b82f6',w:.16},{id:'qa',label:'QA & Tests',clr:'#06b6d4',w:.18},{id:'pharma',label:'Pharma',clr:'#d946ef',w:.16},{id:'ops',label:'Monitoring',clr:'#eab308',w:.16}]},
|
|
];
|
|
|
|
// Flatten departments
|
|
const DEPTS=[];
|
|
LVLS.forEach(l=>l.rows.forEach(r=>DEPTS.push({...r,ly:l.y})));
|
|
|
|
// ═══ CHAIN STATIONS ═══
|
|
const STN=[
|
|
{id:'s0',label:'LEADS',clr:'#3b82f6'},{id:'s1',label:'QUALIFY',clr:'#7c3aed'},
|
|
{id:'s2',label:'DESIGN',clr:'#10b981'},{id:'s3',label:'BUILD',clr:'#10b981'},
|
|
{id:'s4',label:'SECURE',clr:'#ef4444'},{id:'s5',label:'TEST',clr:'#06b6d4'},
|
|
{id:'s6',label:'DEPLOY',clr:'#f59e0b'},{id:'s7',label:'DELIVER',clr:'#22c55e'},
|
|
];
|
|
|
|
// ═══ AGENTS with unique visual traits ═══
|
|
const AG=[
|
|
// CEO
|
|
{n:'CEO',e:'👔',dept:'ceo',stn:1,d:'Agent CEO autonome souverain',p:'Stratégie, budget, hiring',
|
|
head:'round',hair:'slick',hairC:'#1a1a1a',skinC:'#e8c8a0',bodyC:'#1a1a2e',acc:'crown',glasses:false,beard:true},
|
|
// Consulting
|
|
{n:'Architect',e:'🏗️',dept:'consult',stn:2,d:'Architecture technique',p:'Blueprints, diagrammes',
|
|
head:'round',hair:'short',hairC:'#2a2a3a',skinC:'#e0c090',bodyC:'#7c3aed',acc:'',glasses:true,beard:false},
|
|
{n:'Planner',e:'📋',dept:'consult',stn:1,d:'Roadmaps & planning',p:'Sprint plans, Gantt',
|
|
head:'round',hair:'side',hairC:'#5a3a1a',skinC:'#f0d0b0',bodyC:'#7c3aed',acc:'',glasses:false,beard:false},
|
|
{n:'DeerFlow',e:'🦌',dept:'consult',stn:1,d:'Deep research multi-sources',p:'Synthèses R&D',
|
|
head:'round',hair:'wild',hairC:'#6a4a2a',skinC:'#e0b890',bodyC:'#7c3aed',acc:'antlers',glasses:false,beard:true},
|
|
// Strategy
|
|
{n:'Critic',e:'⚖️',dept:'strat',stn:1,d:'Validation & risques',p:'Reviews, alertes',
|
|
head:'round',hair:'short',hairC:'#3a3a4a',skinC:'#e8c8a0',bodyC:'#3b82f6',acc:'',glasses:true,beard:false},
|
|
{n:'Brain',e:'💡',dept:'strat',stn:2,d:'Brainstorming créatif',p:'Idées, innovation',
|
|
head:'round',hair:'spiky',hairC:'#eab308',skinC:'#f0d0b0',bodyC:'#3b82f6',acc:'lightbulb',glasses:false,beard:false},
|
|
// Dev
|
|
{n:'Executor',e:'⚡',dept:'dev',stn:3,d:'Exécution & deploy',p:'Scripts, migrations',
|
|
head:'round',hair:'mohawk',hairC:'#22c55e',skinC:'#d4a574',bodyC:'#10b981',acc:'',glasses:false,beard:false},
|
|
{n:'Debugger',e:'🐛',dept:'dev',stn:3,d:'Root cause analysis',p:'Fixes, patches',
|
|
head:'round',hair:'messy',hairC:'#4a2a1a',skinC:'#f0d0b0',bodyC:'#10b981',acc:'',glasses:true,beard:true},
|
|
{n:'Reviewer',e:'👁️',dept:'dev',stn:3,d:'Code review expert',p:'PR reviews, qualité',
|
|
head:'round',hair:'short',hairC:'#3a3a3a',skinC:'#e8c8a0',bodyC:'#10b981',acc:'monocle',glasses:false,beard:false},
|
|
{n:'Designer',e:'🎨',dept:'dev',stn:2,d:'UI/UX design',p:'Mockups, interfaces',
|
|
head:'round',hair:'long',hairC:'#d946ef',skinC:'#f0d0b0',bodyC:'#10b981',acc:'beret',glasses:false,beard:false},
|
|
{n:'WEDROID',e:'🤖',dept:'dev',stn:3,d:'Auto-diagnostic v5',p:'DB fix, API repair',
|
|
head:'square',hair:'none',hairC:'#4a6a8a',skinC:'#7a8a9a',bodyC:'#10b981',acc:'antenna',glasses:false,beard:false},
|
|
{n:'Simplifier',e:'✂️',dept:'dev',stn:3,d:'Refactoring clean code',p:'-40% complexité',
|
|
head:'round',hair:'bun',hairC:'#8a5a3a',skinC:'#e8c8a0',bodyC:'#10b981',acc:'',glasses:true,beard:false},
|
|
// Infra
|
|
{n:'Watchdog',e:'🐕',dept:'infra',stn:6,d:'Monitor */3min',p:'Auto-restart + TG',
|
|
head:'round',hair:'ears',hairC:'#8a6a3a',skinC:'#e0b890',bodyC:'#f59e0b',acc:'collar',glasses:false,beard:false},
|
|
{n:'Guardian',e:'🛡️',dept:'infra',stn:4,d:'Protection système',p:'chattr +i',
|
|
head:'round',hair:'buzz',hairC:'#1a2a1a',skinC:'#d4a574',bodyC:'#f59e0b',acc:'helmet',glasses:false,beard:true},
|
|
{n:'Blade',e:'💻',dept:'infra',stn:6,d:'Desktop agent Razer',p:'PowerShell tasks',
|
|
head:'round',hair:'cap',hairC:'#1a3a5a',skinC:'#f0d0b0',bodyC:'#f59e0b',acc:'headset',glasses:false,beard:false},
|
|
{n:'GitMaster',e:'🌿',dept:'infra',stn:6,d:'Git flow & releases',p:'Tags, deploys',
|
|
head:'round',hair:'ponytail',hairC:'#3a5a2a',skinC:'#e8c8a0',bodyC:'#f59e0b',acc:'',glasses:true,beard:true},
|
|
// Security
|
|
{n:'Security',e:'🔐',dept:'sec',stn:4,d:'Audit OWASP',p:'Rapports sécurité',
|
|
head:'round',hair:'buzz',hairC:'#1a1a2a',skinC:'#d4a574',bodyC:'#ef4444',acc:'shades',glasses:false,beard:true},
|
|
{n:'Verifier',e:'✅',dept:'sec',stn:4,d:'Conformité ISO/RGPD',p:'Checks PCI-DSS',
|
|
head:'round',hair:'short',hairC:'#3a3a4a',skinC:'#e8c8a0',bodyC:'#ef4444',acc:'badge',glasses:true,beard:false},
|
|
// Sales
|
|
{n:'Ethica',e:'💊',dept:'sales',stn:0,d:'Scraping HCP DabaDoc',p:'131K+ médecins',
|
|
head:'round',hair:'curly',hairC:'#2a1a0a',skinC:'#d4a574',bodyC:'#3b82f6',acc:'stethoscope',glasses:false,beard:false},
|
|
{n:'Analyst',e:'🔍',dept:'sales',stn:0,d:'Analyse besoins',p:'Specs, études marché',
|
|
head:'round',hair:'parted',hairC:'#4a3a2a',skinC:'#f0d0b0',bodyC:'#3b82f6',acc:'magnifier',glasses:true,beard:false},
|
|
{n:'Writer',e:'✍️',dept:'sales',stn:0,d:'Rédaction proposals',p:'Cold emails, articles',
|
|
head:'round',hair:'long',hairC:'#8a5a2a',skinC:'#f0d0b0',bodyC:'#3b82f6',acc:'pen',glasses:false,beard:false},
|
|
// QA
|
|
{n:'QA',e:'🧪',dept:'qa',stn:5,d:'Tests E2E',p:'148 NonReg PASS',
|
|
head:'round',hair:'short',hairC:'#2a3a5a',skinC:'#f0d0b0',bodyC:'#06b6d4',acc:'goggles',glasses:false,beard:false},
|
|
{n:'TestEng',e:'🧰',dept:'qa',stn:5,d:'CI/CD pipelines',p:'Automatisation',
|
|
head:'round',hair:'flat',hairC:'#4a3a2a',skinC:'#e8c8a0',bodyC:'#06b6d4',acc:'wrench',glasses:false,beard:true},
|
|
{n:'Tracer',e:'🔦',dept:'qa',stn:5,d:'Log tracing',p:'Stack traces',
|
|
head:'round',hair:'short',hairC:'#3a2a1a',skinC:'#e0b890',bodyC:'#06b6d4',acc:'flashlight',glasses:false,beard:false},
|
|
{n:'Scientist',e:'🔬',dept:'qa',stn:5,d:'Benchmarks',p:'AI Bench 182',
|
|
head:'round',hair:'einstein',hairC:'#888',skinC:'#f0d0b0',bodyC:'#06b6d4',acc:'labcoat',glasses:true,beard:false},
|
|
// Pharma
|
|
{n:'Explore',e:'🧭',dept:'pharma',stn:0,d:'Exploration R&D',p:'Nouvelles sources',
|
|
head:'round',hair:'adventurer',hairC:'#5a3a1a',skinC:'#d4a574',bodyC:'#d946ef',acc:'compass',glasses:false,beard:true},
|
|
{n:'DocSpec',e:'📝',dept:'pharma',stn:7,d:'Documentation',p:'Templates, guides',
|
|
head:'round',hair:'neat',hairC:'#3a3a3a',skinC:'#e8c8a0',bodyC:'#d946ef',acc:'clipboard',glasses:true,beard:false},
|
|
{n:'MiroFish',e:'🐟',dept:'pharma',stn:2,d:'Creative AI',p:'Contenu, brainstorm',
|
|
head:'round',hair:'wavy',hairC:'#06b6d4',skinC:'#f0d0b0',bodyC:'#d946ef',acc:'fins',glasses:false,beard:false},
|
|
// Monitoring
|
|
{n:'TaskMgr',e:'📋',dept:'ops',stn:7,d:'Suivi tâches',p:'Kanban, deadlines',
|
|
head:'round',hair:'side',hairC:'#4a4a3a',skinC:'#e8c8a0',bodyC:'#eab308',acc:'',glasses:false,beard:false},
|
|
{n:'Intro',e:'🧠',dept:'ops',stn:5,d:'Méta-analyse',p:'Auto-amélioration',
|
|
head:'round',hair:'glow',hairC:'#a855f7',skinC:'#e8c8a0',bodyC:'#eab308',acc:'brain',glasses:false,beard:false},
|
|
{n:'Orch',e:'🎯',dept:'ops',stn:6,d:'Orchestration',p:'Coordination',
|
|
head:'round',hair:'military',hairC:'#2a2a2a',skinC:'#d4a574',bodyC:'#eab308',acc:'baton',glasses:false,beard:true},
|
|
];
|
|
|
|
AG.forEach(a=>{a.state='idle';a.x=0;a.y=0;a.dx=0;a.dy=0;a.cx=0;a.cy=0;
|
|
a.bob=Math.random()*6.28;a.wk=0;a.tmr=150+Math.random()*500;a.wtmr=0;
|
|
a.sc=1;a.dir=1;a.bl=0;a.blt=60+Math.random()*200;a.bub='';a.bubt=0;});
|
|
|
|
function layout(){
|
|
const chainY=H*.82;
|
|
// Pyramid departments
|
|
LVLS.forEach((lv,li)=>{
|
|
const totalW=lv.rows.reduce((s,r)=>s+r.w,0);
|
|
const gap=.02;
|
|
const startX=(1-totalW-(lv.rows.length-1)*gap)/2;
|
|
let cx=startX;
|
|
lv.rows.forEach(r=>{
|
|
const d=DEPTS.find(d=>d.id===r.id);
|
|
if(d){d.px=cx*W;d.py=(lv.y+.04)*H;d.pw=r.w*W;d.ph=H*.12;}
|
|
cx+=r.w+gap;
|
|
});
|
|
});
|
|
// Stations
|
|
const sg=(W-100)/STN.length;
|
|
STN.forEach((s,i)=>{s.x=60+i*sg+sg/2;s.y=chainY;});
|
|
// Agent positions
|
|
AG.forEach(a=>{
|
|
const d=DEPTS.find(dd=>dd.id===a.dept);
|
|
if(!d)return;
|
|
const mates=AG.filter(b=>b.dept===a.dept);
|
|
const mi=mates.indexOf(a);
|
|
const cols=Math.max(Math.ceil(mates.length/2),1);
|
|
const row=Math.floor(mi/cols),col=mi%cols;
|
|
a.dx=d.px+24+col*((d.pw-48)/Math.max(cols-1,1));
|
|
a.dy=d.py+30+row*36;
|
|
if(a.state==='idle'){a.x=a.dx;a.y=a.dy;}
|
|
const st=STN[a.stn];
|
|
if(st){a.cx=st.x+(Math.random()-.5)*24;a.cy=st.y-12;}
|
|
});
|
|
}
|
|
resize();
|
|
|
|
// ═══ DRAW 3D DEPT BOX ═══
|
|
function drawDept(d){
|
|
const dp=6;
|
|
// 3D shadow
|
|
X.fillStyle='#00000030';
|
|
X.beginPath();X.roundRect(d.px+dp,d.py+dp,d.pw,d.ph,8);X.fill();
|
|
// Side 3D
|
|
X.fillStyle=d.clr+'18';
|
|
X.beginPath();X.moveTo(d.px+d.pw,d.py);X.lineTo(d.px+d.pw+dp,d.py+dp);
|
|
X.lineTo(d.px+d.pw+dp,d.py+d.ph+dp);X.lineTo(d.px+d.pw,d.py+d.ph);X.closePath();X.fill();
|
|
X.beginPath();X.moveTo(d.px,d.py+d.ph);X.lineTo(d.px+dp,d.py+d.ph+dp);
|
|
X.lineTo(d.px+d.pw+dp,d.py+d.ph+dp);X.lineTo(d.px+d.pw,d.py+d.ph);X.closePath();X.fill();
|
|
// Face
|
|
const g=X.createLinearGradient(d.px,d.py,d.px,d.py+d.ph);
|
|
g.addColorStop(0,d.clr+'15');g.addColorStop(1,'#0a0a18');
|
|
X.fillStyle=g;X.beginPath();X.roundRect(d.px,d.py,d.pw,d.ph,8);X.fill();
|
|
X.strokeStyle=d.clr+'50';X.lineWidth=1.5;X.beginPath();X.roundRect(d.px,d.py,d.pw,d.ph,8);X.stroke();
|
|
// Accent bar
|
|
X.fillStyle=d.clr+'60';X.beginPath();X.roundRect(d.px,d.py,d.pw,3,[8,8,0,0]);X.fill();
|
|
// Label
|
|
X.font='800 11px Nunito';X.textAlign='center';X.fillStyle=d.clr;
|
|
X.fillText(d.label,d.px+d.pw/2,d.py+14);
|
|
}
|
|
|
|
// ═══ DRAW CHARACTER (HD) ═══
|
|
function drawC(a){
|
|
const isH=a===hov;
|
|
const sit=a.state==='idle';
|
|
const s=isH?1.2:1;
|
|
const b=sit?Math.sin(a.bob)*.4:Math.sin(a.bob)*2;
|
|
const lsw=sit?0:Math.sin(a.wk)*4;
|
|
X.save();X.translate(a.x,a.y+b);X.scale(s*a.dir,s);
|
|
if(isH){X.shadowColor=a.bodyC;X.shadowBlur=20;}
|
|
// Shadow
|
|
X.fillStyle='rgba(0,0,0,.3)';X.beginPath();X.ellipse(0,sit?10:14,9,3,0,0,6.28);X.fill();
|
|
const oy=sit?-3:0;
|
|
// ═ LEGS ═
|
|
X.fillStyle='#2a2a4e';
|
|
if(sit){X.fillRect(-5,oy+5,4,5);X.fillRect(1,oy+5,4,5);
|
|
X.fillStyle='#1e1e3a';X.fillRect(-6,oy+9,6,3);X.fillRect(0,oy+9,6,3);
|
|
} else {
|
|
X.save();X.translate(-3,oy+5);X.rotate(lsw*.05);X.fillRect(-2,0,4,10);X.restore();
|
|
X.save();X.translate(3,oy+5);X.rotate(-lsw*.05);X.fillRect(-2,0,4,10);X.restore();
|
|
X.fillStyle='#1e1e3a';
|
|
X.beginPath();X.roundRect(-6+lsw*.3,oy+14,7,3.5,[0,0,2,2]);X.fill();
|
|
X.beginPath();X.roundRect(-1-lsw*.3,oy+14,7,3.5,[0,0,2,2]);X.fill();
|
|
}
|
|
// ═ BODY ═
|
|
const bg=X.createLinearGradient(0,oy-9,0,oy+5);
|
|
bg.addColorStop(0,a.bodyC);bg.addColorStop(1,a.bodyC+'88');
|
|
X.fillStyle=bg;X.beginPath();X.roundRect(-8,oy-9,16,15,[4,4,2,2]);X.fill();
|
|
// Shirt detail
|
|
X.strokeStyle='rgba(255,255,255,.15)';X.lineWidth=.6;
|
|
X.beginPath();X.moveTo(0,oy-8);X.lineTo(0,oy+5);X.stroke();
|
|
// Collar
|
|
X.fillStyle='rgba(255,255,255,.2)';
|
|
X.beginPath();X.moveTo(-4,oy-9);X.lineTo(0,oy-6);X.lineTo(4,oy-9);X.closePath();X.fill();
|
|
// ═ ARMS ═
|
|
X.fillStyle=a.skinC;
|
|
const asw=sit?.08:Math.sin(a.wk+.5)*.22;
|
|
X.save();X.translate(-9,oy-5);X.rotate(sit?.35:asw);
|
|
X.beginPath();X.roundRect(-2.5,0,5,sit?7:10,2);X.fill();
|
|
// Hand
|
|
X.fillStyle=a.skinC;X.beginPath();X.arc(0,sit?7:10,2.5,0,6.28);X.fill();
|
|
X.restore();
|
|
X.save();X.translate(9,oy-5);X.rotate(sit?-.35:-asw);
|
|
X.beginPath();X.roundRect(-2.5,0,5,sit?7:10,2);X.fill();
|
|
X.fillStyle=a.skinC;X.beginPath();X.arc(0,sit?7:10,2.5,0,6.28);X.fill();
|
|
X.restore();
|
|
// ═ NECK ═
|
|
X.fillStyle=a.skinC;X.fillRect(-2.5,oy-12,5,4);
|
|
// ═ HEAD ═
|
|
const hy=oy-21;
|
|
if(a.head==='square'){
|
|
// Robot
|
|
X.fillStyle='#5a7a9a';X.beginPath();X.roundRect(-8,hy,16,14,3);X.fill();
|
|
X.fillStyle='#3a5a7a';X.fillRect(-6,hy+2,12,4);
|
|
// Antenna
|
|
X.strokeStyle='#8aaa';X.lineWidth=1.5;X.beginPath();X.moveTo(0,hy);X.lineTo(0,hy-6);X.stroke();
|
|
X.fillStyle='#ef4444';X.beginPath();X.arc(0,hy-6,2.5,0,6.28);X.fill();
|
|
// Robot eyes
|
|
X.fillStyle=a.state!=='idle'?'#22c55e':'#3b82f6';
|
|
X.beginPath();X.roundRect(-5,hy+6,4,3,1);X.fill();
|
|
X.beginPath();X.roundRect(1,hy+6,4,3,1);X.fill();
|
|
} else {
|
|
// Human head
|
|
X.fillStyle=a.skinC;X.beginPath();X.arc(0,hy+7,9,0,6.28);X.fill();
|
|
// Cheeks
|
|
X.fillStyle=a.skinC+'40';
|
|
X.beginPath();X.arc(-5,hy+10,3,0,6.28);X.fill();
|
|
X.beginPath();X.arc(5,hy+10,3,0,6.28);X.fill();
|
|
// ═ HAIR (unique per style) ═
|
|
X.fillStyle=a.hairC;
|
|
switch(a.hair){
|
|
case'slick':X.beginPath();X.arc(0,hy+5,9.5,.8,Math.PI+.5);X.fill();X.fillRect(-7,hy-1,14,5);break;
|
|
case'short':X.beginPath();X.arc(0,hy+5,9.5,.6,Math.PI-.3);X.fill();break;
|
|
case'buzz':X.beginPath();X.arc(0,hy+5,9.8,.5,Math.PI-.2);X.fill();break;
|
|
case'mohawk':X.beginPath();X.arc(0,hy+5,9.5,.6,Math.PI-.3);X.fill();
|
|
for(let i=0;i<5;i++){X.fillRect(-2+i*1,hy-4-i*1.5,4,5);}break;
|
|
case'long':X.beginPath();X.arc(0,hy+5,10,.3,Math.PI-.1);X.fill();
|
|
X.fillRect(-10,hy+5,5,10);X.fillRect(5,hy+5,5,10);break;
|
|
case'messy':X.beginPath();X.arc(0,hy+4,10,.3,Math.PI-.1);X.fill();
|
|
for(let i=0;i<6;i++){const ag=-2+i*.8;X.fillRect(-8+i*3,hy-3-Math.random()*3,4,5);}break;
|
|
case'wild':X.beginPath();X.arc(0,hy+4,11,.2,Math.PI);X.fill();
|
|
X.beginPath();X.arc(-9,hy+3,4,0,6.28);X.fill();X.beginPath();X.arc(9,hy+3,4,0,6.28);X.fill();break;
|
|
case'spiky':for(let i=0;i<7;i++){const ag=-1.8+i*.5;const r=10+Math.random()*4;
|
|
X.beginPath();X.moveTo(Math.cos(ag)*7,hy+5+Math.sin(ag)*7);X.lineTo(Math.cos(ag)*r,hy+3+Math.sin(ag)*r);
|
|
X.lineTo(Math.cos(ag+.25)*7,hy+5+Math.sin(ag+.25)*7);X.fill();}break;
|
|
case'side':X.beginPath();X.arc(0,hy+5,9.5,.5,Math.PI-.2);X.fill();X.fillRect(-9,hy+3,6,8);break;
|
|
case'ears':X.beginPath();X.arc(0,hy+5,9.5,.6,Math.PI-.3);X.fill();
|
|
X.beginPath();X.moveTo(-8,hy+2);X.lineTo(-13,hy-7);X.lineTo(-4,hy+3);X.fill();
|
|
X.beginPath();X.moveTo(8,hy+2);X.lineTo(13,hy-7);X.lineTo(4,hy+3);X.fill();break;
|
|
case'cap':X.beginPath();X.arc(0,hy+4,10,.3,Math.PI-.1);X.fill();
|
|
X.fillStyle=a.hairC;X.fillRect(-10,hy+2,20,4);X.fillRect(-12,hy+4,8,3);break;
|
|
case'ponytail':X.beginPath();X.arc(0,hy+5,9.5,.5,Math.PI-.2);X.fill();
|
|
X.fillRect(6,hy+5,3,12);X.beginPath();X.arc(7.5,hy+17,3,0,6.28);X.fill();break;
|
|
case'curly':for(let i=0;i<12;i++){const ag=-2.2+i*.4;
|
|
X.beginPath();X.arc(Math.cos(ag)*8,hy+4+Math.sin(ag)*7,3.5,0,6.28);X.fill();}break;
|
|
case'parted':X.beginPath();X.arc(0,hy+4,9.5,.4,Math.PI-.2);X.fill();
|
|
X.fillStyle='#080810';X.fillRect(-.5,hy-2,.8,6);break;
|
|
case'einstein':X.beginPath();X.arc(0,hy+3,11,.2,Math.PI);X.fill();
|
|
X.beginPath();X.arc(-10,hy+4,5,0,6.28);X.fill();X.beginPath();X.arc(10,hy+4,5,0,6.28);X.fill();
|
|
for(let i=0;i<4;i++)X.fillRect(-6+i*4,hy-5-Math.random()*4,3,6);break;
|
|
case'flat':X.beginPath();X.arc(0,hy+5,9.5,.6,Math.PI-.3);X.fill();X.fillRect(-8,hy,16,3);break;
|
|
case'adventurer':X.beginPath();X.arc(0,hy+5,9.5,.5,Math.PI-.2);X.fill();
|
|
X.fillStyle=a.hairC+'88';X.fillRect(-11,hy+2,22,4);X.fillRect(-13,hy+4,10,3);break;
|
|
case'neat':X.beginPath();X.arc(0,hy+5,9.5,.6,Math.PI-.3);X.fill();break;
|
|
case'wavy':for(let i=0;i<8;i++){const ag=-2+i*.5;
|
|
X.beginPath();X.arc(Math.cos(ag)*8,hy+4+Math.sin(ag)*7+Math.sin(i)*2,3,0,6.28);X.fill();}break;
|
|
case'glow':X.beginPath();X.arc(0,hy+4,10,.3,Math.PI-.1);X.fill();
|
|
X.fillStyle=a.hairC+'30';X.beginPath();X.arc(0,hy+3,16,0,6.28);X.fill();break;
|
|
case'military':X.beginPath();X.arc(0,hy+5,9.8,.5,Math.PI-.2);X.fill();X.fillRect(-8,hy+1,16,2);break;
|
|
case'bun':X.beginPath();X.arc(0,hy+5,9.5,.6,Math.PI-.3);X.fill();
|
|
X.beginPath();X.arc(0,hy-3,5,0,6.28);X.fill();break;
|
|
default:X.beginPath();X.arc(0,hy+5,9.5,.6,Math.PI-.3);X.fill();
|
|
}
|
|
// ═ EYES ═
|
|
if(a.bl<=0){
|
|
X.fillStyle='#fff';X.beginPath();X.ellipse(-3.5,hy+6,3,3.2,0,0,6.28);X.fill();
|
|
X.beginPath();X.ellipse(3.5,hy+6,3,3.2,0,0,6.28);X.fill();
|
|
X.fillStyle='#1a1a30';X.beginPath();X.arc(-3,hy+6.5,1.8,0,6.28);X.fill();
|
|
X.beginPath();X.arc(4,hy+6.5,1.8,0,6.28);X.fill();
|
|
X.fillStyle='#fff';X.beginPath();X.arc(-3.5,hy+5.5,.7,0,6.28);X.fill();
|
|
X.beginPath();X.arc(3.5,hy+5.5,.7,0,6.28);X.fill();
|
|
} else {
|
|
X.strokeStyle='#1a1a30';X.lineWidth=1.5;
|
|
X.beginPath();X.moveTo(-6,hy+6);X.lineTo(-1,hy+6);X.stroke();
|
|
X.beginPath();X.moveTo(1,hy+6);X.lineTo(6,hy+6);X.stroke();
|
|
}
|
|
// Glasses
|
|
if(a.glasses){
|
|
X.strokeStyle='#8090b0';X.lineWidth=1;
|
|
X.beginPath();X.arc(-3.5,hy+6,4,0,6.28);X.stroke();
|
|
X.beginPath();X.arc(3.5,hy+6,4,0,6.28);X.stroke();
|
|
X.beginPath();X.moveTo(-.5,hy+6);X.lineTo(.5,hy+6);X.stroke();
|
|
}
|
|
// Beard
|
|
if(a.beard){
|
|
X.fillStyle=a.hairC+'80';
|
|
X.beginPath();X.arc(0,hy+12,5,0,Math.PI);X.fill();
|
|
}
|
|
// Mouth
|
|
X.strokeStyle='#c08080';X.lineWidth=.8;X.beginPath();
|
|
if(a.state==='working'){X.arc(0,hy+11,2.5,.2,Math.PI-.2);}
|
|
else{X.moveTo(-2,hy+11.5);X.lineTo(2,hy+11.5);}
|
|
X.stroke();
|
|
// Nose
|
|
X.fillStyle=a.skinC+'cc';X.beginPath();X.arc(0,hy+9,1.2,0,6.28);X.fill();
|
|
}
|
|
// Accessories
|
|
if(a.acc==='crown'){X.font='10px sans-serif';X.textAlign='center';X.fillText('👑',0,hy-8);}
|
|
if(a.acc==='helmet'){X.fillStyle='#4a6a4a';X.beginPath();X.arc(0,hy+3,10.5,.3,Math.PI-.1);X.fill();}
|
|
if(a.acc==='beret'){X.fillStyle='#e94560';X.beginPath();X.arc(-3,hy,8,.5,Math.PI);X.fill();}
|
|
if(a.acc==='headset'){X.strokeStyle='#333';X.lineWidth=2;X.beginPath();X.arc(0,hy+3,11,.8,Math.PI-.5);X.stroke();
|
|
X.fillStyle='#333';X.beginPath();X.arc(-9,hy+7,3,0,6.28);X.fill();}
|
|
if(a.acc==='antlers'){X.strokeStyle=a.hairC;X.lineWidth=1.5;
|
|
X.beginPath();X.moveTo(-6,hy);X.lineTo(-10,hy-8);X.lineTo(-7,hy-5);X.lineTo(-12,hy-10);X.stroke();
|
|
X.beginPath();X.moveTo(6,hy);X.lineTo(10,hy-8);X.lineTo(7,hy-5);X.lineTo(12,hy-10);X.stroke();}
|
|
// Emoji badge
|
|
X.font='9px sans-serif';X.textAlign='center';X.fillText(a.e,13,hy+4);
|
|
// Name
|
|
X.font=`${isH?'800':'600'} ${isH?9:7}px Nunito`;
|
|
X.fillStyle=isH?'#fff':a.state!=='idle'?'#c0d0f0':'#4a5a70';
|
|
X.fillText(a.n,0,sit?20:26);
|
|
// Active dot
|
|
if(a.state!=='idle'){X.fillStyle='#22c55e';X.beginPath();X.arc(0,oy-28,3,0,6.28);X.fill();
|
|
X.fillStyle='#22c55e30';X.beginPath();X.arc(0,oy-28,7,0,6.28);X.fill();}
|
|
// Bubble
|
|
if(a.bubt>0){const ba=Math.min(a.bubt/20,1);X.globalAlpha=ba;X.fillStyle='#ffffffee';
|
|
const bw=Math.min(a.bub.length*4.2+14,110);X.beginPath();X.roundRect(-bw/2,oy-48,bw,17,7);X.fill();
|
|
X.fillStyle='#fff';X.beginPath();X.moveTo(-3,oy-31);X.lineTo(3,oy-31);X.lineTo(0,oy-27);X.closePath();X.fill();
|
|
X.font='600 7px Nunito';X.fillStyle='#1a1a2e';X.fillText(a.bub,0,oy-37);X.globalAlpha=1;}
|
|
X.restore();
|
|
}
|
|
|
|
// ═══ DRAW CHAIN ═══
|
|
function drawChain(){
|
|
const y=STN[0].y;
|
|
X.fillStyle='#0a0c18';X.beginPath();X.roundRect(25,y-22,W-50,44,8);X.fill();
|
|
X.strokeStyle='#1a2040';X.lineWidth=1;X.beginPath();X.roundRect(25,y-22,W-50,44,8);X.stroke();
|
|
const off=(fr*1.2)%24;
|
|
X.strokeStyle='#12182a';X.lineWidth=.5;
|
|
for(let x=30-off;x<W-30;x+=24){X.beginPath();X.moveTo(x,y-21);X.lineTo(x,y+21);X.stroke();}
|
|
STN.forEach((s,i)=>{
|
|
const g=X.createRadialGradient(s.x,y,2,s.x,y,28);g.addColorStop(0,s.clr+'30');g.addColorStop(1,'transparent');
|
|
X.fillStyle=g;X.beginPath();X.arc(s.x,y,28,0,6.28);X.fill();
|
|
X.fillStyle=s.clr+'50';X.beginPath();X.arc(s.x,y,7,0,6.28);X.fill();
|
|
X.strokeStyle=s.clr;X.lineWidth=1.5;X.beginPath();X.arc(s.x,y,7,0,6.28);X.stroke();
|
|
X.font='700 8px Nunito';X.textAlign='center';X.fillStyle=s.clr;X.fillText(s.label,s.x,y+28);
|
|
if(i<STN.length-1){const nx=STN[i+1];X.strokeStyle='#1a2540';X.lineWidth=1;
|
|
X.beginPath();X.moveTo(s.x+10,y);X.lineTo(nx.x-10,y);X.stroke();
|
|
const dt=((fr*1.8+i*40)%(nx.x-s.x-20));X.fillStyle='#53d8fb40';
|
|
X.beginPath();X.arc(s.x+10+dt,y,2,0,6.28);X.fill();}
|
|
});
|
|
}
|
|
|
|
// ═══ UPDATE ═══
|
|
function upd(dt){
|
|
fr++;let ac=0;
|
|
AG.forEach(a=>{
|
|
a.bob+=dt*(a.state==='idle'?1.5:3.5);a.blt-=dt*60;
|
|
if(a.blt<=0){a.bl=5;a.blt=80+Math.random()*220;}
|
|
if(a.bl>0)a.bl-=dt*60;if(a.bubt>0)a.bubt-=dt*25;
|
|
switch(a.state){
|
|
case'idle':a.tmr-=dt*60;if(a.tmr<=0){a.state='walk_to';a.wk=0;}break;
|
|
case'walk_to':a.wk+=dt*8;ac++;
|
|
const d1x=a.cx-a.x,d1y=a.cy-a.y,d1=Math.sqrt(d1x*d1x+d1y*d1y);
|
|
if(d1>3){const sp=100*dt;a.x+=d1x/d1*sp;a.y+=d1y/d1*sp;a.dir=d1x>0?1:-1;}
|
|
else{a.state='working';a.wtmr=70+Math.random()*120;a.bub=a.p.substring(0,20);a.bubt=50;tasks++;}break;
|
|
case'working':a.wk+=dt*3;ac++;a.wtmr-=dt*60;if(a.wtmr<=0)a.state='walk_back';break;
|
|
case'walk_back':a.wk+=dt*8;ac++;
|
|
const d2x=a.dx-a.x,d2y=a.dy-a.y,d2=Math.sqrt(d2x*d2x+d2y*d2y);
|
|
if(d2>3){const sp2=100*dt;a.x+=d2x/d2*sp2;a.y+=d2y/d2*sp2;a.dir=d2x>0?1:-1;}
|
|
else{a.state='idle';a.x=a.dx;a.y=a.dy;a.dir=1;a.tmr=250+Math.random()*700;}break;
|
|
}
|
|
});
|
|
document.getElementById('ac').textContent=ac;document.getElementById('tc').textContent=tasks;
|
|
}
|
|
|
|
function hit(){hov=null;AG.forEach(a=>{if(Math.abs(mx-a.x)<12&&Math.abs(my-a.y)<20)hov=a;});
|
|
const t=document.getElementById('tip');
|
|
if(hov){t.style.display='block';t.style.left=Math.min(mx+16,W-270)+'px';t.style.top=Math.max(my-170,10)+'px';
|
|
const dc=DEPTS.find(d=>d.id===hov.dept);t.style.borderColor=dc?dc.clr:'#3b82f6';t.style.background='#080810ee';
|
|
t.querySelector('.tn').textContent=hov.e+' '+hov.n;t.querySelector('.tt').textContent=hov.dept.toUpperCase();
|
|
t.querySelector('.tt').style.color=dc?dc.clr:'#fff';t.querySelector('.td').textContent=hov.d;
|
|
t.querySelector('.tp').textContent='→ '+hov.p;
|
|
const sm={idle:'💤 Au bureau',walk_to:'🚶 → Chaîne',working:'⚙️ Production',walk_back:'🔙 Retour'};
|
|
t.querySelector('.st').textContent=sm[hov.state];t.querySelector('.st').style.color=hov.state==='idle'?'#5a6a88':'#22c55e';
|
|
}else t.style.display='none';}
|
|
|
|
let lt=0;function loop(t){const dt=Math.min((t-lt)/1000,.04);lt=t;X.clearRect(0,0,W,H);
|
|
X.fillStyle='#080810';X.fillRect(0,0,W,H);
|
|
// Pyramid lines
|
|
X.strokeStyle='#0e1225';X.lineWidth=.5;
|
|
for(let i=1;i<LVLS.length;i++){const py1=LVLS[i-1].y*H+H*.16;const py2=LVLS[i].y*H+H*.04;
|
|
X.beginPath();X.moveTo(W*.2,py1);X.lineTo(W*.1,py2);X.stroke();
|
|
X.beginPath();X.moveTo(W*.8,py1);X.lineTo(W*.9,py2);X.stroke();}
|
|
DEPTS.forEach(d=>drawDept(d));drawChain();upd(dt);
|
|
// Trail lines
|
|
AG.filter(a=>a.state==='walk_to'||a.state==='walk_back').forEach(a=>{
|
|
X.strokeStyle='#22c55e10';X.lineWidth=1;X.setLineDash([2,5]);
|
|
X.beginPath();X.moveTo(a.dx,a.dy);X.lineTo(a.x,a.y);X.stroke();X.setLineDash([]);});
|
|
[...AG].sort((a,b)=>a.y-b.y).forEach(a=>drawC(a));hit();requestAnimationFrame(loop);}
|
|
|
|
C.addEventListener('mousemove',e=>{mx=e.clientX;my=e.clientY;C.style.cursor=hov?'pointer':'default'});
|
|
C.addEventListener('mouseleave',()=>{mx=my=-1});
|
|
requestAnimationFrame(loop);
|
|
</script>
|
|
<!-- CARTO_REMOVED -->
|
|
</body>
|
|
</html>
|