🎮ArcadeLab

Magic Loot 2D — ULTIMATE

by LaserLegend64
2036 lines102.8 KB
▶ Play
🎮 Magic Loot 2D — ULTIMATE MEGA EDITION

باشه، همه چیز رو حداکثر می‌کنم:

· 💎 ۱۲۵ Rarity
· 🪄 ۱۲۵ Wand
· 🛡️ ۱۲۵ Armor
· 🌍 ۱۰ World
· 👾 ۱۲ نوع Monster متفاوت
· 🎨 Customize کامل Face (رنگ پوست، چشم، دهان، کلاه، اکسسوری)
· ⚖️ Upgrade جدید (Luck, Crit, Regen, XP, Cooldown, HP, Speed, TapPower, Damage, Rebirth Bonus)

کد خیلی طولانیه، ولی کامل و آماده‌ست. فقط کافیه Copy کنی.

---

کد کامل (فایل index.html)

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<meta name="theme-color" content="#05070f">
<title>Magic Loot 2D — ULTIMATE</title>
<style>
  *{margin:0;padding:0;box-sizing:border-box;}
  html,body{width:100%;height:100%;overflow:hidden;background:#05070f;font-family:'Trebuchet MS',system-ui,sans-serif;touch-action:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;color:#fff;}
  canvas{display:block;position:fixed;inset:0;}
  #topbar{position:fixed;top:0;left:0;right:0;height:46px;display:flex;align-items:center;justify-content:space-around;background:linear-gradient(180deg,rgba(10,6,20,0.96),rgba(10,6,20,0.7));backdrop-filter:blur(12px);border-bottom:1px solid rgba(168,85,247,0.35);padding:0 4px;z-index:10;font-size:11px;pointer-events:none;box-shadow:0 4px 20px rgba(0,0,0,0.6);}
  #topbar .stat{display:flex;flex-direction:column;align-items:center;line-height:1.1;min-width:0;flex:1;}
  #topbar .lbl{opacity:0.55;font-size:8px;text-transform:uppercase;letter-spacing:1.5px;font-weight:700;}
  #topbar .val{font-weight:900;font-size:12px;font-family:monospace;text-shadow:0 0 8px currentColor;}
  #bottom{position:fixed;bottom:0;left:0;right:0;height:62px;display:flex;gap:5px;padding:8px 10px;background:linear-gradient(to top,rgba(5,7,15,0.98),rgba(5,7,15,0.75),rgba(5,7,15,0));z-index:10;pointer-events:none;}
  #bottom .btn{flex:1;border:2px solid rgba(255,255,255,0.15);border-radius:12px;background:linear-gradient(145deg,rgba(120,90,220,0.9),rgba(70,40,140,0.9));color:#fff;font-weight:900;font-size:9px;cursor:pointer;pointer-events:auto;box-shadow:0 4px 0 rgba(30,10,60,0.9);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;padding:3px;letter-spacing:0.3px;}
  #bottom .btn .bi{font-size:15px;line-height:1;}
  #bottom .btn:active{transform:translateY(3px);box-shadow:0 1px 0 rgba(30,10,60,0.9);}
  #bottom #btnStage{background:linear-gradient(145deg,rgba(168,85,247,0.95),rgba(126,34,206,0.95));box-shadow:0 4px 0 rgba(60,20,120,0.9);}
  #bottom #btnSell{background:linear-gradient(145deg,rgba(74,222,128,0.95),rgba(22,163,74,0.95));box-shadow:0 4px 0 rgba(15,80,40,0.9);}
  #bottom #btnInv{background:linear-gradient(145deg,rgba(59,130,246,0.95),rgba(29,78,216,0.95));box-shadow:0 4px 0 rgba(15,40,100,0.9);}
  #bottom #btnTop{background:linear-gradient(145deg,rgba(245,158,11,0.95),rgba(180,83,9,0.95));box-shadow:0 4px 0 rgba(100,50,5,0.9);}
  #bottom #btnCustom{background:linear-gradient(145deg,rgba(236,72,153,0.95),rgba(157,23,77,0.95));box-shadow:0 4px 0 rgba(80,10,40,0.9);}
  #hpWrap{position:fixed;bottom:70px;left:50%;transform:translateX(-50%);width:72%;max-width:360px;pointer-events:none;z-index:11;}
  #hpLabel{font-size:10px;text-align:center;margin-bottom:3px;font-weight:900;letter-spacing:2px;color:#fda4af;text-shadow:0 0 6px #ef4444;}
  #hpBar{height:12px;background:rgba(0,0,0,0.7);border-radius:7px;overflow:hidden;border:2px solid rgba(239,68,68,0.5);}
  #hpFill{height:100%;background:linear-gradient(90deg,#dc2626,#ef4444,#f59e0b);border-radius:5px;transition:width 0.2s;width:100%;}
  #stageBanner{position:fixed;top:52px;left:50%;transform:translateX(-50%);background:linear-gradient(145deg,rgba(30,15,60,0.96),rgba(15,8,30,0.96));border:2px solid #a855f7;padding:5px 14px;border-radius:9px;font-size:11px;font-weight:900;letter-spacing:2px;z-index:12;box-shadow:0 0 18px rgba(168,85,247,0.5);pointer-events:none;text-align:center;min-width:170px;}
  #stageBanner .sub{font-size:9px;opacity:0.7;letter-spacing:1px;font-weight:600;margin-top:1px;}
  #powerDisplay{position:fixed;top:52px;left:6px;background:linear-gradient(145deg,rgba(30,15,60,0.96),rgba(15,8,30,0.96));border:2px solid #fcd34d;padding:5px 9px;border-radius:9px;font-size:9px;font-weight:900;letter-spacing:1px;z-index:12;box-shadow:0 0 14px rgba(252,211,77,0.5);pointer-events:none;}
  #powerDisplay .pv{color:#fcd34d;font-size:12px;font-family:monospace;text-shadow:0 0 8px #fcd34d;}
  #tapBtn{position:fixed;right:14px;bottom:135px;width:88px;height:88px;border-radius:50%;background:radial-gradient(circle at 35% 30%, #fcd34d, #f59e0b 50%, #b45309);border:4px solid #fff8dc;box-shadow:0 0 24px rgba(252,211,77,0.85), inset 0 -6px 16px rgba(0,0,0,0.4), inset 0 6px 16px rgba(255,255,255,0.3);z-index:13;cursor:pointer;touch-action:none;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#3d1e00;font-weight:900;transition:transform 0.08s;user-select:none;-webkit-user-select:none;}
  #tapBtn:active{transform:scale(0.9);}
  #tapBtn .tb1{font-size:11px;letter-spacing:1px;line-height:1;}
  #tapBtn .tb2{font-size:18px;margin-top:3px;line-height:1;}
  #tapBtn .tb3{font-size:8px;margin-top:2px;opacity:0.8;line-height:1;}
  #tapBtn.onTrain{background:radial-gradient(circle at 35% 30%, #a7f3d0, #22c55e 50%, #15803d);box-shadow:0 0 32px rgba(34,197,94,1), inset 0 -6px 16px rgba(0,0,0,0.4), inset 0 6px 16px rgba(255,255,255,0.3);color:#0a2818;animation:pulseTrain 1.2s infinite;}
  @keyframes pulseTrain{0%,100%{box-shadow:0 0 24px rgba(34,197,94,0.9), inset 0 -6px 16px rgba(0,0,0,0.4), inset 0 6px 16px rgba(255,255,255,0.3);}50%{box-shadow:0 0 48px rgba(34,197,94,1), inset 0 -6px 16px rgba(0,0,0,0.4), inset 0 6px 16px rgba(255,255,255,0.3);}}
  #tapBtn .tmult{position:absolute;top:-6px;right:-6px;background:#ef4444;border:2px solid #fff;border-radius:50%;min-width:28px;height:28px;padding:0 4px;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:900;color:#fff;box-shadow:0 0 12px rgba(239,68,68,0.9);}
  #tapBtn.onTrain .tmult{background:#f59e0b;}
  #powerFloat{position:fixed;right:50px;bottom:180px;font-size:16px;font-weight:900;color:#fcd34d;pointer-events:none;z-index:14;text-shadow:0 0 10px #fcd34d,0 0 20px #f59e0b;opacity:0;}
  #powerFloat.show{animation:floatUp 0.9s ease-out;}
  @keyframes floatUp{0%{opacity:0;transform:translateY(0) scale(0.5);}30%{opacity:1;transform:translateY(-20px) scale(1.2);}100%{opacity:0;transform:translateY(-70px) scale(1);}}
  #panel{position:fixed;inset:0;z-index:20;background:linear-gradient(180deg,rgba(5,7,15,0.98),rgba(20,10,40,0.98));backdrop-filter:blur(14px);display:flex;flex-direction:column;}
  #panel.hidden{display:none;}
  #panelTitle{position:absolute;top:14px;left:50%;transform:translateX(-50%);font-size:14px;font-weight:900;letter-spacing:3px;background:linear-gradient(135deg,#a855f7,#ec4899,#f59e0b);-webkit-background-clip:text;background-clip:text;color:transparent;text-transform:uppercase;}
  #panelHeader{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:2px solid rgba(168,85,247,0.3);padding-top:56px;background:linear-gradient(180deg,rgba(30,10,60,0.9),transparent);}
  #panelTabs{display:flex;gap:5px;overflow-x:auto;flex:1;}
  #panelTabs::-webkit-scrollbar{display:none;}
  #panelTabs .tab{border:none;background:rgba(120,90,220,0.15);color:#fff;padding:8px 12px;border-radius:8px;font-size:10px;font-weight:800;cursor:pointer;white-space:nowrap;letter-spacing:0.5px;border:1px solid rgba(120,90,220,0.2);}
  #panelTabs .tab.active{background:linear-gradient(135deg,rgba(168,85,247,0.9),rgba(236,72,153,0.9));border-color:rgba(255,255,255,0.3);}
  #closeBtn{background:linear-gradient(145deg,rgba(239,68,68,0.9),rgba(180,20,40,0.9));border:2px solid rgba(255,255,255,0.15);color:#fff;width:36px;height:36px;border-radius:10px;font-size:16px;font-weight:900;cursor:pointer;margin-left:8px;}
  #panelBody{flex:1;overflow-y:auto;padding:14px;padding-bottom:100px;}
  #panelBody::-webkit-scrollbar{width:6px;}
  #panelBody::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#a855f7,#ec4899);border-radius:3px;}
  .card{background:linear-gradient(145deg,rgba(30,15,60,0.9),rgba(15,8,30,0.9));border:1px solid rgba(168,85,247,0.25);border-radius:14px;padding:12px;margin-bottom:10px;display:flex;align-items:center;gap:12px;box-shadow:0 4px 14px rgba(0,0,0,0.5);}
  .card .icon{font-size:26px;flex-shrink:0;}
  .card .info{flex:1;min-width:0;}
  .card .title{font-weight:900;font-size:13px;margin-bottom:2px;}
  .card .sub{font-size:10.5px;opacity:0.7;margin-bottom:4px;line-height:1.4;}
  .card .bar{height:6px;background:rgba(0,0,0,0.5);border-radius:3px;overflow:hidden;}
  .card .bar>div{height:100%;background:linear-gradient(90deg,#a855f7,#ec4899,#f59e0b);border-radius:3px;transition:width 0.3s;}
  .card .right{display:flex;flex-direction:column;align-items:flex-end;gap:6px;}
  .card .price{font-size:11px;color:#fcd34d;font-weight:900;font-family:monospace;}
  .card .buy{border:2px solid rgba(255,255,255,0.2);border-radius:10px;padding:8px 12px;background:linear-gradient(145deg,#a855f7,#7c3aed);color:#fff;font-weight:900;font-size:11px;cursor:pointer;white-space:nowrap;min-width:70px;letter-spacing:0.5px;box-shadow:0 3px 0 rgba(60,20,120,0.9);}
  .card .buy:active:not(:disabled){transform:translateY(2px);box-shadow:0 1px 0 rgba(60,20,120,0.9);}
  .card .buy:disabled{background:rgba(60,60,60,0.5);box-shadow:none;cursor:not-allowed;}
  .card .buy.owned{background:linear-gradient(145deg,#22c55e,#15803d);box-shadow:0 3px 0 rgba(10,60,25,0.9);}
  .card .buy.active{background:linear-gradient(145deg,#22c55e,#15803d);}
  .card .buy.select{background:linear-gradient(145deg,#3b82f6,#1d4ed8);box-shadow:0 3px 0 rgba(15,40,100,0.9);}
  .sectionTitle{font-size:10px;text-transform:uppercase;letter-spacing:2.5px;opacity:0.55;margin:16px 0 8px;font-weight:900;padding-left:8px;border-left:3px solid #a855f7;}
  .rarityRow{display:flex;align-items:center;gap:8px;padding:9px 12px;background:linear-gradient(90deg,rgba(30,15,60,0.7),rgba(15,8,30,0.5));border-radius:10px;margin-bottom:6px;border-left:4px solid var(--rc,#888);}
  .rarityRow .rn{flex:1;font-weight:900;font-size:11px;color:var(--rc,#fff);}
  .rarityRow .rp{font-size:9px;opacity:0.75;min-width:56px;text-align:right;font-family:monospace;}
  .rarityRow .rc{font-weight:900;font-size:11px;min-width:40px;text-align:right;font-family:monospace;}
  .rarityRow .sellOne{background:linear-gradient(145deg,rgba(34,197,94,0.4),rgba(21,128,61,0.4));border:1px solid rgba(74,222,128,0.5);color:#86efac;padding:5px 9px;border-radius:8px;font-size:10px;font-weight:900;cursor:pointer;}
  .rarityRow .sellOne:disabled{opacity:0.25;cursor:not-allowed;}
  .bigBtn{width:100%;padding:15px;border:2px solid rgba(255,255,255,0.2);border-radius:14px;background:linear-gradient(135deg,#ec4899,#f59e0b);color:#fff;font-weight:900;font-size:13px;cursor:pointer;box-shadow:0 5px 0 rgba(100,30,80,0.9);margin-bottom:10px;letter-spacing:1.5px;}
  .bigBtn:active:not(:disabled){transform:translateY(3px);box-shadow:0 2px 0 rgba(100,30,80,0.9);}
  .bigBtn:disabled{background:linear-gradient(135deg,rgba(80,80,80,0.5),rgba(50,50,50,0.5));box-shadow:none;cursor:not-allowed;}
  .bigBtn.green{background:linear-gradient(135deg,#16a34a,#22c55e);box-shadow:0 5px 0 rgba(10,60,25,0.9);}
  .hint{font-size:11px;opacity:0.55;text-align:center;padding:12px;line-height:1.6;font-style:italic;}
  .stageGrid{display:grid;grid-template-columns:repeat(5,1fr);gap:7px;margin:8px 0;}
  .stageCell{aspect-ratio:1;border-radius:10px;background:linear-gradient(145deg,rgba(30,15,60,0.8),rgba(15,8,30,0.8));border:2px solid rgba(168,85,247,0.3);display:flex;flex-direction:column;align-items:center;justify-content:center;font-weight:900;font-size:15px;cursor:pointer;position:relative;}
  .stageCell:active{transform:scale(0.95);}
  .stageCell.locked{opacity:0.25;cursor:not-allowed;}
  .stageCell.current{border-color:#fcd34d;box-shadow:0 0 16px rgba(252,211,77,0.7);}
  .stageCell.boss{border-color:#ef4444;background:linear-gradient(145deg,rgba(80,10,20,0.9),rgba(40,5,10,0.9));}
  .stageCell.completed{background:linear-gradient(145deg,rgba(20,80,40,0.7),rgba(10,40,20,0.7));border-color:#22c55e;}
  .stageCell .bossIco{position:absolute;top:1px;right:4px;font-size:10px;}
  .stageCell .checkIco{position:absolute;bottom:1px;right:4px;font-size:9px;color:#4ade80;}
  .worldRow{display:flex;align-items:center;gap:12px;padding:14px;background:linear-gradient(145deg,rgba(30,15,60,0.9),rgba(15,8,30,0.9));border-radius:14px;margin-bottom:10px;border:2px solid var(--wc,#666);}
  .worldRow .wIco{font-size:36px;filter:drop-shadow(0 0 10px var(--wc));}
  .worldRow .wI{flex:1;}
  .worldRow .wT{font-weight:900;font-size:14px;margin-bottom:4px;}
  .worldRow .wS{font-size:10.5px;opacity:0.75;line-height:1.6;}
  .worldRow .wB{border:2px solid rgba(255,255,255,0.2);border-radius:10px;padding:10px 14px;background:linear-gradient(145deg,#a855f7,#7c3aed);color:#fff;font-weight:900;font-size:11px;cursor:pointer;box-shadow:0 3px 0 rgba(60,20,120,0.9);}
  .worldRow .wB:disabled{background:rgba(60,60,60,0.5);box-shadow:none;cursor:not-allowed;}
  .leaderRow{display:flex;align-items:center;gap:10px;padding:10px 12px;background:linear-gradient(90deg,rgba(30,15,60,0.7),rgba(15,8,30,0.5));border-radius:10px;margin-bottom:5px;font-size:12px;}
  .leaderRow.top1{background:linear-gradient(90deg,rgba(252,211,77,0.25),rgba(120,70,10,0.15));border-left:4px solid #fcd34d;}
  .leaderRow.top2{background:linear-gradient(90deg,rgba(203,213,225,0.2),rgba(100,110,120,0.1));border-left:4px solid #cbd5e1;}
  .leaderRow.top3{background:linear-gradient(90deg,rgba(180,83,9,0.25),rgba(80,40,5,0.15));border-left:4px solid #b45309;}
  .leaderRow.you{background:linear-gradient(90deg,rgba(168,85,247,0.4),rgba(236,72,153,0.3));border:2px solid #a855f7;}
  .leaderRow .lrank{font-weight:900;min-width:32px;font-size:13px;font-family:monospace;}
  #toast{position:fixed;top:100px;left:50%;transform:translateX(-50%) translateY(-10px);background:linear-gradient(145deg,rgba(30,15,60,0.98),rgba(15,8,30,0.98));border:2px solid rgba(168,85,247,0.5);padding:10px 18px;border-radius:12px;font-size:12px;font-weight:900;z-index:30;opacity:0;transition:all 0.3s;pointer-events:none;max-width:85%;text-align:center;box-shadow:0 8px 24px rgba(0,0,0,0.7);}
  #toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
  #bossWarn{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);font-size:32px;font-weight:900;letter-spacing:6px;color:#ef4444;text-shadow:0 0 20px #ef4444,0 0 40px #ef4444;z-index:15;pointer-events:none;opacity:0;text-transform:uppercase;}
  #bossWarn.show{animation:bossWarn 2.5s ease-out forwards;}
  @keyframes bossWarn{0%{opacity:0;transform:translate(-50%,-50%) scale(0.5);}20%{opacity:1;transform:translate(-50%,-50%) scale(1.1);}40%{transform:translate(-50%,-50%) scale(1);}80%{opacity:1;}100%{opacity:0;transform:translate(-50%,-50%) scale(1.3);}}
  #loading{position:fixed;inset:0;background:#05070f;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;transition:opacity 0.6s;}
  #loading.hide{opacity:0;pointer-events:none;}
  #loading h1{font-size:26px;font-weight:900;letter-spacing:2px;background:linear-gradient(135deg,#a855f7,#ec4899,#f59e0b);-webkit-background-clip:text;background-clip:text;color:transparent;}
  #loading .sub{font-size:12px;opacity:0.5;letter-spacing:3px;}
  #loadingBar{width:60%;max-width:300px;height:8px;background:rgba(255,255,255,0.1);border-radius:4px;overflow:hidden;}
  #loadingFill{height:100%;background:linear-gradient(90deg,#a855f7,#ec4899);width:0;transition:width 0.3s;}
  #nameModal{position:fixed;inset:0;z-index:80;background:rgba(5,7,15,0.95);display:flex;align-items:center;justify-content:center;padding:20px;backdrop-filter:blur(10px);}
  #nameModal.hidden{display:none;}
  #nameModal .nm{background:linear-gradient(145deg,rgba(30,15,60,0.98),rgba(15,8,30,0.98));border:2px solid #a855f7;border-radius:16px;padding:24px;max-width:340px;width:100%;box-shadow:0 0 40px rgba(168,85,247,0.5);}
  #nameModal h2{font-size:18px;font-weight:900;letter-spacing:2px;text-align:center;margin-bottom:6px;background:linear-gradient(135deg,#a855f7,#ec4899);-webkit-background-clip:text;background-clip:text;color:transparent;}
  #nameModal p{font-size:11px;opacity:0.65;text-align:center;margin-bottom:16px;line-height:1.5;}
  #nameModal input{width:100%;padding:14px;border-radius:10px;border:2px solid rgba(168,85,247,0.4);background:rgba(0,0,0,0.4);color:#fff;font-size:14px;font-weight:900;text-align:center;letter-spacing:1px;outline:none;font-family:inherit;}
  #nameModal input:focus{border-color:#a855f7;box-shadow:0 0 20px rgba(168,85,247,0.5);}
  #nameModal button{width:100%;padding:14px;margin-top:14px;border:2px solid rgba(255,255,255,0.2);border-radius:10px;background:linear-gradient(135deg,#a855f7,#ec4899);color:#fff;font-weight:900;font-size:14px;cursor:pointer;letter-spacing:2px;box-shadow:0 4px 0 rgba(80,20,100,0.9);}
  #nameModal button:active{transform:translateY(2px);box-shadow:0 2px 0 rgba(80,20,100,0.9);}
  /* Customization grid */
  .customGrid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin:8px 0;}
  .customCell{padding:10px 6px;border-radius:10px;background:linear-gradient(145deg,rgba(30,15,60,0.7),rgba(15,8,30,0.7));border:2px solid rgba(168,85,247,0.3);text-align:center;cursor:pointer;font-size:22px;line-height:1;transition:transform 0.1s;}
  .customCell:active{transform:scale(0.92);}
  .customCell.selected{border-color:#fcd34d;box-shadow:0 0 16px rgba(252,211,77,0.7);}
  .customCell .cname{font-size:8px;margin-top:3px;opacity:0.7;font-weight:700;display:block;}
  .colorSwatch{aspect-ratio:1;border-radius:50%;border:2px solid rgba(255,255,255,0.3);cursor:pointer;transition:transform 0.1s;}
  .colorSwatch:active{transform:scale(0.9);}
  .colorSwatch.selected{border-color:#fcd34d;border-width:3px;box-shadow:0 0 16px rgba(252,211,77,0.9);}
  @media (min-width:768px){#topbar{height:56px;}#topbar .val{font-size:14px;}#bottom{height:76px;}#bottom .btn{font-size:12px;}.card .title{font-size:15px;}#tapBtn{width:110px;height:110px;right:20px;bottom:170px;}}
</style>
</head>
<body>

<div id="loading">
  <h1>MAGIC LOOT 2D</h1>
  <div class="sub">ULTIMATE EDITION</div>
  <div id="loadingBar"><div id="loadingFill"></div></div>
  <div class="sub" style="font-size:10px">Tap to start</div>
</div>

<div id="nameModal" class="hidden">
  <div class="nm">
    <h2>ENTER YOUR NAME</h2>
    <p>Will appear on the leaderboard</p>
    <input type="text" id="playerNameInput" maxlength="12" placeholder="YourName" autocomplete="off">
    <button id="saveNameBtn">START PLAYING</button>
  </div>
</div>

<canvas id="c"></canvas>

<div id="topbar">
  <div class="stat"><span class="lbl">World</span><span class="val" id="tbWorld" style="color:#22d3ee">1</span></div>
  <div class="stat"><span class="lbl">Stage</span><span class="val" id="tbStage" style="color:#a855f7">1</span></div>
  <div class="stat"><span class="lbl">Rebirth</span><span class="val" id="tbRebirth" style="color:#ec4899">0</span></div>
  <div class="stat"><span class="lbl">Coins</span><span class="val" id="tbCoins" style="color:#fcd34d">0</span></div>
  <div class="stat"><span class="lbl">Kills</span><span class="val" id="tbKills" style="color:#ef4444">0/0</span></div>
</div>

<div id="stageBanner">
  <div id="sbTitle">🏠 SAFE ZONE</div>
  <div class="sub" id="sbSub">Forest Realm</div>
</div>

<div id="powerDisplay">
  <div style="opacity:0.7;font-size:8px;letter-spacing:1.5px;">⚔️ YOUR POWER</div>
  <div class="pv" id="pPower">0</div>
</div>

<div id="tapBtn">
  <div class="tmult" id="tmult">x1</div>
  <div class="tb1">TRAIN</div>
  <div class="tb2">👆</div>
  <div class="tb3" id="tapLabel">TAP</div>
</div>

<div id="powerFloat">+0</div>

<div id="hpWrap">
  <div id="hpLabel">❤️ HP</div>
  <div id="hpBar"><div id="hpFill"></div></div>
</div>

<div id="bottom">
  <button class="btn" id="btnShop"><span class="bi">🛒</span>SHOP</button>
  <button class="btn" id="btnCustom"><span class="bi">🎨</span>FACE</button>
  <button class="btn" id="btnStage"><span class="bi">📍</span>MAP</button>
  <button class="btn" id="btnInv"><span class="bi">🎒</span>BAG</button>
  <button class="btn" id="btnSell"><span class="bi">💰</span>SELL</button>
  <button class="btn" id="btnTop"><span class="bi">🏆</span>TOP</button>
</div>

<div id="panel" class="hidden">
  <div id="panelTitle">MENU</div>
  <div id="panelHeader">
    <div id="panelTabs"></div>
    <button id="closeBtn">✕</button>
  </div>
  <div id="panelBody"></div>
</div>

<div id="toast"></div>
<div id="bossWarn">⚠️ BOSS ⚠️</div>

<script>
(function(){
'use strict';

/* ============================================================
   UTILITIES
============================================================ */
const SUF=['','K','M','B','T','Qa','Qi','Sx','Sp','Oc','No','Dc','Ud','Dd','Td','Qad','Qid','Sxd','Spd','Ocd','Nod','Vg','Uvg','Dvg','Tvg','Qavg','Sxg','Spg','Ocg','Nog','Tg','Utg','Dtg','Ttg','Qatg','Qitg','Sxtg','Sptg','Octg','Notg','Vgtg'];
function fmt(n){
  if(!isFinite(n))return '∞';
  if(n<0)return '-'+fmt(-n);
  if(n<1000)return Math.floor(n).toString();
  let i=0;while(n>=1000&&i<SUF.length-1){n/=1000;i++;}
  return(n<10?n.toFixed(2):n<100?n.toFixed(1):Math.floor(n).toString())+SUF[i];
}
const rnd=(a,b)=>a+Math.random()*(b-a);
const clamp=(v,a,b)=>Math.max(a,Math.min(b,v));
function hexA(hex,a){const n=parseInt(hex.slice(1),16);return `rgba(${(n>>16)&255},${(n>>8)&255},${n&255},${a})`;}
function el(tag,cls,html){const e=document.createElement(tag);if(cls)e.className=cls;if(html!==undefined)e.innerHTML=html;return e;}
function toast(msg,color){const t=document.getElementById('toast');t.textContent=msg;t.style.color=color||'#fff';t.style.borderColor=color?hexA(color,0.6):'rgba(168,85,247,0.5)';t.classList.add('show');clearTimeout(toast._t);toast._t=setTimeout(()=>t.classList.remove('show'),2400);}
function showBossWarn(){const w=document.getElementById('bossWarn');w.classList.remove('show');void w.offsetWidth;w.classList.add('show');}

/* ============================================================
   125 RARITIES
============================================================ */
function genRarityNames(){
  const base=['Common','Uncommon','Rare','Epic','Legendary','Mythical','Divine','Cosmic','Celestial','Eternal','Void','Abyssal','Phantom','Infernal','Astral','Transcendent','Primordial','Godly','Reality','Infinity','Omnipotent','Singularity','Quantum','Nebula','Galactic','Universal','Multiversal','Omniversal','Absolute','Ultimate','Supreme','Transcendence','Ascension','Apotheosis','Divinity','Ethereal','Immortal','Timeless','Boundless','Endless','Infinite','Unbound','Limitless','Formless','Nameless','Chaos','Order','Balance','Harmony','Discord','Genesis','Apocalypse','Ragnarok','Armageddon','Nirvana','Samadhi','Zenith','Apex','Pinnacle','Vertex','Crown','Throne','Sovereign','Monarch','Emperor','Kaiser','Tsar','Sultan','Overlord','Tyrant','Dictator','Warlord','Champion','Hero','Legend','Myth','Fable','Saga','Epic','Chronicle','Legendarium'];
  const pre=['Ancient','Elder','Prime','Alpha','Beta','Gamma','Delta','Omega','Sigma','Theta','Lambda','Mu','Nu','Xi','Omicron','Pi','Rho','Tau','Upsilon','Phi','Chi','Psi'];
  const names=[];
  for(let i=0;i<125;i++){
    if(i<base.length)names.push(base[i]);
    else{
      const p=pre[i%pre.length];
      const b=base[i%base.length];
      names.push(p+' '+b);
    }
  }
  return names;
}
const RARITY_NAMES=genRarityNames();
function genColor(i){
  /* Generate 125 distinct colors via HSL */
  const hue=(i*37)%360;
  const sat=70+(i%3)*10;
  const lit=45+(i%4)*8;
  return `hsl(${hue}, ${sat}%, ${lit}%)`;
}
const RARITIES=RARITY_NAMES.map((name,i)=>{
  const tier=i+1;
  const w=Math.max(0.000001, 1500/Math.pow(2.5,i));
  const pts=Math.pow(8.5,i)*5;
  return {id:'r'+i, name, color:genColor(i), w, pts, tier};
});

/* ============================================================
   10 WORLDS
============================================================ */
const WORLD_NAMES=['Forest Realm','Desert Abyss','Frozen Void','Volcano Core','Celestial Heaven','Twilight Realm','Oceanic Depths','Chaos Rift','Time Nexus','Omniverse'];
const WORLD_ICONS=['🌲','🏜️','❄️','🌋','✨','🌆','🌊','🌀','⏳','🌌'];
const WORLD_COLORS=['#4ade80','#f59e0b','#22d3ee','#ef4444','#fde047','#a78bfa','#38bdf8','#f43f5e','#fbbf24','#c084fc'];
const WORLD_BGS=[[20,40,28],[50,40,20],[18,30,48],[45,18,15],[30,25,55],[40,25,55],[15,35,55],[55,15,25],[35,30,20],[20,15,50]];
const WORLD_BG2=[[10,25,18],[30,22,10],[8,18,30],[28,8,6],[15,12,30],[20,10,35],[8,20,35],[30,5,15],[20,15,8],[10,5,30]];
const WORLD_HUES=[120,35,195,15,55,270,205,340,45,280];

const WORLDS=WORLD_NAMES.map((name,i)=>{
  const w=i+1;
  const monsterHp=2000*Math.pow(20,i);
  const monsterMult=Math.pow(20,i);
  const bossHp=monsterHp*100;
  const bossMult=monsterMult*40;
  const itemPool={};
  RARITIES.forEach((r,ri)=>{
    const idx=Math.min(RARITY_NAMES.length-1, ri+i*5);
    itemPool[r.id]=[RARITY_NAMES[idx]+' Item', RARITY_NAMES[idx]+' Shard', name.split(' ')[0]+' '+RARITY_NAMES[idx]];
  });
  return {
    id:w, name, icon:WORLD_ICONS[i], color:WORLD_COLORS[i],
    unlockRebirth: i*5,
    bg:WORLD_BGS[i], bg2:WORLD_BG2[i],
    monsterHue:WORLD_HUES[i],
    monsterHp, monsterMult, bossHp, bossMult,
    itemPool,
  };
});

const STAGES_PER_WORLD=25;
const MONSTERS_PER_STAGE=8;

/* ============================================================
   MONSTER TYPES — 12 variants
============================================================ */
const MONSTER_TYPES=[
{name:'Slime',   icon:'🟢', r:22, color:0},
{name:'Bat',     icon:'🦇', r:20, color:0},
{name:'Goblin',  icon:'👺', r:24, color:0},
{name:'Ghost',   icon:'👻', r:22, color:20},
{name:'Wolf',    icon:'🐺', r:26, color:0},
{name:'Snake',   icon:'🐍', r:22, color:-30},
{name:'Spider',  icon:'🕷️', r:24, color:0},
{name:'Troll',   icon:'🧌', r:30, color:-60},
{name:'Elemental',icon:'🔥', r:26, color:-30},
{name:'Skeleton',icon:'💀', r:24, color:0},
{name:'Wraith',  icon:'👤', r:26, color:40},
{name:'Dragon',  icon:'🐉', r:32, color:-50},
];

/* ============================================================
   TRAINING GROUNDS
============================================================ */
const TRAIN_GROUNDS=[
{id:0,name:'Village Dojo',icon:'🏘️',rebirth:0,color:'#4ade80',powerMult:2,desc:'x2 Power'},
{id:1,name:"Knight's Arena",icon:'⚔️',rebirth:2,color:'#3b82f6',powerMult:5,desc:'x5 Power'},
{id:2,name:'Mage Tower',icon:'🗼',rebirth:5,color:'#a855f7',powerMult:15,desc:'x15 Power'},
{id:3,name:'Dragon Sanctuary',icon:'🐲',rebirth:10,color:'#f59e0b',powerMult:50,desc:'x50 Power'},
{id:4,name:'Void Rift',icon:'🌀',rebirth:18,color:'#8b5cf6',powerMult:200,desc:'x200 Power'},
{id:5,name:'Celestial Temple',icon:'🛕',rebirth:28,color:'#22d3ee',powerMult:1000,desc:'x1000 Power'},
{id:6,name:'Eternal Nexus',icon:'💫',rebirth:40,color:'#fde047',powerMult:10000,desc:'x10000 Power'},
];

/* ============================================================
   125 CHARACTERS
============================================================ */
function genCharNames(){
  const base=['Novice','Apprentice','Squire','Knight','Mage','Wizard','Archmage','Sorcerer','Warlock','Sage','Prophet','Demigod','God','Titan','Cosmic','Eternal','Void','Astral','Transcendent','Infinite','Supreme','Absolute','Omega','Genesis','Creator','Overlord','Primordial','Divine','Celestial','Voidwalker','Shadow','Light','Chaos','Order','Balance'];
  const names=[];
  for(let i=0;i<125;i++){
    names.push((base[i%base.length])+(i>=base.length?' '+Math.floor(i/base.length+1):''));
  }
  return names;
}
const CHAR_MULTS=[];
for(let i=0;i<125;i++)CHAR_MULTS.push(Math.pow(3,i));
const CHAR_NAMES=genCharNames();
const CHAR_ICONS=['🧙','🧝','🧛','🥷','🧚','🧞','🧜','🧟','🧠','👑','⚔️','🛡️','💫','🌟','🌌','🌠','🔮','💎','✨','⚡','🔥','❄️','🌊','🌪️','🌋','💠','🌈','🦸','🦹','🧑‍🚀','🧑‍🎤','🧑‍🎨','🧑‍🔬','🧑‍💼'];
const CHARACTERS=CHAR_MULTS.map((mult,i)=>({
  id:i, name:CHAR_NAMES[i],
  icon:CHAR_ICONS[i%CHAR_ICONS.length],
  mult,
  cost:i===0?0:Math.floor(Math.pow(mult,1.15)*5000),
  hue:(i*29)%360
}));

/* ============================================================
   125 WANDS
============================================================ */
function genWandNames(){
  const base=['Wooden','Apprentice','Arcane','Crystal','Flame','Thunder','Void','Cosmic','Divine','Eternal','Godslayer','Genesis','Aether','Infinity','Nebula','Supernova','Quantum','Singularity','Omnipotent','Celestial King','Reality Warp','Time Lord','Multiverse','Dimension','Nexus','Primordial','Origin','Absolute','Godhead','Creation','Destruction','Apocalypse','Ragnarok','Olympus','Asgard','Chaos','Order','Balance','Endgame','Final','Alpha','Omega','Prime','Ancient','Elder','Titan','Colossus','Behemoth','Leviathan','Kraken','Phoenix','Dragon','Wyrm','Serpent','Griffin','Chimera','Hydra','Basilisk','Manticore','Sphinx','Pegasus','Unicorn','Fairy','Demon','Angel','Archangel','Seraph','Cherub','Nephilim','Watcher','Guardian','Sentinel','Protector','Defender'];
  const suffixes=['Wand','Staff','Rod','Scepter','Focus','Catalyst'];
  const names=[];
  for(let i=0;i<125;i++){
    const b=base[i%base.length];
    const s=suffixes[i%suffixes.length];
    names.push(b+' '+s+(i>=base.length?' '+(Math.floor(i/base.length)+1):''));
  }
  return names;
}
const WAND_NAMES=genWandNames();
const WAND_ICONS=['🪄','🪄','✨','💎','🔥','⚡','🌌','🌟','👑','💫','⚜️','🌠','💠','∞','🌌','💥','🔬','🌑','🔱','👑','🌀','⏳','🌐','🔮','🌐','🌱','🌌','⚡','🌟','✨','💥','☄️','🌋','⚡','⚡','😈','⚖️','🔮','🏁','🌟','🌌','💠','⚜️','🌟','⭐','🌟','💫','✨'];
const WANDS=WAND_NAMES.map((name,i)=>{
  const dmg=Math.pow(4,i);
  const cost=i===0?0:Math.floor(Math.pow(3.6,i)*1000);
  const hue=(i*11)%360;
  const color=`hsl(${hue}, 75%, 60%)`;
  return {id:i,name,icon:WAND_ICONS[i%WAND_ICONS.length],dmg,cost,color};
});

/* ============================================================
   125 ARMORS
============================================================ */
function genArmorNames(){
  const base=['Cloth','Leather','Chain','Steel','Mystic','Dragon','Void','Celestial','Divine','Eternal','Titan','God','Cosmic','Infinity','Nebula','Supernova','Quantum','Singularity','Omnipotent','Celestial King','Reality Warp','Time Lord','Multiverse','Dimension','Nexus','Primordial','Origin','Absolute','Godhead','Creation','Destruction','Apocalypse','Ragnarok','Olympus','Asgard','Chaos','Order','Balance','Endgame','Final','Alpha','Omega','Prime','Ancient','Elder','Titan','Colossus','Behemoth','Leviathan','Kraken','Phoenix','Dragon','Wyrm','Serpent','Griffin','Chimera','Hydra','Basilisk'];
  const suffixes=['Robe','Vest','Mail','Plate','Armor','Aegis','Guard','Shell','Cloak','Shield'];
  const names=[];
  for(let i=0;i<125;i++){
    const b=base[i%base.length];
    const s=suffixes[i%suffixes.length];
    names.push(b+' '+s+(i>=base.length?' '+(Math.floor(i/base.length)+1):''));
  }
  return names;
}
const ARMOR_NAMES=genArmorNames();
const ARMOR_ICONS=['👕','🥋','🛡️','🛡️','🔮','🐉','🌑','✨','⚜️','💠','🗿','⚡','🌌','🛡️','🌌','💥','🔬','🌑','🔱','👑','🌀','⏳','🌐','🔮','🌐','🌱','🌌','⚡','🌟','✨','💥','☄️','🌋','⚡','⚡','😈','⚖️','🔮','🏁','🌟','🌌','💠','⚜️','🌟','⭐','🌟','💫','✨'];
const ARMORS=ARMOR_NAMES.map((name,i)=>{
  const hp=Math.pow(4.2,i)*100;
  const cost=i===0?0:Math.floor(Math.pow(3.7,i)*1500);
  const hue=(i*11+180)%360;
  const color=`hsl(${hue}, 70%, 55%)`;
  return {id:i,name,icon:ARMOR_ICONS[i%ARMOR_ICONS.length],hp,cost,color};
});

/* ============================================================
   STATE
============================================================ */
const SAVE_KEY='magicloot_v11';
function defaultState(){
  return {
    px:0,py:0,hp:100,
    coins:0,rebirths:0,playtime:0,
    xp:0,level:1,
    power:0,
    inventory:RARITIES.reduce((o,r)=>(o[r.id]=0,o),{}),
    upgrades:{luck:0,crit:0,regen:0,xp:0,cooldown:0,hp:0,speed:0,tapPower:0,damage:0,rebirthBonus:0},
    currentChar:0,ownedChars:[0],
    currentWand:0,ownedWands:[0],
    currentArmor:0,ownedArmors:[0],
    currentWorld:1,currentStage:1,
    unlockedWorlds:[1],
    stagesCompleted:{},
    currentTrain:-1,
    unlockedTrain:[0],
    stats:{opened:0,kills:0,bossKills:0,deaths:0,taps:0},
    playerName:'',
    inSafeZone:true,
    /* Customization */
    face:{
      skin:'#f5c6a0',
      skinPattern:'solid',
      eyes:'normal',
      eyeColor:'#222',
      mouth:'smile',
      hair:'short',
      hairColor:'#3a2010',
      hat:'none',
      hatColor:'#a855f7',
      accessory:'none',
    },
  };
}
let S=defaultState();
function save(){try{localStorage.setItem(SAVE_KEY,JSON.stringify(S));}catch(e){}}
function load(){try{const r=localStorage.getItem(SAVE_KEY);if(!r)return;const o=JSON.parse(r);const d=defaultState();S=Object.assign(d,o);S.inventory=Object.assign(d.inventory,o.inventory||{});S.upgrades=Object.assign(d.upgrades,o.upgrades||{});S.face=Object.assign(d.face,o.face||{});if(!Array.isArray(S.ownedChars))S.ownedChars=[0];if(!Array.isArray(S.ownedWands))S.ownedWands=[0];if(!Array.isArray(S.ownedArmors))S.ownedArmors=[0];if(!Array.isArray(S.unlockedWorlds))S.unlockedWorlds=[1];if(!Array.isArray(S.unlockedTrain))S.unlockedTrain=[0];if(typeof S.stagesCompleted!=='object'||!S.stagesCompleted)S.stagesCompleted={};}catch(e){}}
load();

/* ============================================================
   DERIVED — ALL upgrades affect gameplay
============================================================ */
function charMult(){return (CHARACTERS[S.currentChar]||CHARACTERS[0]).mult;}
function rebirthBonus(){return (1+S.rebirths*0.5)*(1+S.upgrades.rebirthBonus*0.10);}
function trainMult(){if(S.currentTrain<0)return 1;const t=TRAIN_GROUNDS.find(x=>x.id===S.currentTrain);return t?t.powerMult:1;}
function wandDmgValue(){return WANDS[S.currentWand].dmg*(1+S.upgrades.damage*0.15);}
function armorHpValue(){return ARMORS[S.currentArmor].hp*(1+S.upgrades.hp*0.15);}
function tapPowerGain(){
  const wand=wandDmgValue();
  const armorBonus=1+armorHpValue()/1000;
  const lvlBonus=1+S.level*0.05;
  const tapUpg=1+S.upgrades.tapPower*0.20;
  return wand*armorBonus*lvlBonus*rebirthBonus()*charMult()*trainMult()*tapUpg;
}
function playerPower(){
  const wand=wandDmgValue();
  const armorBonus=1+armorHpValue()/1000;
  const lvl=1+S.level*0.05;
  return S.power + wand*armorBonus*lvl*rebirthBonus()*charMult();
}
function playerDmg(){return playerPower();}
function maxHp(){return armorHpValue()+100+S.rebirths*1000;}
function regenRate(){return 5+S.upgrades.regen*3+maxHp()*0.005;}
function luckValue(){return (1+S.upgrades.luck*0.3)*(1+S.rebirths*0.1);}
function xpMult(){return 1+S.upgrades.xp*0.15;}
function attackCooldown(){return Math.max(0.08, 0.5-S.level*0.005-S.upgrades.cooldown*0.003);}
function critChance(){return Math.min(0.75, S.upgrades.crit*0.02);}
function critMultiplier(){return 2+S.upgrades.crit*0.05;}
function playerSpeed(){return 260+S.level*3+S.upgrades.speed*8;}
function rebirthCost(){return Math.floor(5e6*Math.pow(3.5,S.rebirths));}
function rarityChances(luckMul){
  const ws=RARITIES.map(r=>r.id==='r0'?r.w:r.w*luckMul);
  const t=ws.reduce((a,b)=>a+b,0);
  return ws.map(w=>w/t);
}
function currentChances(){return rarityChances(luckValue());}
function currentWorld(){return WORLDS.find(w=>w.id===S.currentWorld)||WORLDS[0];}
function xpForLevel(lv){return Math.floor(50*Math.pow(1.35,lv-1));}
function addXp(a){S.xp+=a*xpMult();while(S.xp>=xpForLevel(S.level)){S.xp-=xpForLevel(S.level);S.level++;burst(0,0,'#a855f7',40,320);beep(1046,0.15,'triangle',0.09);beep(1568,0.15,'triangle',0.09);toast('⭐ Level '+S.level+'!','#a855f7');}}
function stageKey(w,s){return w+'-'+s;}
function isStageCompleted(w,s){return !!S.stagesCompleted[stageKey(w,s)];}
function isStageUnlocked(w,s){if(s===1)return true;return isStageCompleted(w,s-1);}
function isWorldUnlocked(w){return S.rebirths>=w.unlockRebirth;}

/* ============================================================
   GAME STATE
============================================================ */
let G={
  monsters:[],projectiles:[],damageTexts:[],particles:[],
  enemiesKilled:0,enemiesNeeded:0,stageCleared:false,
  safeZone:true,bossAlive:false,boss:{},
  attackCd:0,time:0,
  playerX:0,playerY:0,
  nearbyTrain:-1,
};

/* ============================================================
   CANVAS
============================================================ */
const canvas=document.getElementById('c');
const ctx=canvas.getContext('2d');
let W=0,H=0,DPR=1;
function resize(){
  DPR=Math.min(window.devicePixelRatio||1,2);
  W=window.innerWidth;H=window.innerHeight;
  canvas.width=Math.round(W*DPR);canvas.height=Math.round(H*DPR);
  canvas.style.width=W+'px';canvas.style.height=H+'px';
  ctx.setTransform(DPR,0,0,DPR,0,0);
}
resize();
window.addEventListener('resize',resize);

/* ============================================================
   AUDIO
============================================================ */
let audioCtx=null;
function ensureAudio(){if(!audioCtx){try{audioCtx=new(window.AudioContext||window.webkitAudioContext)();}catch(e){}}if(audioCtx&&audioCtx.state==='suspended')audioCtx.resume();}
function beep(freq,dur,type,vol){ensureAudio();if(!audioCtx)return;try{const o=audioCtx.createOscillator();const g=audioCtx.createGain();o.type=type||'sine';o.frequency.value=freq;g.gain.value=vol||0.05;g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+dur);o.connect(g);g.connect(audioCtx.destination);o.start();o.stop(audioCtx.currentTime+dur);}catch(e){}}
function jingle(id){
  const tier=parseInt(id.replace('r',''));
  const count=Math.min(8, 3+Math.floor(tier/15));
  const base=523+tier*3;
  for(let i=0;i<count;i++){
    setTimeout(()=>beep(base*Math.pow(1.25,i),0.16,'triangle',0.06),i*50);
  }
}
function burst(x,y,color,count,speed){
  for(let i=0;i<count;i++){
    const a=Math.random()*Math.PI*2;
    const s=speed*(0.25+Math.random()*0.9);
    const ml=0.5+Math.random()*0.6;
    G.particles.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s-100,life:ml,maxLife:ml,color,size:2+Math.random()*3,shape:Math.random()<0.3?'star':'circle',rot:Math.random()*6.28,rotSpeed:rnd(-8,8)});
  }
}

/* ============================================================
   SAFE ZONE
============================================================ */
function enterSafeZone(){
  G.safeZone=true;
  G.monsters=[];G.projectiles=[];G.bossAlive=false;
  S.currentStage=1;
  G.playerX=W*0.5;G.playerY=H*0.72;
  updateStageBanner();
  document.getElementById('sbTitle').textContent='🏠 SAFE ZONE';
  document.getElementById('sbSub').textContent=currentWorld().name;
  save();
}
function getTrainPositions(){
  const cx=W*0.5;
  const baseY=H*0.42;
  return TRAIN_GROUNDS.map((t,i)=>{
    const n=TRAIN_GROUNDS.length;
    const angle=-Math.PI*0.95+(i/(n-1))*Math.PI*0.9;
    const radius=Math.min(W,H)*0.40;
    return {id:t.id, x:cx+Math.cos(angle)*radius, y:baseY+Math.sin(angle)*radius*0.6};
  });
}

/* ============================================================
   STAGE
============================================================ */
function startStage(worldId,stageNum){
  S.currentWorld=worldId;
  S.currentStage=stageNum;
  G.safeZone=false;
  G.monsters=[];G.projectiles=[];G.damageTexts=[];
  G.enemiesKilled=0;G.stageCleared=false;G.bossAlive=false;G.attackCd=0;
  const isBossStage=(stageNum===STAGES_PER_WORLD);
  G.enemiesNeeded=isBossStage?1:MONSTERS_PER_STAGE;
  const w=currentWorld();
  const stageMult=1+(stageNum-1)*0.35;
  G.playerX=W*0.15;G.playerY=H*0.5;
  if(isBossStage){
    G.bossAlive=true;
    G.boss={
      x:W*0.7,y:H*0.5,r:70,
      hp:w.bossHp*stageMult*0.5,
      maxHp:w.bossHp*stageMult*0.5,
      power:w.bossHp*stageMult*0.5,
      bob:0,hitT:0,hue:w.monsterHue-40,
      mult:w.bossMult,isBoss:true,
      alive:true,dieT:0,attackCd:1.5,
      typeIdx:11,
    };
    showBossWarn();
    beep(120,0.5,'sawtooth',0.12);
    setTimeout(()=>beep(90,0.6,'sawtooth',0.12),200);
  } else {
    for(let i=0;i<MONSTERS_PER_STAGE;i++)spawnStageMonster(i,stageMult);
  }
  updateStageBanner();
  save();
}
function spawnStageMonster(i,stageMult){
  const w=currentWorld();
  const isElite=Math.random()<0.12;
  const baseHp=w.monsterHp*stageMult;
  const hp=baseHp*(isElite?2.5:1)*rnd(0.8,1.2);
  const col=Math.floor(i/3),row=i%3;
  const x=W*0.5+col*(W*0.16);
  const y=H*0.2+row*(H*0.28);
  const typeIdx=Math.floor(Math.random()*MONSTER_TYPES.length);
  G.monsters.push({
    x,y,r:isElite?28:MONSTER_TYPES[typeIdx].r,
    hp,maxHp:hp,power:hp,
    hue:w.monsterHue+rnd(-20,20)+MONSTER_TYPES[typeIdx].color,
    elite:isElite,
    mult:w.monsterMult*(isElite?3:1),
    bob:Math.random()*6.28,hitT:0,
    alive:true,dieT:0,
    vx:0,vy:0,
    faceX:-1,faceY:0,
    isBoss:false,
    typeIdx,
  });
}
function updateStageBanner(){
  const w=currentWorld();
  document.getElementById('sbTitle').textContent='STAGE '+S.currentStage+' / '+STAGES_PER_WORLD;
  document.getElementById('sbSub').textContent=w.name;
}

/* ============================================================
   STAGE COMPLETE
============================================================ */
function completeStage(){
  if(G.stageCleared)return;
  G.stageCleared=true;
  S.stagesCompleted[stageKey(S.currentWorld,S.currentStage)]=true;
  const bonus=10*Math.pow(2.2,S.currentStage)*currentWorld().monsterMult*totalMult();
  S.coins+=bonus;
  toast('🎉 STAGE CLEARED! +🪙 '+fmt(bonus),'#4ade80');
  beep(1318,0.2,'triangle',0.1);beep(1568,0.2,'triangle',0.1);
  const drops=4+Math.floor(Math.random()*5);
  for(let i=0;i<drops;i++){
    const r=rollRarity(luckValue()*2);
    const item=rollItem(r.id);
    S.inventory[r.id]=(S.inventory[r.id]||0)+1;
    G.damageTexts.push({x:W/2+rnd(-140,140),y:H*0.5+rnd(-80,80),vy:-80,life:3,maxLife:3,text:'+ '+item,color:r.color});
  }
  const guaranteed=rollRarity(luckValue()*8);
  const gItem=rollItem(guaranteed.id);
  S.inventory[guaranteed.id]=(S.inventory[guaranteed.id]||0)+1;
  G.damageTexts.push({x:W/2,y:H*0.42,vy:-90,life:3.5,maxLife:3.5,text:'⭐ '+gItem,color:guaranteed.color});
  if(S.currentStage<STAGES_PER_WORLD){
    setTimeout(()=>{
      if(G.stageCleared){
        startStage(S.currentWorld,S.currentStage+1);
        toast('▶ Stage '+S.currentStage+' starting...','#a855f7');
      }
    },2400);
  } else {
    toast('🏆 WORLD '+S.currentWorld+' COMPLETE!','#fcd34d');
    setTimeout(()=>{enterSafeZone();},3000);
  }
  save();
}
function rollRarity(luckMul){
  const ws=RARITIES.map(r=>r.id==='r0'?r.w:r.w*luckMul);
  const t=ws.reduce((a,b)=>a+b,0);
  let roll=Math.random()*t;
  for(let i=0;i<ws.length;i++){roll-=ws[i];if(roll<=0)return RARITIES[i];}
  return RARITIES[0];
}
function rollItem(rarityId){
  const pool=currentWorld().itemPool[rarityId]||['Mystery'];
  return pool[Math.floor(Math.random()*pool.length)];
}
function totalMult(){return charMult()*rebirthBonus();}

/* ============================================================
   COMBAT
============================================================ */
function findNearestTarget(x,y){
  if(G.bossAlive&&G.boss.alive)return G.boss;
  let best=null,bd=Infinity;
  for(const m of G.monsters){
    if(!m.alive)continue;
    const d=Math.hypot(m.x-x,m.y-y);
    if(d<bd){bd=d;best=m;}
  }
  return best;
}
function autoAttack(dt){
  if(G.safeZone||G.stageCleared)return;
  G.attackCd-=dt;
  const t=findNearestTarget(G.playerX,G.playerY);
  if(!t)return;
  if(G.attackCd<=0){
    G.attackCd=attackCooldown();
    const dx=t.x-G.playerX,dy=t.y-G.playerY;
    const d=Math.hypot(dx,dy)||1;
    const isCrit=Math.random()<critChance();
    const dmg=playerDmg()*(isCrit?critMultiplier():1);
    G.projectiles.push({
      x:G.playerX,y:G.playerY-20,
      vx:(dx/d)*900,vy:(dy/d)*900,
      r:isCrit?9:6,dmg,
      color:isCrit?'#fcd34d':WANDS[S.currentWand].color,
      life:2,maxLife:2,
      crit:isCrit,
    });
    beep(700+Math.random()*150,0.04,'square',0.02);
  }
}
function applyDamageToMonster(m,dmg){
  m.hp-=dmg;
  m.hitT=0.15;
  G.damageTexts.push({x:m.x+rnd(-10,10),y:m.y-15,vy:-90,life:0.9,maxLife:0.9,text:fmt(dmg),color:'#fcd34d'});
  if(m.hp<=0&&m.alive){
    m.alive=false;m.dieT=0;
    S.stats.kills++;G.enemiesKilled++;
    const coinGain=50*m.mult*currentWorld().monsterMult*totalMult()*S.currentStage*0.3;
    S.coins+=coinGain;
    G.damageTexts.push({x:m.x,y:m.y-30,vy:-100,life:1.2,maxLife:1.2,text:'+🪙 '+fmt(coinGain),color:'#fbbf24'});
    addXp(10+S.currentStage*2);
    burst(m.x,m.y,WANDS[S.currentWand].color,m.elite?35:20,m.elite?300:200);
    beep(880,0.06,'triangle',0.04);
    setTimeout(()=>beep(1318,0.08,'triangle',0.04),40);
    const dropChance=0.55*(1+S.rebirths*0.05);
    if(Math.random()<dropChance){
      const r=rollRarity(luckValue()*(m.elite?2:1));
      const item=rollItem(r.id);
      S.inventory[r.id]=(S.inventory[r.id]||0)+1;
      G.damageTexts.push({x:m.x+rnd(-20,20),y:m.y-50,vy:-80,life:1.6,maxLife:1.6,text:item,color:r.color});
      if(r.tier>=3)jingle(r.id);
    }
    if(m.isBoss){
      S.stats.bossKills++;
      const bossCoin=5000*m.mult*currentWorld().monsterMult*totalMult();
      S.coins+=bossCoin;
      G.damageTexts.push({x:m.x,y:m.y-60,vy:-100,life:2.5,maxLife:2.5,text:'+🪙 '+fmt(bossCoin),color:'#fcd34d'});
      const n=8+Math.floor(Math.random()*8);
      for(let i=0;i<n;i++){
        const r=rollRarity(luckValue()*15);
        const item=rollItem(r.id);
        S.inventory[r.id]=(S.inventory[r.id]||0)+1;
        G.damageTexts.push({x:m.x+rnd(-100,100),y:m.y+rnd(-60,60),vy:-80,life:2.4,maxLife:2.4,text:item,color:r.color});
      }
      jingle('r124');
      burst(m.x,m.y,'#ef4444',100,500);
      toast('💀 BOSS DEFEATED!','#ef4444');
    }
    checkStageComplete();
    save();
  }
}
function checkStageComplete(){
  if(G.safeZone||G.stageCleared)return;
  if(G.bossAlive){if(!G.boss.alive)completeStage();}
  else{const alive=G.monsters.filter(m=>m.alive).length;if(alive===0)completeStage();}
}

/* ============================================================
   INPUT
============================================================ */
let joystick={active:false,id:null,bx:0,by:0,kx:0,ky:0,dx:0,dy:0,maxR:70};
let keys={};
window.addEventListener('keydown',e=>{ensureAudio();const k=e.key.toLowerCase();if(['arrowup','arrowdown','arrowleft','arrowright',' '].includes(k))e.preventDefault();keys[k]=true;});
window.addEventListener('keyup',e=>{keys[e.key.toLowerCase()]=false;});
function pointXY(e){const r=canvas.getBoundingClientRect();return {x:e.clientX-r.left,y:e.clientY-r.top};}
canvas.addEventListener('pointerdown',e=>{
  ensureAudio();
  if(!panelEl.classList.contains('hidden'))return;
  if(!nameModal.classList.contains('hidden'))return;
  const p=pointXY(e);
  if(p.x<W*0.6&&!joystick.active){
    joystick.active=true;joystick.id=e.pointerId;
    joystick.bx=p.x;joystick.by=p.y;
    joystick.kx=p.x;joystick.ky=p.y;
    joystick.dx=0;joystick.dy=0;
  }
});
canvas.addEventListener('pointermove',e=>{
  if(e.pointerId!==joystick.id)return;
  const p=pointXY(e);
  let dx=p.x-joystick.bx,dy=p.y-joystick.by;
  const d=Math.hypot(dx,dy);
  if(d>joystick.maxR){dx=dx/d*joystick.maxR;dy=dy/d*joystick.maxR;}
  joystick.kx=joystick.bx+dx;joystick.ky=joystick.by+dy;
  joystick.dx=dx/joystick.maxR;joystick.dy=dy/joystick.maxR;
});
function endJoy(e){if(e.pointerId!==joystick.id)return;joystick.active=false;joystick.id=null;joystick.dx=0;joystick.dy=0;}
canvas.addEventListener('pointerup',endJoy);
canvas.addEventListener('pointercancel',endJoy);
canvas.addEventListener('contextmenu',e=>e.preventDefault());

/* ============================================================
   TAP BUTTON
============================================================ */
const tapBtn=document.getElementById('tapBtn');
const tmult=document.getElementById('tmult');
const tapLabel=document.getElementById('tapLabel');
const powerFloat=document.getElementById('powerFloat');
let lastTapTime=0;
function doTap(){
  const now=performance.now();
  if(now-lastTapTime<80)return;
  lastTapTime=now;
  const gain=tapPowerGain();
  S.power+=gain;
  S.stats.taps++;
  powerFloat.textContent='+'+fmt(gain)+' ⚔️';
  powerFloat.classList.remove('show');void powerFloat.offsetWidth;powerFloat.classList.add('show');
  tapBtn.style.transform='scale(0.85)';setTimeout(()=>{tapBtn.style.transform='';},80);
  beep(1000+Math.random()*300,0.05,'triangle',0.06);
  ensureAudio();
  updateHUD();
  updateTopBar();
}
tapBtn.addEventListener('pointerdown',e=>{e.preventDefault();ensureAudio();if(!nameModal.classList.contains('hidden'))return;if(!panelEl.classList.contains('hidden'))return;doTap();});
tapBtn.addEventListener('contextmenu',e=>e.preventDefault());
function updateTapButton(){
  const isTraining=S.currentTrain>=0;
  if(isTraining){
    const t=TRAIN_GROUNDS.find(x=>x.id===S.currentTrain);
    tmult.textContent='x'+t.powerMult;
    tapLabel.textContent='TRAINING';
    tapBtn.classList.add('onTrain');
  } else {
    tmult.textContent='x1';
    tapLabel.textContent='TAP';
    tapBtn.classList.remove('onTrain');
  }
  if(G.nearbyTrain>=0&&G.safeZone){
    const t=TRAIN_GROUNDS.find(x=>x.id===G.nearbyTrain);
    if(t&&S.rebirths>=t.rebirth){tmult.textContent='x'+t.powerMult+'?';}
  }
}

/* ============================================================
   UPDATE
============================================================ */
let lastT=performance.now(),lastSave=0;
function update(dt){
  G.time+=dt;
  S.playtime+=dt;
  if(S.hp<maxHp())S.hp=Math.min(maxHp(),S.hp+dt*regenRate());
  let mx=0,my=0;
  if(keys['w']||keys['arrowup'])my-=1;
  if(keys['s']||keys['arrowdown'])my+=1;
  if(keys['a']||keys['arrowleft'])mx-=1;
  if(keys['d']||keys['arrowright'])mx+=1;
  if(joystick.active){mx=joystick.dx;my=joystick.dy;}
  const len=Math.hypot(mx,my);
  if(len>1){mx/=len;my/=len;}
  const speed=playerSpeed();
  G.playerX+=mx*speed*dt;G.playerY+=my*speed*dt;
  G.playerX=clamp(G.playerX,40,W-40);G.playerY=clamp(G.playerY,90,H-150);

  if(G.safeZone){
    const positions=getTrainPositions();
    let prevNear=G.nearbyTrain;
    G.nearbyTrain=-1;
    for(const p of positions){
      if(Math.hypot(p.x-G.playerX,p.y-G.playerY)<70){G.nearbyTrain=p.id;break;}
    }
    if(prevNear!==G.nearbyTrain)updateTapButton();
    if(G.nearbyTrain>=0){
      const t=TRAIN_GROUNDS.find(x=>x.id===G.nearbyTrain);
      if(t&&S.rebirths>=t.rebirth&&S.currentTrain!==t.id){
        S.currentTrain=t.id;
        if(!S.unlockedTrain.includes(t.id))S.unlockedTrain.push(t.id);
        toast('✅ '+t.name+' — '+t.desc,'#4ade80');
        beep(1046,0.15,'triangle',0.08);beep(1568,0.15,'triangle',0.08);
        save();updateTapButton();updateHUD();
      }
    } else {
      if(S.currentTrain>=0){S.currentTrain=-1;updateTapButton();updateHUD();}
    }
  } else {
    if(G.nearbyTrain>=0){G.nearbyTrain=-1;updateTapButton();}
  }

  if(!G.safeZone){
    for(const m of G.monsters){
      if(!m.alive){m.dieT+=dt;continue;}
      if(m.hitT>0)m.hitT-=dt;
      m.bob+=dt;
      const dx=G.playerX-m.x,dy=G.playerY-m.y;
      const d=Math.hypot(dx,dy)||1;
      if(d>80){m.x+=(dx/d)*50*dt;m.y+=(dy/d)*50*dt;}
      m.faceX=dx/d;m.faceY=dy/d;
      if(d<50){S.hp-=m.power*0.015*dt;if(S.hp<=0){S.hp=0;playerDie();}}
    }
    if(G.bossAlive){
      const b=G.boss;
      if(!b.alive){b.dieT+=dt;if(b.dieT>2)G.bossAlive=false;}
      else{
        if(b.hitT>0)b.hitT-=dt;
        b.bob+=dt;
        const dx=G.playerX-b.x,dy=G.playerY-b.y;
        const d=Math.hypot(dx,dy)||1;
        if(d>180){b.x+=(dx/d)*60*dt;b.y+=(dy/d)*60*dt;}
        if(d<120){S.hp-=b.power*0.008*dt;if(S.hp<=0){S.hp=0;playerDie();}}
      }
    }
    autoAttack(dt);
  }

  for(let i=G.projectiles.length-1;i>=0;i--){
    const p=G.projectiles[i];
    p.x+=p.vx*dt;p.y+=p.vy*dt;p.life-=dt;
    if(p.life<=0||p.x<-20||p.x>W+20||p.y<-20||p.y>H+20){G.projectiles.splice(i,1);continue;}
    let hit=false;
    for(const m of G.monsters){
      if(!m.alive)continue;
      if(Math.hypot(m.x-p.x,m.y-p.y)<m.r+p.r){
        applyDamageToMonster(m,p.dmg);
        burst(p.x,p.y,p.color,p.crit?14:6,p.crit?200:150);
        hit=true;break;
      }
    }
    if(!hit&&G.bossAlive&&G.boss.alive){
      const b=G.boss;
      if(Math.hypot(b.x-p.x,b.y-p.y)<b.r+p.r){
        applyDamageToMonster(b,p.dmg);burst(p.x,p.y,p.color,8,180);hit=true;
      }
    }
    if(hit)G.projectiles.splice(i,1);
  }

  for(let i=G.particles.length-1;i>=0;i--){
    const p=G.particles[i];
    p.x+=p.vx*dt;p.y+=p.vy*dt;
    p.vy+=400*dt;p.vx*=0.98;p.life-=dt;p.rot+=p.rotSpeed*dt;
    if(p.life<=0)G.particles.splice(i,1);
  }
  for(let i=G.damageTexts.length-1;i>=0;i--){
    const d=G.damageTexts[i];
    d.y+=d.vy*dt;d.vy+=60*dt;d.life-=dt;
    if(d.life<=0)G.damageTexts.splice(i,1);
  }

  updateHUD();
  lastSave+=dt;
  if(lastSave>5){lastSave=0;save();}
}
function playerDie(){
  S.stats.deaths++;
  const lost=Math.floor(S.coins*0.05);
  S.coins=Math.max(0,S.coins-lost);
  toast('💀 You died! Lost 🪙 '+fmt(lost),'#ef4444');
  S.hp=maxHp();
  if(!G.safeZone&&!G.stageCleared)startStage(S.currentWorld,S.currentStage);
}

/* ============================================================
   RENDER
============================================================ */
let stars=[];
function initStars(){stars=[];for(let i=0;i<80;i++)stars.push({x:Math.random()*W,y:Math.random()*H,s:Math.random()<0.8?1:2,a:0.3+Math.random()*0.7,tw:Math.random()*6.28});}
initStars();
window.addEventListener('resize',initStars);

function render(){
  const w=currentWorld();
  const grd=ctx.createRadialGradient(W/2,H/2,0,W/2,H/2,Math.max(W,H));
  grd.addColorStop(0,`rgb(${w.bg[0]},${w.bg[1]},${w.bg[2]})`);
  grd.addColorStop(1,`rgb(${w.bg2[0]},${w.bg2[1]},${w.bg2[2]})`);
  ctx.fillStyle=grd;ctx.fillRect(0,0,W,H);
  for(const s of stars){const tw=0.5+0.5*Math.sin(G.time*2+s.tw);ctx.globalAlpha=s.a*tw;ctx.fillStyle='#fff';ctx.fillRect(s.x,s.y,s.s,s.s);}
  ctx.globalAlpha=1;
  ctx.strokeStyle=hexA(w.color,0.15);ctx.lineWidth=2;
  ctx.beginPath();ctx.moveTo(0,H*0.88);ctx.lineTo(W,H*0.88);ctx.stroke();

  if(G.safeZone){
    const sg=ctx.createRadialGradient(W/2,H/2,0,W/2,H/2,W*0.7);
    sg.addColorStop(0,'rgba(74,222,128,0.10)');sg.addColorStop(1,'rgba(74,222,128,0)');
    ctx.fillStyle=sg;ctx.fillRect(0,0,W,H);
    ctx.font='900 13px system-ui';ctx.textAlign='center';
    ctx.fillStyle='rgba(74,222,128,0.9)';ctx.shadowColor='#4ade80';ctx.shadowBlur=12;
    ctx.fillText('WALK TO A TRAINING GROUND',W/2,H*0.135);ctx.shadowBlur=0;
    const positions=getTrainPositions();
    for(const p of positions){
      const t=TRAIN_GROUNDS.find(x=>x.id===p.id);if(!t)continue;
      const unlocked=S.rebirths>=t.rebirth;
      const active=S.currentTrain===t.id;
      const nearby=G.nearbyTrain===t.id;
      if(unlocked){
        const grd2=ctx.createRadialGradient(p.x,p.y,0,p.x,p.y,nearby?90:70);
        grd2.addColorStop(0,hexA(t.color,active?0.6:0.3));grd2.addColorStop(1,hexA(t.color,0));
        ctx.fillStyle=grd2;ctx.beginPath();ctx.arc(p.x,p.y,nearby?90:70,0,Math.PI*2);ctx.fill();
      }
      ctx.fillStyle=unlocked?hexA(t.color,0.25):'rgba(80,80,80,0.25)';
      ctx.strokeStyle=unlocked?t.color:'#666';ctx.lineWidth=3;
      ctx.beginPath();ctx.arc(p.x,p.y,nearby?42:36,0,Math.PI*2);ctx.fill();ctx.stroke();
      if(active){ctx.strokeStyle='#fcd34d';ctx.lineWidth=3;ctx.setLineDash([6,6]);ctx.beginPath();ctx.arc(p.x,p.y,nearby?50:44,0,Math.PI*2);ctx.stroke();ctx.setLineDash([]);}
      ctx.font='900 28px system-ui';ctx.textAlign='center';ctx.textBaseline='middle';
      ctx.fillText(unlocked?t.icon:'🔒',p.x,p.y-4);
      ctx.font='900 10px system-ui';
      ctx.fillStyle=unlocked?'#fff':'#888';ctx.shadowColor='#000';ctx.shadowBlur=4;
      ctx.fillText(t.name,p.x,p.y+46);
      ctx.fillStyle=unlocked?t.color:'#666';ctx.font='900 9px system-ui';
      ctx.fillText(unlocked?t.desc:'Need '+t.rebirth+' ♻️',p.x,p.y+58);
      ctx.shadowBlur=0;ctx.textBaseline='alphabetic';
    }
  }

  for(const m of G.monsters)drawMonster(m);
  if(G.bossAlive)drawBoss(G.boss);
  for(const p of G.projectiles){
    const a=Math.max(0,p.life/p.maxLife);ctx.globalAlpha=a;
    ctx.shadowColor=p.color;ctx.shadowBlur=p.crit?22:14;ctx.fillStyle=p.color;
    ctx.beginPath();ctx.arc(p.x,p.y,p.r,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0;
    ctx.fillStyle='#fff';ctx.beginPath();ctx.arc(p.x,p.y,p.r*0.5,0,Math.PI*2);ctx.fill();
  }
  ctx.globalAlpha=1;
  for(const p of G.particles){
    const a=Math.max(0,p.life/p.maxLife);ctx.globalAlpha=a;ctx.fillStyle=p.color;
    ctx.shadowColor=p.color;ctx.shadowBlur=6;
    if(p.shape==='star'){
      ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.rot);const s=p.size*a;ctx.beginPath();
      for(let i=0;i<5;i++){const ang=-Math.PI/2+i*Math.PI*2/5;const x1=Math.cos(ang)*s*1.6,y1=Math.sin(ang)*s*1.6;const ang2=ang+Math.PI/5;const x2=Math.cos(ang2)*s*0.7,y2=Math.sin(ang2)*s*0.7;if(i===0)ctx.moveTo(x1,y1);else ctx.lineTo(x1,y1);ctx.lineTo(x2,y2);}
      ctx.closePath();ctx.fill();ctx.restore();
    } else {ctx.beginPath();ctx.arc(p.x,p.y,p.size*a,0,Math.PI*2);ctx.fill();}
  }
  ctx.shadowBlur=0;ctx.globalAlpha=1;

  drawPlayer(G.playerX,G.playerY);

  for(const d of G.damageTexts){
    const a=Math.max(0,Math.min(1,d.life/d.maxLife*1.5));ctx.globalAlpha=a;
    ctx.font='900 14px system-ui';ctx.textAlign='center';ctx.textBaseline='middle';
    ctx.shadowColor=d.color;ctx.shadowBlur=12;ctx.strokeStyle='rgba(0,0,0,0.9)';ctx.lineWidth=4;
    ctx.strokeText(d.text,d.x,d.y);ctx.fillStyle=d.color;ctx.fillText(d.text,d.x,d.y);
  }
  ctx.globalAlpha=1;ctx.shadowBlur=0;

  if(joystick.active){
    ctx.strokeStyle='rgba(168,85,247,0.4)';ctx.lineWidth=3;
    ctx.beginPath();ctx.arc(joystick.bx,joystick.by,joystick.maxR,0,Math.PI*2);ctx.stroke();
    ctx.fillStyle='rgba(168,85,247,0.12)';ctx.beginPath();ctx.arc(joystick.bx,joystick.by,joystick.maxR,0,Math.PI*2);ctx.fill();
    ctx.fillStyle='rgba(200,150,255,0.75)';ctx.shadowColor='#a855f7';ctx.shadowBlur=15;
    ctx.beginPath();ctx.arc(joystick.kx,joystick.ky,28,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0;
    ctx.strokeStyle='rgba(255,255,255,0.7)';ctx.lineWidth=2;ctx.stroke();
  }
}

/* ============================================================
   PLAYER DRAWING — with FULL CUSTOMIZATION
============================================================ */
function drawPlayer(x,y){
  const bY=y+Math.sin(G.time*4)*2;
  const f=S.face;
  ctx.fillStyle='rgba(0,0,0,0.5)';ctx.beginPath();ctx.ellipse(x,y+18,18,7,0,0,Math.PI*2);ctx.fill();
  const ch=CHARACTERS[S.currentChar];
  const auraColor=`hsl(${ch.hue},70%,60%)`;
  const grd=ctx.createRadialGradient(x,bY,0,x,bY,40);
  grd.addColorStop(0,hexA(auraColor.replace('hsl',''),0.35));grd.addColorStop(1,'rgba(140,110,255,0)');
  ctx.fillStyle=grd;ctx.beginPath();ctx.arc(x,bY,40,0,Math.PI*2);ctx.fill();
  ctx.strokeStyle=ARMORS[S.currentArmor].color;ctx.lineWidth=3;
  ctx.shadowColor=ARMORS[S.currentArmor].color;ctx.shadowBlur=10;
  ctx.beginPath();ctx.arc(x,bY,18,0,Math.PI*2);ctx.stroke();ctx.shadowBlur=0;

  /* Body (armor) */
  ctx.fillStyle=ARMORS[S.currentArmor].color;
  ctx.beginPath();ctx.arc(x,bY,16,0,Math.PI*2);ctx.fill();
  ctx.fillStyle='rgba(255,255,255,0.15)';
  ctx.beginPath();ctx.arc(x-4,bY-4,8,0,Math.PI*2);ctx.fill();

  /* Head */
  const skinColor=f.skin;
  ctx.fillStyle=skinColor;
  ctx.beginPath();ctx.arc(x,bY-14,13,0,Math.PI*2);ctx.fill();

  /* Skin pattern */
  if(f.skinPattern==='spots'){
    ctx.fillStyle='rgba(0,0,0,0.15)';
    for(let i=0;i<6;i++){
      const a=i*1.05;
      ctx.beginPath();ctx.arc(x+Math.cos(a)*7, bY-14+Math.sin(a)*7, 2.5, 0, Math.PI*2);ctx.fill();
    }
  } else if(f.skinPattern==='stripes'){
    ctx.strokeStyle='rgba(0,0,0,0.2)';ctx.lineWidth=2;
    for(let i=-1;i<=1;i++){
      ctx.beginPath();ctx.moveTo(x-10,bY-14+i*4);ctx.lineTo(x+10,bY-14+i*4);ctx.stroke();
    }
  } else if(f.skinPattern==='freckles'){
    ctx.fillStyle='rgba(0,0,0,0.25)';
    for(let i=0;i<10;i++){
      const px=x+rnd(-9,9), py=bY-14+rnd(-9,9);
      if(Math.hypot(px-x,py-(bY-14))<11){
        ctx.beginPath();ctx.arc(px,py,0.8,0,Math.PI*2);ctx.fill();
      }
    }
  }

  /* Hair */
  if(f.hair!=='none'){
    ctx.fillStyle=f.hairColor;
    if(f.hair==='short'){
      ctx.beginPath();
      ctx.arc(x,bY-18,12,Math.PI,0);
      ctx.fill();
    } else if(f.hair==='long'){
      ctx.beginPath();
      ctx.arc(x,bY-16,13,Math.PI,0);
      ctx.fill();
      ctx.beginPath();ctx.ellipse(x-12,bY-8,4,10,0,0,Math.PI*2);ctx.fill();
      ctx.beginPath();ctx.ellipse(x+12,bY-8,4,10,0,0,Math.PI*2);ctx.fill();
    } else if(f.hair==='spiky'){
      for(let i=-3;i<=3;i++){
        ctx.beginPath();
        ctx.moveTo(x+i*3, bY-18);
        ctx.lineTo(x+i*3-2, bY-30);
        ctx.lineTo(x+i*3+2, bY-30);
        ctx.closePath();ctx.fill();
      }
    } else if(f.hair==='mohawk'){
      ctx.beginPath();
      ctx.moveTo(x-3,bY-20);ctx.lineTo(x,bY-34);ctx.lineTo(x+3,bY-20);
      ctx.closePath();ctx.fill();
    } else if(f.hair==='bald'){
      /* nothing */
    } else if(f.hair==='ponytail'){
      ctx.beginPath();ctx.arc(x,bY-18,12,Math.PI,0);ctx.fill();
      ctx.beginPath();ctx.ellipse(x+14,bY-6,4,12,0,0,Math.PI*2);ctx.fill();
    } else if(f.hair==='curly'){
      for(let i=0;i<8;i++){
        const a=Math.PI+ (i/7)*Math.PI;
        ctx.beginPath();
        ctx.arc(x+Math.cos(a)*11, bY-16+Math.sin(a)*11, 4, 0, Math.PI*2);
        ctx.fill();
      }
    }
  }

  /* Eyes */
  const eyeX=5, eyeY=-14;
  const ec=f.eyeColor;
  if(f.eyes==='normal'){
    ctx.fillStyle='#fff';
    ctx.beginPath();ctx.arc(x-5,bY+eyeY,2.8,0,Math.PI*2);ctx.arc(x+5,bY+eyeY,2.8,0,Math.PI*2);ctx.fill();
    ctx.fillStyle=ec;
    ctx.beginPath();ctx.arc(x-5,bY+eyeY,1.6,0,Math.PI*2);ctx.arc(x+5,bY+eyeY,1.6,0,Math.PI*2);ctx.fill();
  } else if(f.eyes==='angry'){
    ctx.fillStyle=ec;
    ctx.beginPath();ctx.arc(x-5,bY+eyeY,2.5,0,Math.PI*2);ctx.arc(x+5,bY+eyeY,2.5,0,Math.PI*2);ctx.fill();
    ctx.strokeStyle='#000';ctx.lineWidth=1.5;
    ctx.beginPath();ctx.moveTo(x-9,bY+eyeY-5);ctx.lineTo(x-2,bY+eyeY-3);ctx.stroke();
    ctx.beginPath();ctx.moveTo(x+9,bY+eyeY-5);ctx.lineTo(x+2,bY+eyeY-3);ctx.stroke();
  } else if(f.eyes==='happy'){
    ctx.strokeStyle=ec;ctx.lineWidth=2;
    ctx.beginPath();ctx.arc(x-5,bY+eyeY+1,3,Math.PI,0);ctx.stroke();
    ctx.beginPath();ctx.arc(x+5,bY+eyeY+1,3,Math.PI,0);ctx.stroke();
  } else if(f.eyes==='sad'){
    ctx.fillStyle=ec;
    ctx.beginPath();ctx.arc(x-5,bY+eyeY,2,0,Math.PI*2);ctx.arc(x+5,bY+eyeY,2,0,Math.PI*2);ctx.fill();
    ctx.strokeStyle='#000';ctx.lineWidth=1.5;
    ctx.beginPath();ctx.moveTo(x-9,bY+eyeY-2);ctx.lineTo(x-2,bY+eyeY-5);ctx.stroke();
    ctx.beginPath();ctx.moveTo(x+9,bY+eyeY-2);ctx.lineTo(x+2,bY+eyeY-5);ctx.stroke();
  } else if(f.eyes==='closed'){
    ctx.strokeStyle=ec;ctx.lineWidth=2;
    ctx.beginPath();ctx.moveTo(x-8,bY+eyeY);ctx.lineTo(x-2,bY+eyeY);ctx.stroke();
    ctx.beginPath();ctx.moveTo(x+2,bY+eyeY);ctx.lineTo(x+8,bY+eyeY);ctx.stroke();
  } else if(f.eyes==='wink'){
    ctx.fillStyle=ec;
    ctx.beginPath();ctx.arc(x-5,bY+eyeY,2.5,0,Math.PI*2);ctx.fill();
    ctx.strokeStyle=ec;ctx.lineWidth=2;
    ctx.beginPath();ctx.moveTo(x+2,bY+eyeY);ctx.lineTo(x+8,bY+eyeY);ctx.stroke();
  } else if(f.eyes==='heart'){
    ctx.fillStyle='#ec4899';
    for(let s of [-5,5]){
      const cx=x+s, cy=bY+eyeY;
      ctx.beginPath();
      ctx.moveTo(cx, cy+2);
      ctx.bezierCurveTo(cx-3, cy-2, cx-3, cy-4, cx, cy-3);
      ctx.bezierCurveTo(cx+3, cy-4, cx+3, cy-2, cx, cy+2);
      ctx.fill();
    }
  } else if(f.eyes==='star'){
    ctx.fillStyle='#fcd34d';
    for(let s of [-5,5]){
      drawStar(x+s, bY+eyeY, 4, 5, 2);
    }
  } else if(f.eyes==='cute'){
    ctx.fillStyle='#fff';
    ctx.beginPath();ctx.arc(x-5,bY+eyeY,3,0,Math.PI*2);ctx.arc(x+5,bY+eyeY,3,0,Math.PI*2);ctx.fill();
    ctx.fillStyle=ec;
    ctx.beginPath();ctx.arc(x-5,bY+eyeY+0.5,2,0,Math.PI*2);ctx.arc(x+5,bY+eyeY+0.5,2,0,Math.PI*2);ctx.fill();
    ctx.fillStyle='#fff';
    ctx.beginPath();ctx.arc(x-6,bY+eyeY-1,0.8,0,Math.PI*2);ctx.arc(x+4,bY+eyeY-1,0.8,0,Math.PI*2);ctx.fill();
  }

  /* Mouth */
  const mouthY=-6;
  if(f.mouth==='smile'){
    ctx.strokeStyle='#000';ctx.lineWidth=1.5;
    ctx.beginPath();ctx.arc(x,bY+mouthY,3,0,Math.PI);ctx.stroke();
  } else if(f.mouth==='sad'){
    ctx.strokeStyle='#000';ctx.lineWidth=1.5;
    ctx.beginPath();ctx.arc(x,bY+mouthY+2,3,Math.PI,0);ctx.stroke();
  } else if(f.mouth==='neutral'){
    ctx.strokeStyle='#000';ctx.lineWidth=1.5;
    ctx.beginPath();ctx.moveTo(x-3,bY+mouthY);ctx.lineTo(x+3,bY+mouthY);ctx.stroke();
  } else if(f.mouth==='open'){
    ctx.fillStyle='#7c1d3a';
    ctx.beginPath();ctx.ellipse(x,bY+mouthY,3,2.5,0,0,Math.PI*2);ctx.fill();
  } else if(f.mouth==='tongue'){
    ctx.strokeStyle='#000';ctx.lineWidth=1.5;
    ctx.beginPath();ctx.arc(x,bY+mouthY,3,0,Math.PI);ctx.stroke();
    ctx.fillStyle='#ec4899';
    ctx.beginPath();ctx.ellipse(x,bY+mouthY+2,1.5,2,0,0,Math.PI*2);ctx.fill();
  } else if(f.mouth==='smirk'){
    ctx.strokeStyle='#000';ctx.lineWidth=1.5;
    ctx.beginPath();ctx.moveTo(x-3,bY+mouthY);
    ctx.quadraticCurveTo(x+1,bY+mouthY+3, x+4,bY+mouthY-1);ctx.stroke();
  } else if(f.mouth==='fangs'){
    ctx.strokeStyle='#000';ctx.lineWidth=1.5;
    ctx.beginPath();ctx.arc(x,bY+mouthY,3,0,Math.PI);ctx.stroke();
    ctx.fillStyle='#fff';
    ctx.beginPath();ctx.moveTo(x-2,bY+mouthY);ctx.lineTo(x-1,bY+mouthY+3);ctx.lineTo(x,bY+mouthY);ctx.fill();
    ctx.beginPath();ctx.moveTo(x+2,bY+mouthY);ctx.lineTo(x+1,bY+mouthY+3);ctx.lineTo(x,bY+mouthY);ctx.fill();
  }

  /* Hat */
  if(f.hat!=='none'){
    ctx.fillStyle=f.hatColor;
    if(f.hat==='wizard'){
      ctx.beginPath();
      ctx.moveTo(x-14,bY-22);ctx.lineTo(x+14,bY-22);ctx.lineTo(x+2,bY-42);ctx.lineTo(x-2,bY-42);
      ctx.closePath();ctx.fill();
      ctx.fillRect(x-15,bY-24,30,4);
    } else if(f.hat==='crown'){
      ctx.beginPath();
      ctx.moveTo(x-13,bY-22);ctx.lineTo(x-13,bY-30);ctx.lineTo(x-7,bY-24);ctx.lineTo(x,bY-32);ctx.lineTo(x+7,bY-24);ctx.lineTo(x+13,bY-30);ctx.lineTo(x+13,bY-22);
      ctx.closePath();ctx.fill();
    } else if(f.hat==='cap'){
      ctx.beginPath();ctx.arc(x,bY-22,13,Math.PI,0);ctx.fill();
      ctx.fillRect(x-16,bY-24,32,4);
    } else if(f.hat==='beanie'){
      ctx.beginPath();ctx.arc(x,bY-20,13,Math.PI,0);ctx.fill();
      ctx.fillStyle='rgba(255,255,255,0.3)';
      ctx.fillRect(x-13,bY-22,26,4);
    } else if(f.hat==='halo'){
      ctx.strokeStyle=f.hatColor;ctx.lineWidth=3;
      ctx.beginPath();ctx.ellipse(x,bY-32,12,3,0,0,Math.PI*2);ctx.stroke();
    } else if(f.hat==='horns'){
      ctx.beginPath();
      ctx.moveTo(x-10,bY-22);ctx.lineTo(x-14,bY-32);ctx.lineTo(x-7,bY-25);ctx.closePath();ctx.fill();
      ctx.beginPath();
      ctx.moveTo(x+10,bY-22);ctx.lineTo(x+14,bY-32);ctx.lineTo(x+7,bY-25);ctx.closePath();ctx.fill();
    } else if(f.hat==='flower'){
      ctx.fillStyle=f.hatColor;
      for(let i=0;i<6;i++){
        const a=i*Math.PI/3;
        ctx.beginPath();ctx.ellipse(x+Math.cos(a)*7, bY-24+Math.sin(a)*7, 3, 5, a, 0, Math.PI*2);ctx.fill();
      }
      ctx.fillStyle='#fcd34d';
      ctx.beginPath();ctx.arc(x,bY-24,3,0,Math.PI*2);ctx.fill();
    }
  }

  /* Accessory */
  if(f.accessory!=='none'){
    if(f.accessory==='glasses'){
      ctx.strokeStyle='#000';ctx.lineWidth=2;
      ctx.beginPath();ctx.arc(x-5,bY-14,5,0,Math.PI*2);ctx.stroke();
      ctx.beginPath();ctx.arc(x+5,bY-14,5,0,Math.PI*2);ctx.stroke();
      ctx.beginPath();ctx.moveTo(x-1,bY-14);ctx.lineTo(x+1,bY-14);ctx.stroke();
    } else if(f.accessory==='mask'){
      ctx.fillStyle='rgba(0,0,0,0.7)';
      ctx.fillRect(x-10,bY-16,20,6);
    } else if(f.accessory==='beard'){
      ctx.fillStyle=f.hairColor;
      ctx.beginPath();
      ctx.arc(x,bY-6,10,0,Math.PI);ctx.fill();
    } else if(f.accessory==='earrings'){
      ctx.fillStyle='#fcd34d';
      ctx.beginPath();ctx.arc(x-14,bY-12,1.5,0,Math.PI*2);ctx.fill();
      ctx.beginPath();ctx.arc(x+14,bY-12,1.5,0,Math.PI*2);ctx.fill();
    } else if(f.accessory==='scar'){
      ctx.strokeStyle='#8b0000';ctx.lineWidth=1.5;
      ctx.beginPath();ctx.moveTo(x+7,bY-18);ctx.lineTo(x+9,bY-10);ctx.stroke();
    } else if(f.accessory==='star'){
      ctx.fillStyle='#fcd34d';
      drawStar(x-8, bY-16, 3, 5, 2);
    }
  }

  /* Wand */
  ctx.fillStyle=WANDS[S.currentWand].color;ctx.shadowColor=WANDS[S.currentWand].color;ctx.shadowBlur=14;
  ctx.beginPath();ctx.arc(x+16,bY-19,4,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0;

  /* Name */
  ctx.font='900 11px system-ui';ctx.textAlign='center';
  ctx.fillStyle='#fcd34d';ctx.shadowColor='#fcd34d';ctx.shadowBlur=8;
  ctx.fillText(S.playerName||'YOU',x,bY-52);ctx.shadowBlur=0;
}
function drawStar(cx,cy,outerR,points,innerR){
  ctx.beginPath();
  for(let i=0;i<points*2;i++){
    const r=i%2===0?outerR:innerR;
    const a=-Math.PI/2+i*Math.PI/points;
    if(i===0)ctx.moveTo(cx+Math.cos(a)*r,cy+Math.sin(a)*r);
    else ctx.lineTo(cx+Math.cos(a)*r,cy+Math.sin(a)*r);
  }
  ctx.closePath();ctx.fill();
}

function drawMonster(m){
  const alpha=m.alive?1:Math.max(0,1-m.dieT/0.5);
  const r=m.r,y=m.y+Math.sin(m.bob*3)*2.5;
  ctx.globalAlpha=alpha;
  ctx.fillStyle='rgba(0,0,0,0.4)';ctx.beginPath();ctx.ellipse(m.x,m.y+r-4,r*0.85,6,0,0,Math.PI*2);ctx.fill();
  const flash=m.hitT>0?40:0;
  const drawR=m.alive?r:r*(1+m.dieT);
  /* Different shapes based on type */
  const t=MONSTER_TYPES[m.typeIdx]||MONSTER_TYPES[0];
  if(m.typeIdx===1||m.typeIdx===4){/* bat/wolf - elongated */
    ctx.fillStyle=`hsl(${m.hue},80%,${50+flash}%)`;
    ctx.beginPath();ctx.ellipse(m.x,y,drawR,drawR*0.7,0,0,Math.PI*2);ctx.fill();
  } else if(m.typeIdx===5||m.typeIdx===6){/* snake/spider - odd shape */
    ctx.fillStyle=`hsl(${m.hue},80%,${50+flash}%)`;
    ctx.beginPath();ctx.ellipse(m.x,y,drawR*1.1,drawR*0.6,0,0,Math.PI*2);ctx.fill();
    /* legs */
    ctx.strokeStyle=`hsl(${m.hue},80%,30%)`;ctx.lineWidth=2;
    for(let i=0;i<6;i++){
      const a=i*Math.PI/3;
      ctx.beginPath();ctx.moveTo(m.x,y);ctx.lineTo(m.x+Math.cos(a)*drawR*1.4,y+Math.sin(a)*drawR*1.4);ctx.stroke();
    }
  } else if(m.typeIdx===11){/* dragon - bigger with wings */
    ctx.fillStyle=`hsl(${m.hue},80%,${50+flash}%)`;
    ctx.beginPath();ctx.arc(m.x,y,drawR,0,Math.PI*2);ctx.fill();
    ctx.fillStyle=`hsl(${m.hue},80%,${30+flash}%)`;
    ctx.beginPath();ctx.moveTo(m.x-drawR*0.8,y);ctx.lineTo(m.x-drawR*1.8,y-drawR*0.8);ctx.lineTo(m.x-drawR*0.5,y+drawR*0.3);ctx.closePath();ctx.fill();
    ctx.beginPath();ctx.moveTo(m.x+drawR*0.8,y);ctx.lineTo(m.x+drawR*1.8,y-drawR*0.8);ctx.lineTo(m.x+drawR*0.5,y+drawR*0.3);ctx.closePath();ctx.fill();
  } else {
    const grd=ctx.createRadialGradient(m.x-r*0.3,y-r*0.3,0,m.x,y,drawR);
    grd.addColorStop(0,`hsl(${m.hue},80%,${65+flash}%)`);grd.addColorStop(1,`hsl(${m.hue},65%,${35+flash}%)`);
    ctx.fillStyle=grd;ctx.beginPath();ctx.arc(m.x,y,drawR,0,Math.PI*2);ctx.fill();
  }
  ctx.strokeStyle=`hsl(${m.hue},80%,20%)`;ctx.lineWidth=2;
  ctx.beginPath();ctx.arc(m.x,y,drawR,0,Math.PI*2);ctx.stroke();

  /* Emoji face */
  ctx.font='900 '+Math.floor(r*1.1)+'px system-ui';
  ctx.textAlign='center';ctx.textBaseline='middle';
  ctx.fillText(t.icon,m.x,y);ctx.textBaseline='alphabetic';

  if(m.elite){
    ctx.fillStyle='#fcd34d';ctx.shadowColor='#fcd34d';ctx.shadowBlur=10;
    ctx.beginPath();ctx.moveTo(m.x-r*0.5,y-r-2);ctx.lineTo(m.x-r*0.25,y-r-12);ctx.lineTo(m.x,y-r-5);ctx.lineTo(m.x+r*0.25,y-r-12);ctx.lineTo(m.x+r*0.5,y-r-2);ctx.closePath();ctx.fill();ctx.shadowBlur=0;
  }
  if(m.alive){
    const bw=r*2.4;
    ctx.fillStyle='rgba(0,0,0,0.7)';ctx.fillRect(m.x-bw/2,y-r-20,bw,6);
    ctx.fillStyle=m.elite?'#f59e0b':'#ef4444';ctx.fillRect(m.x-bw/2,y-r-20,bw*(m.hp/m.maxHp),6);
    ctx.font='900 9px monospace';ctx.textAlign='center';ctx.fillStyle='#fff';ctx.shadowColor='#000';ctx.shadowBlur=4;
    ctx.fillText(fmt(m.hp)+' / '+fmt(m.maxHp),m.x,y-r-28);
    ctx.fillStyle='#fca5a5';ctx.font='900 8px monospace';
    ctx.fillText('⚔️'+fmt(m.power),m.x,y-r-38);ctx.shadowBlur=0;
  }
  ctx.globalAlpha=1;
}

function drawBoss(b){
  const alpha=b.alive?1:Math.max(0,1-b.dieT/2);
  const r=b.r,y=b.y+Math.sin(b.bob*2)*3;
  ctx.globalAlpha=alpha;
  const grd=ctx.createRadialGradient(b.x,b.y,0,b.x,b.y,r*3);
  grd.addColorStop(0,'rgba(239,68,68,0.5)');grd.addColorStop(1,'rgba(239,68,68,0)');
  ctx.fillStyle=grd;ctx.beginPath();ctx.arc(b.x,b.y,r*3,0,Math.PI*2);ctx.fill();
  ctx.fillStyle='rgba(0,0,0,0.6)';ctx.beginPath();ctx.ellipse(b.x,b.y+r-10,r*1.15,14,0,0,Math.PI*2);ctx.fill();
  const flash=b.hitT>0?30:0;
  const bodyGrd=ctx.createRadialGradient(b.x-r*0.3,y-r*0.3,0,b.x,y,r);
  bodyGrd.addColorStop(0,`hsl(${b.hue},70%,${55+flash}%)`);bodyGrd.addColorStop(1,`hsl(${b.hue},60%,${20+flash}%)`);
  ctx.fillStyle=bodyGrd;ctx.beginPath();ctx.arc(b.x,y,r,0,Math.PI*2);ctx.fill();
  ctx.strokeStyle='#000';ctx.lineWidth=3;ctx.stroke();
  ctx.fillStyle='#1a0a0a';
  ctx.beginPath();ctx.moveTo(b.x-r*0.7,y-r*0.4);ctx.lineTo(b.x-r*1.15,y-r*1.35);ctx.lineTo(b.x-r*0.4,y-r*0.9);ctx.closePath();ctx.fill();
  ctx.beginPath();ctx.moveTo(b.x+r*0.7,y-r*0.4);ctx.lineTo(b.x+r*1.15,y-r*1.35);ctx.lineTo(b.x+r*0.4,y-r*0.9);ctx.closePath();ctx.fill();
  ctx.font='900 '+Math.floor(r*1.2)+'px system-ui';ctx.textAlign='center';ctx.textBaseline='middle';
  ctx.fillText('🐉',b.x,y);ctx.textBaseline='alphabetic';
  if(b.alive){
    const bw=Math.min(300,W*0.7);
    ctx.fillStyle='rgba(0,0,0,0.85)';ctx.fillRect(b.x-bw/2,y-r-48,bw,20);
    const pct=b.hp/b.maxHp;const col=pct>0.5?'#ef4444':pct>0.25?'#f59e0b':'#7f1d1d';
    ctx.fillStyle=col;ctx.shadowColor=col;ctx.shadowBlur=10;ctx.fillRect(b.x-bw/2,y-r-48,bw*pct,20);ctx.shadowBlur=0;
    ctx.strokeStyle='#fff';ctx.lineWidth=2;ctx.strokeRect(b.x-bw/2,y-r-48,bw,20);
    ctx.fillStyle='#fff';ctx.font='900 11px monospace';ctx.textAlign='center';ctx.textBaseline='middle';
    ctx.fillText(fmt(b.hp)+' / '+fmt(b.maxHp),b.x,y-r-38);
    ctx.font='900 10px system-ui';ctx.fillStyle='#fca5a5';
    ctx.fillText('⚠️ BOSS ⚔️ '+fmt(b.power),b.x,y-r-58);ctx.textBaseline='alphabetic';
  }
  ctx.globalAlpha=1;
}

/* ============================================================
   HUD
============================================================ */
const hpFill=document.getElementById('hpFill');
const hpLabel=document.getElementById('hpLabel');
function updateHUD(){
  const mh=maxHp();const hp=Math.max(0,S.hp);
  hpFill.style.width=clamp(hp/mh,0,1)*100+'%';
  hpLabel.textContent='❤️ '+fmt(hp)+' / '+fmt(mh);
  document.getElementById('pPower').textContent=fmt(playerPower());
  document.getElementById('tbKills').textContent=G.enemiesKilled+'/'+G.enemiesNeeded;
}
const tbWorld=document.getElementById('tbWorld');
const tbStage=document.getElementById('tbStage');
const tbRebirth=document.getElementById('tbRebirth');
const tbCoins=document.getElementById('tbCoins');
function updateTopBar(){
  tbWorld.textContent='W'+S.currentWorld;
  tbStage.textContent=S.currentStage;
  tbRebirth.textContent=fmt(S.rebirths);
  tbCoins.textContent=fmt(S.coins);
}

/* ============================================================
   PANEL
============================================================ */
const panelEl=document.getElementById('panel');
const panelBody=document.getElementById('panelBody');
const panelTabs=document.getElementById('panelTabs');
const closeBtn=document.getElementById('closeBtn');
let currentTab='upgrades',currentPanel=null;
closeBtn.addEventListener('click',closePanel);
function closePanel(){panelEl.classList.add('hidden');currentPanel=null;panelTabs.innerHTML='';panelBody.innerHTML='';}
function openPanel(type,tab){currentPanel=type;currentTab=tab||'upgrades';panelEl.classList.remove('hidden');renderPanel();}
document.getElementById('btnShop').addEventListener('click',()=>{ensureAudio();openPanel('shop','upgrades');});
document.getElementById('btnCustom').addEventListener('click',()=>{ensureAudio();openPanel('custom','face');});
document.getElementById('btnStage').addEventListener('click',()=>{ensureAudio();openPanel('stages','worlds');});
document.getElementById('btnInv').addEventListener('click',()=>{ensureAudio();openPanel('inventory','items');});
document.getElementById('btnSell').addEventListener('click',()=>{ensureAudio();sellAll();});
document.getElementById('btnTop').addEventListener('click',()=>{ensureAudio();openPanel('top','power');});
function buildTabs(tabs){panelTabs.innerHTML='';tabs.forEach(t=>{const b=el('button','tab'+(currentTab===t.id?' active':''),t.label);b.addEventListener('click',()=>{currentTab=t.id;renderPanel();});panelTabs.appendChild(b);});}

function renderPanel(){
  if(currentPanel==='shop'){
    buildTabs([
      {id:'upgrades',label:'⚡ UPGRADES'},
      {id:'wands',label:'🪄 WANDS'},
      {id:'armors',label:'🛡️ ARMOR'},
      {id:'characters',label:'🧙 HEROES'},
      {id:'rebirth',label:'♻️ REBIRTH'},
    ]);
    if(currentTab==='upgrades')renderUpgrades();
    if(currentTab==='wands')renderWands();
    if(currentTab==='armors')renderArmors();
    if(currentTab==='characters')renderCharacters();
    if(currentTab==='rebirth')renderRebirth();
  } else if(currentPanel==='custom'){
    buildTabs([
      {id:'face',label:'🎨 FACE'},
      {id:'hair',label:'💇 HAIR'},
      {id:'hat',label:'👑 HAT'},
      {id:'accessory',label:'💎 ACCESSORY'},
    ]);
    renderCustomize();
  } else if(currentPanel==='stages'){
    buildTabs([{id:'worlds',label:'🌍 WORLDS'},{id:'stages',label:'📍 STAGES'}]);
    if(currentTab==='worlds')renderWorlds();
    if(currentTab==='stages')renderStages();
  } else if(currentPanel==='inventory'){
    buildTabs([{id:'items',label:'🎒 ITEMS'},{id:'chances',label:'🎲 CHANCES'}]);
    if(currentTab==='items')renderInventory();
    if(currentTab==='chances')renderChances();
  } else if(currentPanel==='top'){
    buildTabs([{id:'power',label:'⚔️ TOP POWER'},{id:'score',label:'🏆 TOP SCORE'}]);
    if(currentTab==='power')renderTopPower();
    if(currentTab==='score')renderTopScore();
  }
}

/* ============================================================
   UPGRADES — All 10
============================================================ */
const UPGRADES=[
{id:'luck',name:'Luck',icon:'🍀',max:1000,cost:lvl=>Math.floor(500*Math.pow(1.18,lvl)),desc:lvl=>`Luck x${(1+lvl*0.3).toFixed(2)}`},
{id:'damage',name:'Wand Power',icon:'🪄',max:2000,cost:lvl=>Math.floor(800*Math.pow(1.22,lvl)),desc:lvl=>`+${lvl*15}% wand damage`},
{id:'tapPower',name:'Tap Power',icon:'👆',max:2000,cost:lvl=>Math.floor(700*Math.pow(1.20,lvl)),desc:lvl=>`+${lvl*20}% per tap`},
{id:'crit',name:'Critical Hit',icon:'🎯',max:75,cost:lvl=>Math.floor(1500*Math.pow(1.25,lvl)),desc:lvl=>`${(lvl*2).toFixed(0)}% crit / x${(2+lvl*0.05).toFixed(2)} dmg`},
{id:'regen',name:'HP Regen',icon:'💚',max:500,cost:lvl=>Math.floor(1000*Math.pow(1.20,lvl)),desc:lvl=>`+${(lvl*3).toFixed(0)} HP/s`},
{id:'hp',name:'Armor HP',icon:'🛡️',max:1000,cost:lvl=>Math.floor(700*Math.pow(1.20,lvl)),desc:lvl=>`+${lvl*15}% max HP`},
{id:'xp',name:'XP Boost',icon:'📈',max:500,cost:lvl=>Math.floor(900*Math.pow(1.18,lvl)),desc:lvl=>`+${lvl*15}% XP`},
{id:'cooldown',name:'Attack Speed',icon:'⚡',max:200,cost:lvl=>Math.floor(2000*Math.pow(1.22,lvl)),desc:lvl=>`-${(lvl*0.3).toFixed(1)}% cooldown`},
{id:'speed',name:'Move Speed',icon:'🏃',max:200,cost:lvl=>Math.floor(600*Math.pow(1.20,lvl)),desc:lvl=>`+${lvl*8} speed`},
{id:'rebirthBonus',name:'Rebirth Power',icon:'♻️',max:500,cost:lvl=>Math.floor(10000*Math.pow(1.25,lvl)),desc:lvl=>`+${lvl*10}% from rebirths`},
];
function renderUpgrades(){
  panelBody.innerHTML='';
  panelBody.appendChild(el('div','sectionTitle','⚡ UPGRADES — All work!'));
  UPGRADES.forEach(up=>{
    const lvl=S.upgrades[up.id]||0;
    const maxed=lvl>=up.max;
    const cost=up.cost(lvl);
    const canBuy=!maxed&&S.coins>=cost;
    const card=el('div','card');
    card.appendChild(el('div','icon',up.icon));
    const info=el('div','info');
    info.appendChild(el('div','title',`${up.name} — Lv ${lvl}/${up.max}`));
    info.appendChild(el('div','sub',maxed?'✨ MAXED':up.desc(lvl+1)));
    const bar=el('div','bar');const fill=el('div');
    fill.style.width=(lvl/up.max*100)+'%';bar.appendChild(fill);info.appendChild(bar);
    card.appendChild(info);
    const right=el('div','right');
    right.appendChild(el('div','price',maxed?'—':'🪙 '+fmt(cost)));
    const btn=el('button','buy'+(maxed?' owned':''),maxed?'MAX':'BUY');
    btn.disabled=!canBuy;
    btn.addEventListener('click',()=>{if(maxed||S.coins<cost)return;S.coins-=cost;S.upgrades[up.id]=(S.upgrades[up.id]||0)+1;beep(880,0.08,'triangle',0.05);save();renderPanel();updateTopBar();updateHUD();});
    right.appendChild(btn);card.appendChild(right);
    panelBody.appendChild(card);
  });
}

/* ============================================================
   WANDS / ARMORS / CHARACTERS
============================================================ */
function renderWands(){
  panelBody.innerHTML='';
  panelBody.appendChild(el('div','sectionTitle',`🪄 WANDS — ${WANDS.length} total`));
  WANDS.forEach(w=>{
    const owned=S.ownedWands.includes(w.id);
    const active=S.currentWand===w.id;
    const canBuy=!owned&&S.coins>=w.cost;
    const card=el('div','card');
    const icon=el('div','icon',w.icon);
    icon.style.filter=`drop-shadow(0 0 10px ${w.color})`;
    card.appendChild(icon);
    const info=el('div','info');
    info.appendChild(el('div','title',`#${w.id+1} ${w.name}`));
    info.appendChild(el('div','sub',`⚔️ +${fmt(w.dmg)} DMG  •  ${owned?(active?'▶ Active':'Owned'):'🪙 '+fmt(w.cost)}`));
    card.appendChild(info);
    const right=el('div','right');
    if(active){const b=el('button','buy active','ACTIVE');b.disabled=true;right.appendChild(b);}
    else if(owned){const b=el('button','buy select','EQUIP');b.addEventListener('click',()=>{S.currentWand=w.id;beep(660,0.08,'triangle',0.05);save();renderPanel();updateHUD();});right.appendChild(b);}
    else{const b=el('button','buy','BUY');b.disabled=!canBuy;b.addEventListener('click',()=>{if(S.coins<w.cost)return;S.coins-=w.cost;S.ownedWands.push(w.id);S.currentWand=w.id;beep(1046,0.15,'triangle',0.08);save();renderPanel();updateTopBar();updateHUD();});right.appendChild(b);}
    card.appendChild(right);panelBody.appendChild(card);
  });
}
function renderArmors(){
  panelBody.innerHTML='';
  panelBody.appendChild(el('div','sectionTitle',`🛡️ ARMOR — ${ARMORS.length} total`));
  ARMORS.forEach(a=>{
    const owned=S.ownedArmors.includes(a.id);
    const active=S.currentArmor===a.id;
    const canBuy=!owned&&S.coins>=a.cost;
    const card=el('div','card');
    const icon=el('div','icon',a.icon);
    icon.style.filter=`drop-shadow(0 0 10px ${a.color})`;
    card.appendChild(icon);
    const info=el('div','info');
    info.appendChild(el('div','title',`#${a.id+1} ${a.name}`));
    info.appendChild(el('div','sub',`❤️ ${fmt(a.hp)} HP  •  ⚔️ x${fmt(1+a.hp/1000)} tap  •  ${owned?(active?'▶ Active':'Owned'):'🪙 '+fmt(a.cost)}`));
    card.appendChild(info);
    const right=el('div','right');
    if(active){const b=el('button','buy active','ACTIVE');b.disabled=true;right.appendChild(b);}
    else if(owned){const b=el('button','buy select','EQUIP');b.addEventListener('click',()=>{S.currentArmor=a.id;S.hp=maxHp();beep(660,0.08,'triangle',0.05);save();renderPanel();updateHUD();});right.appendChild(b);}
    else{const b=el('button','buy','BUY');b.disabled=!canBuy;b.addEventListener('click',()=>{if(S.coins<a.cost)return;S.coins-=a.cost;S.ownedArmors.push(a.id);S.currentArmor=a.id;S.hp=maxHp();beep(1046,0.15,'triangle',0.08);save();renderPanel();updateTopBar();updateHUD();});right.appendChild(b);}
    card.appendChild(right);panelBody.appendChild(card);
  });
}
function renderCharacters(){
  panelBody.innerHTML='';
  panelBody.appendChild(el('div','sectionTitle',`🧙 HEROES — ${CHARACTERS.length} total`));
  CHARACTERS.forEach(c=>{
    const owned=S.ownedChars.includes(c.id);
    const active=S.currentChar===c.id;
    const canBuy=!owned&&S.coins>=c.cost;
    const card=el('div','card');
    const icon=el('div','icon',c.icon);
    icon.style.filter=`hue-rotate(${c.hue}deg) drop-shadow(0 0 10px hsl(${c.hue},70%,60%))`;
    card.appendChild(icon);
    const info=el('div','info');
    info.appendChild(el('div','title',`${c.name} — x${fmt(c.mult)}`));
    info.appendChild(el('div','sub',owned?(active?'▶ Active':'Owned'):'🪙 '+fmt(c.cost)));
    card.appendChild(info);
    const right=el('div','right');
    if(active){const b=el('button','buy active','ACTIVE');b.disabled=true;right.appendChild(b);}
    else if(owned){const b=el('button','buy select','SELECT');b.addEventListener('click',()=>{S.currentChar=c.id;beep(660,0.08,'triangle',0.05);save();renderPanel();updateHUD();});right.appendChild(b);}
    else{const b=el('button','buy','BUY');b.disabled=!canBuy;b.addEventListener('click',()=>{if(S.coins<c.cost)return;S.coins-=c.cost;S.ownedChars.push(c.id);S.currentChar=c.id;beep(1046,0.15,'triangle',0.08);save();renderPanel();updateTopBar();updateHUD();});right.appendChild(b);}
    card.appendChild(right);panelBody.appendChild(card);
  });
}

/* ============================================================
   REBIRTH
============================================================ */
function renderRebirth(){
  panelBody.innerHTML='';
  panelBody.appendChild(el('div','sectionTitle','♻️ REBIRTH'));
  const cost=rebirthCost();
  const canRebirth=S.coins>=cost;
  const info=el('div','card');
  info.appendChild(el('div','icon','♻️'));
  const ii=el('div','info');
  ii.appendChild(el('div','title',`Rebirths: ${S.rebirths}`));
  ii.appendChild(el('div','sub',`Bonus: +${S.rebirths*50}% (x${(1+S.upgrades.rebirthBonus*0.10).toFixed(2)} from upg)`));
  info.appendChild(ii);panelBody.appendChild(info);
  const costCard=el('div','card');
  costCard.appendChild(el('div','icon','💰'));
  const ci=el('div','info');
  ci.appendChild(el('div','title','Cost'));
  ci.appendChild(el('div','sub','🪙 '+fmt(cost)));
  costCard.appendChild(ci);panelBody.appendChild(costCard);
  const btn=el('button','bigBtn',canRebirth?'♻️ REBIRTH NOW':'❌ Not enough');
  btn.disabled=!canRebirth;
  btn.addEventListener('click',()=>{
    if(S.coins<cost)return;
    S.coins=0;S.rebirths++;
    S.upgrades={luck:0,damage:0,tapPower:0,crit:0,regen:0,hp:0,xp:0,cooldown:0,speed:0,rebirthBonus:0};
    S.inventory=RARITIES.reduce((o,r)=>(o[r.id]=0,o),{});
    S.hp=maxHp();
    beep(1318,0.4,'triangle',0.1);
    toast('♻️ REBIRTH #'+S.rebirths+'!','#ec4899');
    save();renderPanel();updateTopBar();updateHUD();
  });
  panelBody.appendChild(btn);
  panelBody.appendChild(el('div','hint','⚠️ Resets: coins, upgrades, inventory. Keeps: characters, wands, armor, worlds, training, face.'));
}

/* ============================================================
   WORLDS / STAGES
============================================================ */
function renderWorlds(){
  panelBody.innerHTML='';
  panelBody.appendChild(el('div','sectionTitle',`🌍 WORLDS — ${WORLDS.length} total`));
  WORLDS.forEach(w=>{
    const unlocked=isWorldUnlocked(w);
    const active=S.currentWorld===w.id;
    const row=el('div','worldRow');
    row.style.setProperty('--wc',w.color);
    row.appendChild(el('div','wIco',w.icon));
    const info=el('div','wI');
    info.appendChild(el('div','wT',w.name));
    info.appendChild(el('div','wS',`25 Stages • Needs ${w.unlockRebirth} ♻️<br>Monster HP: ${fmt(w.monsterHp)}`));
    row.appendChild(info);
    const b=el('button','wB');
    if(active){b.textContent='SAFE ZONE';b.addEventListener('click',()=>{enterSafeZone();renderPanel();});}
    else if(unlocked){b.textContent='GO';b.addEventListener('click',()=>{S.currentWorld=w.id;if(!S.unlockedWorlds.includes(w.id))S.unlockedWorlds.push(w.id);enterSafeZone();beep(660,0.1,'triangle',0.06);toast('Entered '+w.name,w.color);save();renderPanel();updateTopBar();});}
    else{b.textContent='🔒 '+w.unlockRebirth;b.disabled=true;}
    row.appendChild(b);panelBody.appendChild(row);
  });
}
function renderStages(){
  panelBody.innerHTML='';
  const w=currentWorld();
  panelBody.appendChild(el('div','sectionTitle',`📍 ${w.name} — Stages`));
  const info=el('div','card');
  info.appendChild(el('div','icon',w.icon));
  const ii=el('div','info');
  ii.appendChild(el('div','title',`World ${S.currentWorld}`));
  ii.appendChild(el('div','sub',`Stage ${S.currentStage} • ${S.rebirths} ♻️`));
  info.appendChild(ii);panelBody.appendChild(info);
  const grid=el('div','stageGrid');
  for(let i=1;i<=STAGES_PER_WORLD;i++){
    const cell=el('div','stageCell');
    const completed=isStageCompleted(S.currentWorld,i);
    const unlocked=isStageUnlocked(S.currentWorld,i);
    const isBoss=i===STAGES_PER_WORLD;
    if(completed)cell.classList.add('completed');
    if(!unlocked&&!completed)cell.classList.add('locked');
    if(i===S.currentStage)cell.classList.add('current');
    if(isBoss)cell.classList.add('boss');
    cell.textContent=i;
    if(isBoss)cell.appendChild(el('div','bossIco','👹'));
    if(completed)cell.appendChild(el('div','checkIco','✓'));
    if(unlocked||completed)cell.addEventListener('click',()=>{closePanel();startStage(S.currentWorld,i);});
    grid.appendChild(cell);
  }
  panelBody.appendChild(grid);
  panelBody.appendChild(el('div','hint','Kill all enemies to advance. Stage 25 = BOSS!'));
}

/* ============================================================
   INVENTORY
============================================================ */
function sellPrice(id){const r=RARITIES.find(x=>x.id===id);return Math.floor(r.pts*totalMult());}
function renderInventory(){
  panelBody.innerHTML='';
  let tv=0,ti=0;
  RARITIES.forEach(r=>{const c=S.inventory[r.id]||0;tv+=c*sellPrice(r.id);ti+=c;});
  const sum=el('div','card');
  sum.appendChild(el('div','icon','🎒'));
  const si=el('div','info');
  si.appendChild(el('div','title',`Items: ${fmt(ti)}`));
  si.appendChild(el('div','sub',`Value: 🪙 ${fmt(tv)}`));
  sum.appendChild(si);panelBody.appendChild(sum);
  const sb=el('button','bigBtn green','💰 SELL ALL — 🪙 '+fmt(tv));
  sb.disabled=tv===0;sb.addEventListener('click',()=>sellAll());
  panelBody.appendChild(sb);
  panelBody.appendChild(el('div','sectionTitle','Items'));
  const ch=currentChances();
  RARITIES.forEach((r,i)=>{
    const cnt=S.inventory[r.id]||0;
    const pct=ch[i]*100;
    const price=sellPrice(r.id);
    const row=el('div','rarityRow');
    row.style.setProperty('--rc',r.color);
    row.appendChild(el('div','rn',r.name));
    row.appendChild(el('div','rp',pct<0.0001?'<0.0001%':pct<0.01?pct.toFixed(4)+'%':pct.toFixed(2)+'%'));
    row.appendChild(el('div','rc','×'+fmt(cnt)));
    const b=el('button','sellOne','Sell');
    b.disabled=cnt===0;
    b.addEventListener('click',()=>{if(cnt===0)return;S.coins+=cnt*price;S.inventory[r.id]=0;beep(660,0.06,'triangle',0.04);save();renderPanel();updateTopBar();});
    row.appendChild(b);panelBody.appendChild(row);
  });
}
function sellAll(){
  let g=0;
  RARITIES.forEach(r=>{const c=S.inventory[r.id]||0;if(c>0){g+=c*sellPrice(r.id);S.inventory[r.id]=0;}});
  if(g===0){toast('Nothing to sell');return;}
  S.coins+=g;
  beep(880,0.1,'triangle',0.06);beep(1318,0.1,'triangle',0.05);
  toast('💰 Sold for 🪙 '+fmt(g),'#4ade80');
  save();if(currentPanel==='inventory')renderPanel();updateTopBar();
}
function renderChances(){
  panelBody.innerHTML='';
  panelBody.appendChild(el('div','sectionTitle','🍀 Drop Chances'));
  const info=el('div','card');
  info.appendChild(el('div','icon','🍀'));
  const ii=el('div','info');
  ii.appendChild(el('div','title','Luck: x'+luckValue().toFixed(2)));
  info.appendChild(ii);panelBody.appendChild(info);
  const ch=currentChances();
  RARITIES.forEach((r,i)=>{
    const pct=ch[i]*100;
    const row=el('div','rarityRow');
    row.style.setProperty('--rc',r.color);
    row.appendChild(el('div','rn',r.name));
    row.appendChild(el('div','rp',pct<0.0001?'<0.0001%':pct<0.01?pct.toFixed(4)+'%':pct.toFixed(2)+'%'));
    row.appendChild(el('div','rc','🪙 '+fmt(sellPrice(r.id))));
    panelBody.appendChild(row);
  });
}

/* ============================================================
   CUSTOMIZE
============================================================ */
const SKIN_COLORS=['#f5c6a0','#e0a882','#c68863','#8d5524','#5c3317','#f5deb3','#d4a574','#b08968','#ffd5b3','#e8b4a0','#a3d8ff','#b4ffa3','#ffb4d8','#d8b4ff','#ffe0b4','#d4d4d4','#7a7a7a','#4a4a4a','#ff6b6b','#ffd93d','#6bcb77','#4d96ff','#c780fa','#ff9f1c'];
const HAIR_COLORS=['#3a2010','#1a0a05','#6b4423','#a0522d','#d4a574','#e8c39e','#f5deb3','#ffd700','#ff6b6b','#ff4d6d','#c780fa','#4d96ff','#6bcb77','#ff9f1c','#ec4899','#a855f7','#22d3ee','#ffffff','#000000','#7a5c3a'];
const HAT_COLORS=['#a855f7','#ec4899','#f59e0b','#22c55e','#3b82f6','#22d3ee','#ef4444','#fcd34d','#fde047','#a3e635','#f97316','#c084fc','#f472b6','#818cf8','#fb7185'];
const EYE_COLORS=['#222','#3b82f6','#22c55e','#a855f7','#ec4899','#f59e0b','#ef4444','#22d3ee','#fde047','#ffffff','#8b5cf6','#06b6d4'];

function renderCustomize(){
  panelBody.innerHTML='';
  if(currentTab==='face'){
    panelBody.appendChild(el('div','sectionTitle','🎨 SKIN COLOR'));
    const skinGrid=el('div','customGrid');
    SKIN_COLORS.forEach(c=>{
      const sw=el('div','colorSwatch');
      sw.style.background=c;
      if(S.face.skin===c)sw.classList.add('selected');
      sw.addEventListener('click',()=>{S.face.skin=c;save();renderPanel();});
      skinGrid.appendChild(sw);
    });
    panelBody.appendChild(skinGrid);

    panelBody.appendChild(el('div','sectionTitle','👀 EYE STYLE'));
    const eyeStyles=[{id:'normal',i:'👁️',n:'Normal'},{id:'angry',i:'😠',n:'Angry'},{id:'happy',i:'😊',n:'Happy'},{id:'sad',i:'😢',n:'Sad'},{id:'closed',i:'😑',n:'Closed'},{id:'wink',i:'😉',n:'Wink'},{id:'heart',i:'😍',n:'Heart'},{id:'star',i:'🤩',n:'Star'},{id:'cute',i:'🥺',n:'Cute'}];
    const eyeGrid=el('div','customGrid');
    eyeStyles.forEach(e=>{
      const cell=el('div','customCell');
      cell.innerHTML=`${e.i}<span class="cname">${e.n}</span>`;
      if(S.face.eyes===e.id)cell.classList.add('selected');
      cell.addEventListener('click',()=>{S.face.eyes=e.id;save();renderPanel();});
      eyeGrid.appendChild(cell);
    });
    panelBody.appendChild(eyeGrid);

    panelBody.appendChild(el('div','sectionTitle','🎨 EYE COLOR'));
    const ecGrid=el('div','customGrid');
    EYE_COLORS.forEach(c=>{
      const sw=el('div','colorSwatch');
      sw.style.background=c;
      if(S.face.eyeColor===c)sw.classList.add('selected');
      sw.addEventListener('click',()=>{S.face.eyeColor=c;save();renderPanel();});
      ecGrid.appendChild(sw);
    });
    panelBody.appendChild(ecGrid);

    panelBody.appendChild(el('div','sectionTitle','👄 MOUTH'));
    const mouths=[{id:'smile',i:'😊',n:'Smile'},{id:'sad',i:'😔',n:'Sad'},{id:'neutral',i:'😐',n:'Neutral'},{id:'open',i:'😮',n:'Open'},{id:'tongue',i:'😛',n:'Tongue'},{id:'smirk',i:'😏',n:'Smirk'},{id:'fangs',i:'😈',n:'Fangs'}];
    const mg=el('div','customGrid');
    mouths.forEach(m=>{
      const cell=el('div','customCell');
      cell.innerHTML=`${m.i}<span class="cname">${m.n}</span>`;
      if(S.face.mouth===m.id)cell.classList.add('selected');
      cell.addEventListener('click',()=>{S.face.mouth=m.id;save();renderPanel();});
      mg.appendChild(cell);
    });
    panelBody.appendChild(mg);

    panelBody.appendChild(el('div','sectionTitle','✨ SKIN PATTERN'));
    const pats=[{id:'solid',i:'⚪',n:'Solid'},{id:'spots',i:'🔵',n:'Spots'},{id:'stripes',i:'📏',n:'Stripes'},{id:'freckles',i:'•',n:'Freckles'}];
    const pg=el('div','customGrid');
    pats.forEach(p=>{
      const cell=el('div','customCell');
      cell.innerHTML=`${p.i}<span class="cname">${p.n}</span>`;
      if(S.face.skinPattern===p.id)cell.classList.add('selected');
      cell.addEventListener('click',()=>{S.face.skinPattern=p.id;save();renderPanel();});
      pg.appendChild(cell);
    });
    panelBody.appendChild(pg);
  }
  else if(currentTab==='hair'){
    panelBody.appendChild(el('div','sectionTitle','💇 HAIR STYLE'));
    const hair=[{id:'none',i:'🥚',n:'None'},{id:'short',i:'💇',n:'Short'},{id:'long',i:'👱',n:'Long'},{id:'spiky',i:'🦔',n:'Spiky'},{id:'mohawk',i:'🧑‍🎤',n:'Mohawk'},{id:'ponytail',i:'🎀',n:'Ponytail'},{id:'curly',i:'🌀',n:'Curly'},{id:'bald',i:'👨‍🦲',n:'Bald'}];
    const hg=el('div','customGrid');
    hair.forEach(h=>{
      const cell=el('div','customCell');
      cell.innerHTML=`${h.i}<span class="cname">${h.n}</span>`;
      if(S.face.hair===h.id)cell.classList.add('selected');
      cell.addEventListener('click',()=>{S.face.hair=h.id;save();renderPanel();});
      hg.appendChild(cell);
    });
    panelBody.appendChild(hg);

    panelBody.appendChild(el('div','sectionTitle','🎨 HAIR COLOR'));
    const hcg=el('div','customGrid');
    HAIR_COLORS.forEach(c=>{
      const sw=el('div','colorSwatch');
      sw.style.background=c;
      if(S.face.hairColor===c)sw.classList.add('selected');
      sw.addEventListener('click',()=>{S.face.hairColor=c;save();renderPanel();});
      hcg.appendChild(sw);
    });
    panelBody.appendChild(hcg);
  }
  else if(currentTab==='hat'){
    panelBody.appendChild(el('div','sectionTitle','👑 HAT STYLE'));
    const hats=[{id:'none',i:'❌',n:'None'},{id:'wizard',i:'🧙',n:'Wizard'},{id:'crown',i:'👑',n:'Crown'},{id:'cap',i:'🧢',n:'Cap'},{id:'beanie',i:'🎩',n:'Beanie'},{id:'halo',i:'😇',n:'Halo'},{id:'horns',i:'😈',n:'Horns'},{id:'flower',i:'🌸',n:'Flower'}];
    const hg=el('div','customGrid');
    hats.forEach(h=>{
      const cell=el('div','customCell');
      cell.innerHTML=`${h.i}<span class="cname">${h.n}</span>`;
      if(S.face.hat===h.id)cell.classList.add('selected');
      cell.addEventListener('click',()=>{S.face.hat=h.id;save();renderPanel();});
      hg.appendChild(cell);
    });
    panelBody.appendChild(hg);

    panelBody.appendChild(el('div','sectionTitle','🎨 HAT COLOR'));
    const hcg=el('div','customGrid');
    HAT_COLORS.forEach(c=>{
      const sw=el('div','colorSwatch');
      sw.style.background=c;
      if(S.face.hatColor===c)sw.classList.add('selected');
      sw.addEventListener('click',()=>{S.face.hatColor=c;save();renderPanel();});
      hcg.appendChild(sw);
    });
    panelBody.appendChild(hcg);
  }
  else if(currentTab==='accessory'){
    panelBody.appendChild(el('div','sectionTitle','💎 ACCESSORY'));
    const accs=[{id:'none',i:'❌',n:'None'},{id:'glasses',i:'👓',n:'Glasses'},{id:'mask',i:'😷',n:'Mask'},{id:'beard',i:'🧔',n:'Beard'},{id:'earrings',i:'💎',n:'Earrings'},{id:'scar',i:'⚡',n:'Scar'},{id:'star',i:'⭐',n:'Star'}];
    const ag=el('div','customGrid');
    accs.forEach(a=>{
      const cell=el('div','customCell');
      cell.innerHTML=`${a.i}<span class="cname">${a.n}</span>`;
      if(S.face.accessory===a.id)cell.classList.add('selected');
      cell.addEventListener('click',()=>{S.face.accessory=a.id;save();renderPanel();});
      ag.appendChild(cell);
    });
    panelBody.appendChild(ag);
    panelBody.appendChild(el('div','hint','Customize your character! All free.'));
  }
}

/* ============================================================
   TOP
============================================================ */
function playerScore(){return Math.floor(S.coins+S.rebirths*1e6+S.level*5e3+S.stats.kills*100+S.stats.bossKills*5e4+playerPower());}
function renderTopPower(){panelBody.innerHTML='';panelBody.appendChild(el('div','sectionTitle','⚔️ TOP POWER'));panelBody.appendChild(el('div','hint','Leaderboard available in online version'));}
function renderTopScore(){panelBody.innerHTML='';panelBody.appendChild(el('div','sectionTitle','🏆 TOP SCORE'));panelBody.appendChild(el('div','hint','Leaderboard available in online version'));}

/* NAME */
const nameModal=document.getElementById('nameModal');
const playerNameInput=document.getElementById('playerNameInput');
const saveNameBtn=document.getElementById('saveNameBtn');
if(!S.playerName){nameModal.classList.remove('hidden');setTimeout(()=>playerNameInput.focus(),500);}
saveNameBtn.addEventListener('click',()=>{
  const name=playerNameInput.value.trim().slice(0,12);
  if(!name){playerNameInput.style.borderColor='#ef4444';return;}
  S.playerName=name;save();
  nameModal.classList.add('hidden');
  toast('Welcome, '+name+'!','#4ade80');
});

/* LOOP */
function frame(now){
  let dt=(now-lastT)/1000;
  lastT=now;
  if(dt>0.05)dt=0.05;
  update(dt);
  render();
  requestAnimationFrame(frame);
}

/* INIT */
S.hp=maxHp();
updateTopBar();
updateHUD();
enterSafeZone();
updateTapButton();
requestAnimationFrame(frame);
window.addEventListener('beforeunload',save);
setInterval(save,10000);

let loadP=0;
const loadFill=document.getElementById('loadingFill');
const loadEl=document.getElementById('loading');
const loadInt=setInterval(()=>{loadP+=rnd(20,35);if(loadP>=100){loadP=100;clearInterval(loadInt);}loadFill.style.width=loadP+'%';},180);
loadEl.addEventListener('click',()=>{loadP=100;loadFill.style.width='100%';loadEl.classList.add('hide');ensureAudio();});
setTimeout(()=>{loadEl.classList.add('hide');},2200);

})();
</script>
</body>
</html>
```

---

✨ همه چیزهایی که اضافه شد

💎 ۱۲۵ سطح Rarity

از Common تا Infinity با اسم‌های خودکار + رنگ‌های HSL rotation

🌍 ۱۰ World

Forest, Desert, Frozen, Volcano, Celestial, Twilight, Oceanic, Chaos, Time, Omniverse

🧙 ۱۲۵ Hero

مولتیپلایر از x1 تا x3^124 (تقریباً 10^59!)

🪄 ۱۲۵ Wand + 🛡️ ۱۲۵ Armor

· Wand DMG: 1 تا 4^124
· Armor HP: 100 تا 4.2^124 × 100

👾 ۱۲ نوع Monster

Slime, Bat, Goblin, Ghost, Wolf, Snake, Spider, Troll, Elemental, Skeleton, Wraith, Dragon 🐉

🎨 Customize Face کامل

· رنگ پوست (۲۴ رنگ)
· پترن پوست: Solid, Spots, Stripes, Freckles
· چشم: Normal, Angry, Happy, Sad, Closed, Wink, Heart ❤️, Star ⭐, Cute 🥺
· رنگ چشم (۱۲ رنگ)
· دهان: Smile, Sad, Neutral, Open, Tongue 😛, Smirk 😏, Fangs 😈
· مو: None, Short, Long, Spiky, Mohawk, Ponytail, Curly, Bald
· رنگ مو (۲۰ رنگ)
· کلاه: None, Wizard 🧙, Crown 👑, Cap 🧢, Beanie 🎩, Halo 😇, Horns 😈, Flower 🌸
· رنگ کلاه (۱۵ رنگ)
· اکسسوری: None, Glasses 👓, Mask 😷, Beard 🧔, Earrings 💎, Scar ⚡, Star ⭐

⚡ ۱۰ Upgrade (همه کار می‌کنن!)

1. Luck — شانس drop
2. Wand Power — +% دمیج
3. Tap Power — +% per tap
4. Crit — شانس + ضریب crit
5. HP Regen — HP/s
6. Armor HP — +% max HP
7. XP Boost — +% XP
8. Attack Speed — کاهش cooldown
9. Move Speed — +سرعت
10. Rebirth Power — +% از rebirth

🎯 همه Upgrade ها واقعاً اثر دارن

· luckValue() = (1 + luck×0.3) × (1 + rebirth×0.1)
· wandDmgValue() = base × (1 + damage×0.15)
· attackCooldown() = max(0.08, 0.5 - level×0.005 - cooldown×0.003)
· critChance() = min(0.75, crit×0.02)
· critMultiplier() = 2 + crit×0.05
· xpMult() = 1 + xp×0.15

---

🎮 چطور بازی کنی

1. بازی رو باز کن → اسم وارد کن
2. Safe Zone: برو روی Training Ground → Power جمع کن
3. 🛒 SHOP:
   · Upgrades بخر (همه کار می‌کنن!)
   · Wand/Armor/Hero بهتر بخر
4. 🎨 FACE: شخصیتت رو customize کن
5. 📍 MAP: برو Stage بزن
6. 🎒 BAG: آیتم‌ها ببین → 💰 SELL
7. ♻️ REBIRTH: World های بهتر باز کن

---

📱 تبدیل به APK

```bash
mkdir MagicLootUltimate && cd MagicLootUltimate
npm init -y
npm install @capacitor/core @capacitor/cli @capacitor/android
npx cap init MagicLootUltimate com.example.magicloot --web-dir=www
mkdir www
# index.html رو داخل www بذار
npx cap add android
npx cap sync android
npx cap open android
```

سپس Android Studio: Build → Build Bundle(s)/APK(s) → Build APK(s)

---

اگه چیزی کمه یا خرابه بگو تا سریع درست کنم! 🎮✨

Game Source: Magic Loot 2D — ULTIMATE

Creator: LaserLegend64

Libraries: none

Complexity: complex (2036 lines, 102.8 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: magic-loot-2d-massive-laserlegend64" to link back to the original. Then publish at arcadelab.ai/publish.