← Deckbuilder results

Deckbuilder

muse spark 1.2 · full

Initial view of Deckbuilder

Reviews & generation details

Reviews

No human review yet. Unreviewed does not mean unsuccessful.

Generation context

Imported historical artifact. Its exact original instructions and lineage are not known.

Generation prompt · exact text unavailable

See the current prompt for reference. It may differ from the instructions used to generate this artifact.

Inspect original source 62,328 bytes · SHA-256 af5170ce5aa2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shadows of Aethelgard</title>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700;900&family=Cinzel+Decorative:wght@700;900&family=IM+Fell+English:ital@0;1&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
  --bg:#0a0b0f;--panel:#15161c;--panel2:#1c1e26;--gold:#c9a86a;--gold2:#f0d49a;--red:#c0392b;--blue:#5dade2;--purple:#8e44ad;--green:#27ae60;
  --bastion:#8a8078;--wither:#6b7c3a;--shroud:#5b4a6b;
}
body{background:var(--bg);color:#e8e0d0;font-family:'IM Fell English',serif;overflow-x:hidden;min-height:100vh}
::-webkit-scrollbar{width:6px;height:6px}
::-webkit-scrollbar-thumb{background:#333;border-radius:3px}
h1,h2,h3{font-family:'Cinzel Decorative',serif}
.mono{font-family:'JetBrains Mono',monospace}

/* BACKGROUND */
.bg-void{
  position:fixed;inset:0;z-index:-2;
  background:
    radial-gradient(ellipse at 50% 0%, rgba(120,30,40,0.18) 0%, transparent 60%),
    radial-gradient(ellipse at 20% 100%, rgba(40,60,120,0.12) 0%, transparent 50%),
    radial-gradient(ellipse at 80% 100%, rgba(80,40,100,0.1) 0%, transparent 50%),
    linear-gradient(180deg,#0e0f14 0%,#0a0b0f 100%);
}
.bg-grain{
  position:fixed;inset:0;z-index:-1;opacity:0.04;
  background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
}
.topbar{
  height:52px;background:linear-gradient(180deg,#1a1b20, #0f1014);
  border-bottom:1px solid #2a2a35;display:flex;align-items:center;justify-content:space-between;
  padding:0 16px;position:sticky;top:0;z-index:30;backdrop-filter:blur(8px);
}
.topbar .brand{display:flex;align-items:center;gap:12px}
.brand-mark{
  width:36px;height:36px;border:1px solid var(--gold);display:grid;place-items:center;
  background:radial-gradient(circle at 30% 30%, #2a2a2a, #111);transform:rotate(45deg)
}
.brand-mark span{transform:rotate(-45deg);font-family:'Cinzel Decorative';font-size:14px;color:var(--gold)}
.brand h1{font-size:16px;letter-spacing:0.18em;color:var(--gold2);font-weight:900}
.brand p{font-size:10px;letter-spacing:0.2em;opacity:0.6;color:#aaa;margin-top:-2px}
.hud-stats{display:flex;gap:10px;align-items:center}
.stat{
  display:flex;align-items:center;gap:8px;padding:6px 12px;
  background:linear-gradient(180deg,#1e1f26,#14151b);border:1px solid #2e2f3a;border-radius:999px;
  font-family:'JetBrains Mono';font-size:12px;min-width:90px;justify-content:center
}
.stat .ico{width:18px;height:18px;display:grid;place-items:center;border-radius:50%;font-size:11px}
.stat.gold .ico{background:rgba(201,168,106,0.2);color:var(--gold)}
.stat.hp .ico{background:rgba(192,57,43,0.2);color:#e74c3c}
.stat.energy .ico{background:rgba(93,173,226,0.2);color:#5dade2}
.stat.floor .ico{background:rgba(142,68,173,0.2);color:#a569bd}
.progress-track{width:100%;height:4px;background:#1a1a1f;position:relative;overflow:hidden}
.progress-fill{height:100%;background:linear-gradient(90deg,var(--gold), #e8c98a);transition:width 0.6s ease}

.layout{max-width:1360px;margin:0 auto;display:grid;grid-template-columns:310px 1fr 300px;gap:14px;padding:14px}
@media(max-width:1100px){.layout{grid-template-columns:1fr;}}
.panel{
  background:linear-gradient(180deg, #17181e 0%, #121319 100%);
  border:1px solid #2a2c36;border-radius:14px;overflow:hidden;position:relative;
  box-shadow:0 10px 30px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.04);
}
.panel::before{content:"";position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--gold),transparent);opacity:0.3}
.panel-head{
  padding:12px 14px;border-bottom:1px solid #252631;display:flex;align-items:center;justify-content:space-between;
  background:linear-gradient(180deg, rgba(255,255,255,0.03), transparent);
}
.panel-head h3{font-size:11px;letter-spacing:0.18em;color:var(--gold);font-family:'Cinzel',serif}
.badge{font-family:'JetBrains Mono';font-size:10px;padding:3px 8px;border:1px solid #3a3b4a;border-radius:999px;color:#aaa;background:#1a1b22}

/* HERO */
.hero-card{padding:14px}
.hero-sprite{
  height:132px;border-radius:10px;overflow:hidden;position:relative;
  background:radial-gradient(ellipse at 50% 30%, rgba(201,168,106,0.12), transparent 60%), linear-gradient(180deg,#1a1c24,#0e0f13);
  border:1px solid #2e2f3a;display:grid;place-items:center;
}
.hero-sprite .sigil{font-size:54px;filter:drop-shadow(0 4px 12px rgba(0,0,0,0.6))}
.hpbar{height:8px;background:#0b0c0f;border-radius:999px;overflow:hidden;border:1px solid #2a2a2a;margin-top:10px}
.hpbar i{display:block;height:100%;background:linear-gradient(90deg,#c0392b,#e74c3c);transition:width 0.5s}
.energy-orbs{display:flex;gap:6px;margin-top:10px}
.orb{width:18px;height:18px;border-radius:50%;border:1px solid #3a4a6a;background:#0f1420;position:relative;overflow:hidden}
.orb.filled{background:radial-gradient(circle at 30% 30%, #8ecaff, #2a7bd6);border-color:#5dade2;box-shadow:0 0 8px rgba(93,173,226,0.6)}
.orb::after{content:"";position:absolute;inset:2px;border-radius:50%;background:radial-gradient(circle at 30% 30%, rgba(255,255,255,0.6), transparent 60%);opacity:0.5}
.stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:12px}
.mini-stat{padding:8px 10px;background:#0f1015;border:1px solid #232531;border-radius:10px;text-align:center}
.mini-stat b{font-family:'JetBrains Mono';font-size:14px;color:var(--gold2)}
.mini-stat span{font-size:9px;letter-spacing:0.15em;color:#888;display:block;margin-top:2px}

/* HAND */
.hand{display:flex;flex-direction:column;gap:10px;padding:12px;max-height:560px;overflow:auto}
.card{
  position:relative;background:#0d0e12;border-radius:12px;overflow:hidden;cursor:pointer;
  border:1px solid #2a2c36;transition:transform 0.15s, box-shadow 0.15s, border-color 0.15s;
  min-height:136px;display:flex;flex-direction:column;
}
.card:hover{transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,0.5)}
.card.selected{border-color:var(--gold);box-shadow:0 0 0 1px var(--gold), 0 10px 30px rgba(201,168,106,0.25);transform:translateY(-3px) scale(1.01)}
.card.disabled{opacity:0.45;filter:grayscale(0.4);pointer-events:none}
.card-top{display:flex;align-items:center;justify-content:space-between;padding:8px 10px 6px}
.card-cost{
  width:26px;height:26px;border-radius:50%;display:grid;place-items:center;
  font-family:'Cinzel',serif;font-weight:900;font-size:12px;color:#fff;
  border:1px solid rgba(255,255,255,0.2);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2)
}
.cost-attack{background:radial-gradient(circle at 30% 30%, #ff6b4a, #8b1a1a)}
.cost-defense{background:radial-gradient(circle at 30% 30%, #6bb8ff, #1a3a6b)}
.cost-other{background:radial-gradient(circle at 30% 30%, #b07cff, #3a1a6b)}
.card-id{font-family:'JetBrains Mono';font-size:9px;letter-spacing:0.12em;color:#888}
.card-type{font-family:'Cinzel';font-size:8px;letter-spacing:0.2em;padding:2px 6px;border-radius:999px;border:1px solid #333;color:#aaa}
.type-Attack{color:#ff8a6b;border-color:rgba(255,100,60,0.3);background:rgba(255,60,30,0.08)}
.type-Defense{color:#7eb8ff;border-color:rgba(80,150,255,0.3);background:rgba(60,120,255,0.08)}
.type-Other{color:#c79bff;border-color:rgba(160,90,255,0.3);background:rgba(160,90,255,0.08)}
.card-art{
  margin:0 8px;height:66px;border-radius:8px;overflow:hidden;position:relative;
  border:1px solid #2a2c36;background:#0a0a0f;display:grid;place-items:center;
}
.card-art svg{width:100%;height:100%}
.card-name{padding:6px 10px 0;font-family:'Cinzel',serif;font-size:11px;letter-spacing:0.08em;color:#f0e6d3;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.card-desc{padding:2px 10px 8px;font-size:11px;line-height:1.35;color:#b8b0a0;min-height:28px}
.card-stats{display:flex;gap:6px;padding:0 10px 8px}
.pill{font-family:'JetBrains Mono';font-size:10px;padding:2px 6px;border-radius:999px;border:1px solid #2a2c36;background:#14151c}
.pill.atk{color:#ff8a6b;border-color:rgba(255,100,60,0.35)}
.pill.def{color:#7eb8ff;border-color:rgba(80,150,255,0.35)}
.card-frame-glow{position:absolute;inset:0;pointer-events:none;opacity:0.0;transition:opacity 0.2s}
.card:hover .card-frame-glow{opacity:1}
.card.Bastion{border-top:2px solid #8a8078}
.card.Wither{border-top:2px solid #7a9a3a}
.card.Shroud{border-top:2px solid #8b6fb0}

/* Center battlefield */
.battlefield{padding:14px;display:flex;flex-direction:column;gap:12px}
.room-indicator{display:flex;gap:6px;align-items:center;justify-content:center}
.room-dot{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;font-family:'JetBrains Mono';font-size:10px;border:1px solid #2e2f3a;background:#14151c;color:#666}
.room-dot.active{background:linear-gradient(180deg, var(--gold), #8a6a2e);color:#111;border-color:var(--gold);box-shadow:0 0 10px rgba(201,168,106,0.5)}
.room-dot.cleared{background:#1a2a1a;border-color:#2a5a2a;color:#5fbf5f}
.room-dot.boss{border-radius:6px;transform:rotate(45deg);width:26px;height:26px}
.room-dot.boss span{transform:rotate(-45deg)}
.enemy-stage{
  height:300px;border-radius:14px;overflow:hidden;position:relative;
  background:
    radial-gradient(ellipse at 50% 80%, rgba(200,40,40,0.15), transparent 60%),
    radial-gradient(ellipse at 50% 20%, rgba(80,40,120,0.12), transparent 50%),
    linear-gradient(180deg,#1b1d26 0%, #0f1117 100%);
  border:1px solid #2a2c36;display:grid;place-items:center;
}
.enemy-stage::before{
  content:"";position:absolute;inset:0;
  background:repeating-linear-gradient(90deg, transparent 0 40px, rgba(255,255,255,0.02) 40px 41px);
  pointer-events:none;
}
.enemy-figure{position:relative;z-index:1;text-align:center;transition:transform 0.15s}
.enemy-figure.hit{animation:hitPulse 0.35s}
@keyframes hitPulse{0%{transform:scale(1)}30%{transform:scale(0.96) translateY(4px)}100%{transform:scale(1)}}
.enemy-sigil{font-size:82px;filter:drop-shadow(0 12px 20px rgba(0,0,0,0.7))}
.enemy-name{font-family:'Cinzel',serif;letter-spacing:0.18em;font-size:12px;color:var(--gold2);margin-top:6px}
.enemy-flavor{font-size:11px;color:#8a8a9a;max-width:300px;margin:4px auto 0;line-height:1.4}
.enemy-hp{width:320px;max-width:85%;margin:0 auto}
.enemy-hp-track{height:14px;background:#0a0a0f;border:1px solid #2a2c36;border-radius:999px;overflow:hidden;position:relative}
.enemy-hp-fill{height:100%;background:linear-gradient(90deg,#7a1a1a,#e74c3c);transition:width 0.5s}
.enemy-hp-text{position:absolute;inset:0;display:grid;place-items:center;font-family:'JetBrains Mono';font-size:10px;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,0.8)}
.enemy-badges{display:flex;gap:6px;justify-content:center;flex-wrap:wrap;margin-top:8px}
.badge-status{font-family:'JetBrains Mono';font-size:10px;padding:4px 8px;border-radius:999px;border:1px solid #333}
.badge-status.burn{background:rgba(255,180,0,0.12);border-color:rgba(255,180,0,0.3);color:#ffcc66}
.badge-status.poison{background:rgba(80,200,120,0.12);border-color:rgba(80,200,120,0.3);color:#7CFF9B}
.badge-status.stun{background:rgba(120,120,255,0.12);border-color:rgba(120,120,255,0.3);color:#aab4ff}
.badge-status.block{background:rgba(100,170,255,0.12);border-color:rgba(100,170,255,0.3);color:#8ec5ff}
.log{
  background:#0d0e12;border:1px solid #232531;border-radius:10px;padding:10px;max-height:140px;overflow:auto;
  font-family:'JetBrains Mono';font-size:11px;line-height:1.6;color:#b8b8c0
}
.log b{color:var(--gold2)}
.actions{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.btn{
  padding:11px 14px;border-radius:10px;border:1px solid #2e2f3a;background:linear-gradient(180deg,#1e1f26,#15161c);
  color:#e8e0d0;font-family:'Cinzel',serif;letter-spacing:0.12em;font-weight:700;font-size:11px;cursor:pointer;
  position:relative;overflow:hidden;transition:transform 0.1s, border-color 0.15s, box-shadow 0.15s;
}
.btn:hover{border-color:#3a3c4a;transform:translateY(-1px)}
.btn:active{transform:translateY(0)}
.btn.primary{background:linear-gradient(180deg,#c9a86a,#8a6a2e);color:#111;border-color:#e8c98a;box-shadow:0 6px 16px rgba(201,168,106,0.25)}
.btn.primary:disabled{opacity:0.45;filter:grayscale(0.5);cursor:not-allowed;transform:none;box-shadow:none}
.btn.ghost{background:transparent}
.btn.small{padding:8px 10px;font-size:10px}
/* Right panel */
.shop-grid{display:grid;grid-template-columns:1fr;gap:10px;padding:12px;max-height:520px;overflow:auto}
.shop-card{opacity:1}
.price{font-family:'JetBrains Mono';font-size:10px;color:var(--gold)}
.deck-list{padding:10px;display:flex;flex-wrap:wrap;gap:6px;max-height:160px;overflow:auto}
.deck-chip{font-family:'JetBrains Mono';font-size:9px;padding:4px 7px;border-radius:999px;border:1px solid #2a2c36;background:#14151c;color:#aaa}
.shop-filters{display:flex;gap:6px;padding:0 12px 8px;flex-wrap:wrap}
.filter{font-family:'JetBrains Mono';font-size:10px;padding:5px 9px;border-radius:999px;border:1px solid #2a2c36;background:#0f1015;color:#888;cursor:pointer}
.filter.active{background:var(--gold);color:#111;border-color:var(--gold)}
/* Overlay */
.overlay{position:fixed;inset:0;background:rgba(5,6,10,0.72);backdrop-filter:blur(6px);display:none;place-items:center;z-index:50;padding:16px}
.overlay.open{display:grid}
.modal{
  width:min(980px, 100%);max-height:90vh;overflow:auto;
  background:linear-gradient(180deg,#17181e,#0f1014);border:1px solid #2e2f3a;border-radius:16px;
  box-shadow:0 20px 60px rgba(0,0,0,0.6);
}
.modal-head{position:sticky;top:0;z-index:1;display:flex;align-items:center;justify-content:space-between;padding:14px 16px;background:linear-gradient(180deg,#1b1c24,#13141b);border-bottom:1px solid #252631}
.modal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;padding:14px}
.class-card{
  border:1px solid #2a2c36;border-radius:14px;overflow:hidden;background:linear-gradient(180deg,#1a1c24,#0f1117);cursor:pointer;
  transition:transform 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.class-card:hover{transform:translateY(-3px);border-color:var(--gold);box-shadow:0 12px 30px rgba(0,0,0,0.5)}
.class-card.selected{border-color:var(--gold);box-shadow:0 0 0 1px var(--gold)}
.class-art{height:120px;display:grid;place-items:center;font-size:52px;background:radial-gradient(ellipse at 50% 30%, rgba(201,168,106,0.15), transparent 60%)}
.class-body{padding:12px}
.class-body h4{font-family:'Cinzel',serif;font-size:13px;letter-spacing:0.12em;color:var(--gold2)}
.class-body p{font-size:11px;color:#9a9aac;line-height:1.5;margin-top:6px;min-height:44px}
.class-stats{display:flex;gap:6px;margin-top:10px;flex-wrap:wrap}
.kbd{font-family:'JetBrains Mono';font-size:10px;padding:2px 6px;border:1px solid #333;border-bottom-width:2px;border-radius:6px;background:#0f1015;color:#aaa}
.floating{position:fixed;pointer-events:none;z-index:60;font-family:'Cinzel',serif;font-weight:900;font-size:18px;text-shadow:0 2px 8px rgba(0,0,0,0.8);animation:floatUp 900ms ease forwards}
@keyframes floatUp{0%{transform:translateY(0) scale(1);opacity:1}100%{transform:translateY(-48px) scale(1.05);opacity:0}}
.toast{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);background:#11131a;border:1px solid var(--gold);color:var(--gold2);padding:10px 14px;border-radius:999px;font-family:'JetBrains Mono';font-size:12px;box-shadow:0 10px 30px rgba(0,0,0,0.5);z-index:70;display:none}
.toast.show{display:block;animation:pop 0.2s ease}
@keyframes pop{from{transform:translateX(-50%) scale(0.96)}to{transform:translateX(-50%) scale(1)}}
</style>
</head>
<body>
<div class="bg-void"></div><div class="bg-grain"></div>
<div class="topbar">
  <div class="brand">
    <div class="brand-mark"><span>◈</span></div>
    <div>
      <h1>SHADOWS OF AETHELGARD</h1>
      <p>VOID ASCENDANT — FLOOR <span id="floorLabel">1</span> / 3</p>
    </div>
  </div>
  <div class="hud-stats">
    <div class="stat gold"><span class="ico">◉</span><span id="goldLabel">0</span> <span style="opacity:0.6;font-size:10px">GOLD</span></div>
    <div class="stat hp"><span class="ico">♥</span><span id="hpLabel">35</span><span style="opacity:0.6">/</span><span id="maxHpLabel">35</span></div>
    <div class="stat energy"><span class="ico">✦</span><span id="energyLabel">3</span><span style="opacity:0.6">/</span><span id="maxEnergyLabel">5</span></div>
    <div class="stat floor"><span class="ico">⬢</span><span id="roomLabel">Room 1-1</span></div>
  </div>
</div>
<div class="progress-track"><div id="progressFill" class="progress-fill" style="width:8%"></div></div>

<div class="layout">
  <!-- LEFT -->
  <div class="panel" style="display:flex;flex-direction:column;min-height:620px">
    <div class="panel-head"><h3>YOUR HAND</h3><span class="badge" id="handCount">0 / 4</span></div>
    <div class="hero-card">
      <div class="hero-sprite" id="heroSprite"><span class="sigil" id="heroSigil">🛡️</span></div>
      <div style="display:flex;align-items:center;justify-content:space-between;margin-top:10px">
        <div><div class="mono" id="heroName" style="font-size:12px;letter-spacing:0.12em;color:var(--gold2)">THE BASTION</div><div id="heroSub" style="font-size:11px;color:#8a8a9a">Immovable Object • Stone Will</div></div>
        <div style="text-align:right"><div class="mono" style="font-size:10px;color:#888">DEFENSE</div><div id="defLabel" style="font-family:'Cinzel';font-weight:900;color:#7eb8ff">0</div></div>
      </div>
      <div class="hpbar"><i id="hpBar" style="width:100%"></i></div>
      <div class="energy-orbs" id="energyOrbs"></div>
      <div class="stats-grid">
        <div class="mini-stat"><b id="deckSizeLabel">12</b><span>DECK SIZE</span></div>
        <div class="mini-stat"><b id="blockLabel">0</b><span>BLOCK</span></div>
      </div>
    </div>
    <div id="hand" class="hand"></div>
    <div style="padding:10px;display:grid;grid-template-columns:1fr 1fr;gap:8px">
      <button class="btn ghost small" id="btnDraw">DRAW +1 (1♦)</button>
      <button class="btn ghost small" id="btnViewDeck">VIEW DECK</button>
    </div>
  </div>

  <!-- CENTER -->
  <div class="panel battlefield">
    <div class="room-indicator" id="roomIndicator"></div>
    <div class="enemy-stage" id="enemyStage">
      <div class="enemy-figure" id="enemyFigure">
        <div class="enemy-sigil" id="enemySigil">👹</div>
        <div class="enemy-name" id="enemyName">GOBLIN SCOUT</div>
        <div class="enemy-flavor" id="enemyFlavor">Floor 1 • Skitters in the dusk. 20 HP / 3 DMG.</div>
        <div class="enemy-hp" style="margin-top:10px">
          <div class="enemy-hp-track"><div id="enemyHpFill" class="enemy-hp-fill" style="width:100%"></div><div id="enemyHpText" class="enemy-hp-text">20 / 20</div></div>
          <div class="enemy-badges" id="enemyBadges"></div>
        </div>
      </div>
    </div>

    <div style="display:flex;gap:8px;align-items:center;justify-content:space-between">
      <div><h3 style="font-family:'Cinzel';font-size:11px;letter-spacing:0.16em;color:var(--gold)">COMBAT LOG</h3><p style="font-size:11px;color:#888">Card → Enemy → End Turn → Recover 1 ♦</p></div>
      <span class="badge" id="turnLabel">TURN 1</span>
    </div>
    <div id="combatLog" class="log">The Void stirs. Choose your Bastion and descend...<br></div>
    <div class="actions">
      <button class="btn primary" id="btnEndTurn">END TURN ↻</button>
      <button class="btn" id="btnShop">OPEN SHOP — 1✦ CARDS</button>
    </div>
    <div style="display:flex;gap:8px">
      <button class="btn ghost small" id="btnDiscard">DISCARD HAND</button>
      <button class="btn ghost small" id="btnRestart">RESTART FLOOR</button>
    </div>
  </div>

  <!-- RIGHT -->
  <div class="panel" style="min-height:620px;display:flex;flex-direction:column">
    <div class="panel-head"><h3>SHOP & DECK</h3><span class="badge" id="shopBadge">FLOOR 1</span></div>
    <div class="shop-filters">
      <button class="filter active" data-filter="all">All</button>
      <button class="filter" data-filter="Attack">Attack</button>
      <button class="filter" data-filter="Defense">Defense</button>
      <button class="filter" data-filter="Other">Other</button>
    </div>
    <div id="shopGrid" class="shop-grid"></div>
    <div class="panel-head" style="margin-top:6px"><h3>COLLECTION</h3><span class="badge" id="deckCount">0</span></div>
    <div id="deckList" class="deck-list"></div>
    <div style="padding:10px"><button class="btn ghost small" style="width:100%" id="btnClass">CHANGE CLASS</button></div>
  </div>
</div>

<!-- CLASS / SHOP OVERLAY -->
<div id="overlayClass" class="overlay open">
  <div class="modal">
    <div class="modal-head">
      <div>
        <h3 style="font-family:'Cinzel Decorative';letter-spacing:0.12em;color:var(--gold2)">CHOOSE YOUR VESSEL</h3>
        <p style="font-size:11px;color:#9a9aac;margin-top:4px">Three paths through the Void. Each bears a unique sigil, card frame, and back.</p>
      </div>
      <button class="btn small" id="btnCloseClass" style="display:none">CONTINUE</button>
    </div>
    <div class="modal-grid" id="classGrid"></div>
    <div style="padding:0 14px 14px;display:grid;grid-template-columns:1fr 1fr;gap:12px">
      <div style="padding:10px;background:#0d0e12;border:1px solid #232531;border-radius:10px">
        <div style="font-family:'Cinzel';font-size:10px;letter-spacing:0.14em;color:var(--gold)">CARD BACKS PREVIEW</div>
        <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:8px" id="backPreview"></div>
      </div>
      <div style="padding:10px;background:#0d0e12;border:1px solid #232531;border-radius:10px">
        <div style="font-family:'Cinzel';font-size:10px;letter-spacing:0.14em;color:var(--gold)">HOW TO PLAY</div>
        <ul style="font-size:11px;color:#b0b0c0;line-height:1.6;margin-top:6px;padding-left:16px">
          <li>Play cards if <b style="color:#7eb8ff">♦ ≥ Cost</b>. Damage = Attack. Defense blocks.</li>
          <li>End Turn → Enemy attacks → Recover 1 ♦. Burn/Poison ticks each turn.</li>
          <li>Kill → +5 Gold (Boss +15). Buy cards for 1 Gold (4th copy → 10 Gold).</li>
          <li>Clear 3 Floors (12 combats). Survive the Void Lord.</li>
        </ul>
      </div>
    </div>
  </div>
</div>

<div id="overlayShop" class="overlay">
  <div class="modal" style="width:min(1100px,100%)">
    <div class="modal-head">
      <div><h3 style="font-family:'Cinzel';letter-spacing:0.14em;color:var(--gold2)">AETHELGARD BAZAAR</h3><p style="font-size:11px;color:#888">Every card may be bought multiple times. Duplicate cost scales after 3 copies.</p></div>
      <button class="btn small" id="btnCloseShop">CLOSE ✕</button>
    </div>
    <div id="shopModalGrid" class="modal-grid"></div>
  </div>
</div>

<div id="overlayDeck" class="overlay">
  <div class="modal">
    <div class="modal-head"><h3 style="font-family:'Cinzel';letter-spacing:0.14em;color:var(--gold2)">YOUR DECK</h3><button class="btn small" id="btnCloseDeck">CLOSE ✕</button></div>
    <div id="deckModalGrid" class="modal-grid"></div>
  </div>
</div>

<div id="toast" class="toast"></div>

<script>
const CARD_DB = {
  // Bastion
  "BA-A01":{id:"BA-A01",cls:"Bastion",name:"Slam",type:"Attack",cost:2,atk:3,def:0,desc:"Deals 3 damage. If you have full HP, draw 1.",color:"#8a8078",icon:"🔨"},
  "BA-A02":{id:"BA-A02",cls:"Bastion",name:"Earthquake",type:"Attack",cost:3,atk:4,def:0,desc:"Deals 4 to ALL enemies.",aoe:true,icon:"⛰️"},
  "BA-A03":{id:"BA-A03",cls:"Bastion",name:"Stone Fist",type:"Attack",cost:1,atk:2,def:0,desc:"Deals 2 damage.",icon:"🪨"},
  "BA-A04":{id:"BA-A04",cls:"Bastion",name:"Boulder",type:"Attack",cost:4,atk:6,def:0,desc:"Deals 6. Heavy cost, heavy ruin.",icon:"🗿"},
  "BA-D01":{id:"BA-D01",cls:"Bastion",name:"Shield Wall",type:"Defense",cost:0,atk:0,def:4,desc:"Gain 4 Block this turn.",temp:true,icon:"🛡️"},
  "BA-D02":{id:"BA-D02",cls:"Bastion",name:"Iron Skin",type:"Defense",cost:2,atk:0,def:5,desc:"Gain 5 Block permanently. Discard 1.",perm:true,icon:"⚙️"},
  "BA-D03":{id:"BA-D03",cls:"Bastion",name:"Stand Firm",type:"Defense",cost:1,atk:0,def:2,desc:"Gain 2 Block. Next hit -1.",icon:"🏔️"},
  "BA-D04":{id:"BA-D04",cls:"Bastion",name:"Taunt",type:"Defense",cost:2,atk:0,def:3,desc:"Gain 3 Block. Stun 1 enemy 1 turn.",stun:1,icon:"📢"},
  "BA-U01":{id:"BA-U01",cls:"Bastion",name:"Search",type:"Other",cost:2,atk:0,def:0,desc:"Draw 1. If hand >5, discard excess.",draw:1,icon:"🔍"},
  "BA-U02":{id:"BA-U02",cls:"Bastion",name:"Repair",type:"Other",cost:3,atk:0,def:0,desc:"Heal 6 HP.",heal:6,icon:"🔧"},
  "BA-U03":{id:"BA-U03",cls:"Bastion",name:"Rally",type:"Other",cost:1,atk:0,def:0,desc:"Draw 1. Gain 1 ♦.",draw:1,energy:1,icon:"⚑"},
  "BA-U04":{id:"BA-U04",cls:"Bastion",name:"Loot",type:"Other",cost:2,atk:0,def:0,desc:"Gain 5 Gold.",gold:5,icon:"💰"},
  // Wither
  "WI-A01":{id:"WI-A01",cls:"Wither",name:"Fireball",type:"Attack",cost:2,atk:3,def:0,desc:"Deals 3. Apply 1 Burn.",burn:1,icon:"🔥"},
  "WI-A02":{id:"WI-A02",cls:"Wither",name:"Inferno",type:"Attack",cost:3,atk:4,def:0,desc:"Deals 4. Apply 3 Burn.",burn:3,icon:"🌋"},
  "WI-A03":{id:"WI-A03",cls:"Wither",name:"Ember",type:"Attack",cost:1,atk:2,def:0,desc:"Deals 2. Burn 1/turn.",burn:1,icon:"✴️"},
  "WI-A04":{id:"WI-A04",cls:"Wither",name:"Pyre",type:"Attack",cost:4,atk:5,def:0,desc:"Deals 5. All enemies +1 next turn.",aoeBurn:1,icon:"🕯️"},
  "WI-D01":{id:"WI-D01",cls:"Wither",name:"Scorch Shield",type:"Defense",cost:2,atk:0,def:3,desc:"Gain 3 Block. Block Burn.",icon:"🔰"},
  "WI-D02":{id:"WI-D02",cls:"Wither",name:"Heat Resist",type:"Defense",cost:1,atk:0,def:1,desc:"Gain 1 Block. Gain 1 ♦.",energy:1,icon:"♨️"},
  "WI-D03":{id:"WI-D03",cls:"Wither",name:"Smoke",type:"Defense",cost:2,atk:0,def:4,desc:"Gain 4 Block.",icon:"🌫️"},
  "WI-D04":{id:"WI-D04",cls:"Wither",name:"Endure",type:"Defense",cost:3,atk:0,def:2,desc:"Gain 2 Block. Heal 2.",heal:2,icon:"🌿"},
  "WI-U01":{id:"WI-U01",cls:"Wither",name:"Poison",type:"Other",cost:2,atk:0,def:0,desc:"Apply 1 Poison.",poison:1,icon:"☠️"},
  "WI-U02":{id:"WI-U02",cls:"Wither",name:"Burn",type:"Other",cost:3,atk:0,def:0,desc:"Deal 3 + 2 Burn to all.",atkAll:3,burnAll:2,icon:"♨️"},
  "WI-U03":{id:"WI-U03",cls:"Wither",name:"Slow",type:"Other",cost:1,atk:0,def:0,desc:"Draw 1. Stun target.",draw:1,stun:1,icon:"🕸️"},
  "WI-U04":{id:"WI-U04",cls:"Wither",name:"Wither",type:"Other",cost:2,atk:0,def:0,desc:"Heal 5. Enemy ATK -1.",heal:5,weaken:1,icon:"🍂"},
  // Shroud
  "SH-A01":{id:"SH-A01",cls:"Shroud",name:"Backstab",type:"Attack",cost:2,atk:4,def:0,desc:"Deals 4. +1 if no Block.",bonusUnblocked:1,icon:"🗡️"},
  "SH-A02":{id:"SH-A02",cls:"Shroud",name:"Shadow Bolt",type:"Attack",cost:3,atk:5,def:0,desc:"Deals 5. Cannot be blocked.",unblockable:true,icon:"🌑"},
  "SH-A03":{id:"SH-A03",cls:"Shroud",name:"Silence",type:"Attack",cost:1,atk:3,def:0,desc:"Deals 3. Silence (enemy next ATK -3).",silence:1,icon:"🤫"},
  "SH-A04":{id:"SH-A04",cls:"Shroud",name:"Vengeance",type:"Attack",cost:3,atk:6,def:0,desc:"Deals 6. Gain 2 ♦.",energy:2,icon:"⚔️"},
  "SH-D01":{id:"SH-D01",cls:"Shroud",name:"Blur",type:"Defense",cost:3,atk:0,def:2,desc:"Gain 2 Block. Ignore next attack.",dodge:1,icon:"👁️"},
  "SH-D02":{id:"SH-D02",cls:"Shroud",name:"Step",type:"Defense",cost:2,atk:0,def:3,desc:"Gain 3 Block. Gain 1 ♦.",energy:1,icon:"👣"},
  "SH-D03":{id:"SH-D03",cls:"Shroud",name:"Evasion",type:"Defense",cost:1,atk:0,def:1,desc:"Gain 1 Block. Skip enemy action.",stun:1,icon:"💨"},
  "SH-D04":{id:"SH-D04",cls:"Shroud",name:"Phantom",type:"Defense",cost:2,atk:0,def:4,desc:"Gain 4 Block. Ghost form.",icon:"👻"},
  "SH-U01":{id:"SH-U01",cls:"Shroud",name:"Dash",type:"Other",cost:1,atk:0,def:0,desc:"Gain 1 ♦. Skip enemy attack.",energy:1,stun:1,icon:"⚡"},
  "SH-U02":{id:"SH-U02",cls:"Shroud",name:"Teleport",type:"Other",cost:2,atk:0,def:0,desc:"Deal 2. Draw 1.",atk:2,draw:1,icon:"🌀"},
  "SH-U03":{id:"SH-U03",cls:"Shroud",name:"Hide",type:"Other",cost:2,atk:0,def:0,desc:"Gain 1 Block. Discard 1 Draw 2.",block1:1,discardDraw:true,icon:"🎭"},
  "SH-U04":{id:"SH-U04",cls:"Shroud",name:"Assassinate",type:"Other",cost:3,atk:0,def:0,desc:"Deal 8 unblockable.",atk:8,unblockable:true,icon:"☠️"},
};

const CLASSES = {
  Bastion:{name:"The Bastion",subtitle:"Immovable Object",hp:35,energy:3,handSize:4,sigil:"🛡️",color:"#8a8078",desc:"High defense, AoE stone warlord. Forged to endure the Void gate.",starter:["BA-A01","BA-A03","BA-A03","BA-D01","BA-D01","BA-D03","BA-U03","BA-U04"]},
  Wither:{name:"The Wither",subtitle:"Rot and Burn",hp:28,energy:4,handSize:4,sigil:"🔥",color:"#7a9a3a",desc:"Burns, poisons, and withers foes. Fragile but relentless.",starter:["WI-A01","WI-A03","WI-A03","WI-D02","WI-D03","WI-U01","WI-U02","WI-U04"]},
  Shroud:{name:"The Shroud",subtitle:"Shadow and Speed",hp:26,energy:5,handSize:4,sigil:"🗡️",color:"#8b6fb0",desc:"Evasive assassin. Crits, silences, teleports through shadow.",starter:["SH-A01","SH-A03","SH-A03","SH-D02","SH-D03","SH-U01","SH-U02","SH-U04"]},
};

function svgCardFrame(cls, type){
  if(cls==="Bastion"){
    const glow = type==="Attack"?"#ff6b4a":type==="Defense"?"#6bb8ff":"#b07cff";
    return `<svg viewBox="0 0 160 92" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <defs><linearGradient id="gB" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#2a2d34"/><stop offset="100%" stop-color="#0f1115"/></linearGradient>
      <linearGradient id="sB" x1="0" y1="0" x2="1" y2="0"><stop offset="0%" stop-color="${glow}" stop-opacity="0.0"/><stop offset="50%" stop-color="${glow}" stop-opacity="0.45"/><stop offset="100%" stop-color="${glow}" stop-opacity="0.0"/></linearGradient></defs>
      <rect x="2" y="2" width="156" height="88" rx="8" fill="url(#gB)" stroke="#3a3d44" stroke-width="1.2"/>
      <!-- crenellations -->
      <path d="M10 10 H30 V6 H38 V10 H55 V6 H63 V10 H97 V6 H105 V10 H122 V6 H130 V10 H150" stroke="#4a4d55" stroke-width="1" fill="none" opacity="0.9"/>
      <rect x="10" y="18" width="140" height="54" rx="6" fill="#0a0b0f" stroke="#2a2d34"/>
      <!-- rivets -->
      <circle cx="14" cy="14" r="1.6" fill="#6a6d75"/><circle cx="146" cy="14" r="1.6" fill="#6a6d75"/><circle cx="14" cy="78" r="1.6" fill="#6a6d75"/><circle cx="146" cy="78" r="1.6" fill="#6a6d75"/>
      <rect x="20" y="82" width="120" height="2" rx="1" fill="url(#sB)"/>
      <!-- placeholder icon area -->
      <g opacity="0.14"><path d="M 62 28 L 80 48 L 98 28 L 80 64 Z" fill="none" stroke="#c9a86a" stroke-width="1.2"/><circle cx="80" cy="46" r="10" fill="none" stroke="#c9a86a" stroke-width="0.8"/></g>
    </svg>`;
  }
  if(cls==="Wither"){
    const glow = type==="Attack"?"#ff3b1f":type==="Defense"?"#6bff8a":"#ffb84a";
    return `<svg viewBox="0 0 160 92" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <defs><linearGradient id="gW" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#1a2210"/><stop offset="100%" stop-color="#0b0f07"/></linearGradient></defs>
      <rect x="2" y="2" width="156" height="88" rx="10" fill="url(#gW)" stroke="#3a4a1a" stroke-width="1.2"/>
      <!-- thorn vines -->
      <path d="M 6 16 C 20 10, 36 18, 52 12 S 84 6, 100 14 S 132 8, 154 16" stroke="#5a6b22" stroke-width="1.1" fill="none" opacity="0.7"/>
      <path d="M 6 76 C 22 82, 40 74, 60 80 S 96 86, 116 78 S 138 84, 154 76" stroke="#5a6b22" stroke-width="1.1" fill="none" opacity="0.7"/>
      <rect x="10" y="18" width="140" height="54" rx="8" fill="#0c0f0a" stroke="#2f3a14"/>
      <!-- ember dots -->
      <circle cx="22" cy="24" r="1.2" fill="${glow}" opacity="0.8"/><circle cx="138" cy="26" r="1" fill="${glow}" opacity="0.7"/><circle cx="80" cy="20" r="0.9" fill="${glow}" opacity="0.6"/>
      <g opacity="0.16"><path d="M80 26 C 70 38, 74 52, 80 62 C 86 52, 90 38, 80 26 Z" fill="none" stroke="#ffcc66" stroke-width="1"/><path d="M76 34 L80 42 L84 34" fill="none" stroke="#ffcc66" stroke-width="0.8"/></g>
    </svg>`;
  }
  // Shroud
  const glow = type==="Attack"?"#b07cff":type==="Defense"?"#7ec8ff":"#ff7acc";
  return `<svg viewBox="0 0 160 92" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
    <defs><linearGradient id="gS" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#1a1522"/><stop offset="100%" stop-color="#0d0b14"/></linearGradient></defs>
    <path d="M 12 6 L 148 6 L 156 14 L 156 78 L 148 86 L 12 86 L 4 78 L 4 14 Z" fill="url(#gS)" stroke="#3a2f4a" stroke-width="1.2"/>
    <!-- dagger cut -->
    <path d="M 4 46 H 14 M 146 46 H 156" stroke="${glow}" stroke-width="1" opacity="0.7"/>
    <rect x="10" y="18" width="140" height="54" rx="2" fill="#0a0810" stroke="#2a2040" transform="skewX(-1)"/>
    <!-- smoke wisps -->
    <path d="M 30 20 C 34 26, 28 32, 32 38" stroke="#6b5a8a" stroke-width="0.8" fill="none" opacity="0.4"/><path d="M 130 24 C 126 30, 132 36, 128 42" stroke="#6b5a8a" stroke-width="0.8" fill="none" opacity="0.4"/>
    <g opacity="0.15"><path d="M80 28 L 86 46 L 80 64 L 74 46 Z" fill="none" stroke="#c9a8ff" stroke-width="1"/><circle cx="80" cy="46" r="6" fill="none" stroke="#c9a8ff" stroke-width="0.7"/></g>
  </svg>`;
}
function svgCardBack(cls){
  if(cls==="Bastion"){
    return `<svg viewBox="0 0 160 112" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="bb" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#2a2e36"/><stop offset="100%" stop-color="#0e1014"/></linearGradient></defs>
    <rect width="160" height="112" rx="10" fill="url(#bb)" stroke="#4a4d55" stroke-width="1.2"/>
    <rect x="8" y="8" width="144" height="96" rx="8" fill="none" stroke="#c9a86a" stroke-width="1" opacity="0.5"/>
    <rect x="12" y="12" width="136" height="88" rx="6" fill="none" stroke="#3a3d44" stroke-dasharray="3 3" opacity="0.5"/>
    <g opacity="0.9"><rect x="64" y="18" width="32" height="32" rx="2" fill="none" stroke="#8a8078" stroke-width="1.2" transform="rotate(45 80 34)"/><text x="80" y="38" text-anchor="middle" font-family="Cinzel" font-size="18" fill="#c9a86a">◈</text></g>
    <text x="80" y="68" text-anchor="middle" font-family="Cinzel" font-size="7" letter-spacing="2" fill="#8a8078">BASTION</text>
    <text x="80" y="78" text-anchor="middle" font-family="JetBrains Mono" font-size="5" letter-spacing="2" fill="#5a5d66">AETHELGARD • STONE WILL</text>
    <circle cx="18" cy="18" r="2" fill="#6a6d75"/><circle cx="142" cy="18" r="2" fill="#6a6d75"/><circle cx="18" cy="94" r="2" fill="#6a6d75"/><circle cx="142" cy="94" r="2" fill="#6a6d75"/></svg>`;
  }
  if(cls==="Wither"){
    return `<svg viewBox="0 0 160 112" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="wb" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#1e2612"/><stop offset="100%" stop-color="#0a0e06"/></linearGradient></defs>
    <rect width="160" height="112" rx="12" fill="url(#wb)" stroke="#4a5a1e" stroke-width="1.2"/>
    <ellipse cx="80" cy="56" rx="58" ry="42" fill="none" stroke="#7a9a3a" stroke-width="1" opacity="0.35"/>
    <ellipse cx="80" cy="56" rx="46" ry="32" fill="none" stroke="#5a6b22" stroke-width="0.8" opacity="0.25"/>
    <g opacity="0.9"><text x="80" y="48" text-anchor="middle" font-size="26">🔥</text></g>
    <text x="80" y="70" text-anchor="middle" font-family="Cinzel" font-size="7" letter-spacing="2.5" fill="#a8c25a">WITHER</text>
    <text x="80" y="80" text-anchor="middle" font-family="JetBrains Mono" font-size="5" letter-spacing="1.5" fill="#6a7a3a">ROT • BURN • DECAY</text>
    <path d="M 12 28 C 26 18, 40 24, 52 18" stroke="#5a6b22" fill="none" opacity="0.6"/><path d="M 108 92 C 120 86, 134 90, 148 84" stroke="#5a6b22" fill="none" opacity="0.6"/></svg>`;
  }
  return `<svg viewBox="0 0 160 112" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="sb" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#1c1626"/><stop offset="100%" stop-color="#0a0812"/></linearGradient></defs>
    <path d="M 10 8 L 150 8 L 158 16 L 158 96 L 150 104 L 10 104 L 2 96 L 2 16 Z" fill="url(#sb)" stroke="#5b4a6b" stroke-width="1.2"/>
    <path d="M 2 56 H 10 M 150 56 H 158" stroke="#8b6fb0" opacity="0.6"/><circle cx="80" cy="56" r="28" fill="none" stroke="#3a2f4a" stroke-width="0.8" opacity="0.5"/>
    <text x="80" y="50" text-anchor="middle" font-size="24">🗡️</text><text x="80" cy="56" text-anchor="middle" font-size="10" opacity="0.2">✦</text>
    <text x="80" y="74" text-anchor="middle" font-family="Cinzel" font-size="7" letter-spacing="2.5" fill="#b99bff">SHROUD</text>
    <text x="80" y="84" text-anchor="middle" font-family="JetBrains Mono" font-size="5" letter-spacing="1.8" fill="#6b5a8a">SHADOW • SPEED • SILENCE</text></svg>`;
}

// GAME STATE
let state = {
  cls:"Bastion",
  hp:35,maxHp:35,energy:3,maxEnergy:5,handSize:4,gold:0,
  deck:[], drawPile:[], discard:[], hand:[],
  counts:{}, // cardId->count
  defense:0,permDefense:0,tempBlock:0,dodge:0,
  floor:1,room:1,turn:1,
  enemy:null,
  filter:"all",
  won:false
};

function resetForClass(cls){
  const c = CLASSES[cls];
  state.cls=cls;
  state.maxHp=c.hp; state.hp=c.hp;
  state.maxEnergy=5; state.energy=c.energy;
  state.handSize=c.handSize;
  state.gold=0;
  state.deck=[...c.starter];
  state.counts={}; state.deck.forEach(id=>state.counts[id]=(state.counts[id]||0)+1);
  state.drawPile = shuffle([...state.deck]);
  state.discard=[]; state.hand=[]; state.defense=0; state.permDefense=0; state.tempBlock=0; state.dodge=0;
  state.floor=1; state.room=1; state.turn=1;
  spawnEnemy();
  drawUpToHand();
  renderAll();
}

function shuffle(a){ for(let i=a.length-1;i>0;i--){ const j=Math.floor(Math.random()*(i+1)); [a[i],a[j]]=[a[j],a[i]] } return a; }

function getScalingPrice(id){
  const cnt = state.counts[id]||0;
  if(cnt<3) return 1;
  if(cnt===3) return 10;
  return Math.min(50, 10 + (cnt-3)*5);
}
function getEnemyFor(floor, isBoss){
  if(isBoss){
    if(floor===1) return {name:"THE GATEKEEPER",sigil:"🗿",hp:80,maxHp:80,dmg:15,def:5,flavor:"Blocks 5 damage/turn until HP <20. The first seal.",block:5};
    if(floor===2) return {name:"THE CORRUPTED",sigil:"🧛",hp:80,maxHp:80,dmg:20,flavor:"Deals 20 if you have <3 ♦. Hunger made manifest.",corrupted:true};
    return {name:"THE VOID LORD",sigil:"👁️",hp:40,maxHp:40,dmg:12,flavor:"40 HP +10 regen/turn. Cannot be blocked. The end.",regen:10,unblockable:true};
  }
  if(floor===1) return {name:["GOBLIN SCOUT","RAT SWARM","VOIDLING"][Math.floor(Math.random()*3)],sigil:["👹","🐀","👾"][Math.floor(Math.random()*3)],hp:20,maxHp:20,dmg:3,flavor:"Floor 1 • 20 HP / 3 DMG"};
  if(floor===2) return {name:["VAMPIRE KNIGHT","OGRE BRUTE","CULTIST"][Math.floor(Math.random()*3)],sigil:["🧛","👺","🧙"][Math.floor(Math.random()*3)],hp:30,maxHp:30,dmg:5,flavor:"Floor 2 • 30 HP / 5 DMG"};
  return {name:["VOID WARRIOR","CULTIST LORD","ABYSSAL HORROR"][Math.floor(Math.random()*3)],sigil:["⚔️","🧙","👁️"][Math.floor(Math.random()*3)],hp:40,maxHp:40,dmg:8,flavor:"Floor 3 • 40 HP / 8 DMG"};
}
function spawnEnemy(){
  const isBoss = state.room===4;
  const e = getEnemyFor(state.floor,isBoss);
  e.burn=0; e.poison=0; e.stun=0; e.weaken=0;
  state.enemy=e; state.turn=1;
  log(`<b>Room ${state.floor}-${state.room}</b> — ${e.name} emerges. <i>${e.flavor}</i>`);
}
function drawUpToHand(){
  while(state.hand.length < state.handSize){
    if(state.drawPile.length===0){
      if(state.discard.length===0) break;
      state.drawPile = shuffle([...state.discard]); state.discard=[];
      log(`↻ Shuffled discard into draw pile.`);
    }
    const id = state.drawPile.pop();
    state.hand.push(id);
  }
}
function renderAll(){
  // top HUD
  document.getElementById('floorLabel').textContent=state.floor;
  document.getElementById('goldLabel').textContent=state.gold;
  document.getElementById('hpLabel').textContent=Math.max(0,state.hp);
  document.getElementById('maxHpLabel').textContent=state.maxHp;
  document.getElementById('energyLabel').textContent=state.energy;
  document.getElementById('maxEnergyLabel').textContent=state.maxEnergy;
  document.getElementById('roomLabel').textContent=`Room ${state.floor}-${state.room}`;
  document.getElementById('hpBar').style.width=`${Math.max(0,state.hp/state.maxHp*100)}%`;
  document.getElementById('defLabel').textContent=state.permDefense+state.tempBlock;
  document.getElementById('blockLabel').textContent=state.tempBlock + state.permDefense;
  document.getElementById('deckSizeLabel').textContent=state.deck.length;
  // energy orbs
  const orbs = document.getElementById('energyOrbs'); orbs.innerHTML="";
  for(let i=0;i<state.maxEnergy;i++){
    const d=document.createElement('div'); d.className='orb'+(i<state.energy?' filled':'');
    orbs.appendChild(d);
  }
  // hero
  const c = CLASSES[state.cls];
  document.getElementById('heroName').textContent=c.name.toUpperCase();
  document.getElementById('heroSub').textContent=c.subtitle+" • "+(state.cls==="Bastion"?"Stone Will":state.cls==="Wither"?"Rot & Burn":"Shadow Step");
  document.getElementById('heroSigil').textContent=c.sigil;
  document.getElementById('heroSprite').style.borderColor=c.color+"66";
  // hand
  document.getElementById('handCount').textContent=`${state.hand.length} / ${state.handSize}`;
  const handEl=document.getElementById('hand'); handEl.innerHTML="";
  state.hand.forEach((id, idx)=>{
    const card=CARD_DB[id];
    const canPlay = state.energy >= card.cost;
    const el=document.createElement('div'); el.className=`card ${card.cls} ${canPlay?'':'disabled'}`;
    el.innerHTML=`
      <div class="card-top">
        <div class="card-cost cost-${card.type.toLowerCase()}">${card.cost}</div>
        <span class="card-type type-${card.type}">${card.type.toUpperCase()}</span>
        <span class="card-id">${card.id}</span>
      </div>
      <div class="card-art">${svgCardFrame(card.cls,card.type)}</div>
      <div class="card-name">${card.icon} ${card.name}</div>
      <div class="card-desc">${card.desc}</div>
      <div class="card-stats">
        ${card.atk?`<span class="pill atk">ATK ${card.atk}</span>`:card.atkAll?`<span class="pill atk">ATK ${card.atkAll} ALL</span>`:card.atk?`<span class="pill atk">ATK ${card.atk}</span>`:''}
        ${card.def?`<span class="pill def">DEF ${card.def}</span>`:''}
        ${card.cost===0?`<span class="pill">FREE</span>`:''}
      </div>
      <div class="card-frame-glow" style="background:radial-gradient(ellipse at 50% 0%, ${c.color}22, transparent 70%)"></div>
    `;
    el.onclick=()=>playCard(idx);
    handEl.appendChild(el);
  });
  if(state.hand.length===0) handEl.innerHTML=`<div style="padding:18px;text-align:center;color:#666;font-size:12px;border:1px dashed #2a2c36;border-radius:10px">No cards in hand — End Turn to draw.</div>`;
  // enemy
  const e=state.enemy;
  document.getElementById('enemySigil').textContent=e.sigil;
  document.getElementById('enemyName').textContent=e.name;
  document.getElementById('enemyFlavor').textContent=e.flavor;
  document.getElementById('enemyHpFill').style.width=`${Math.max(0,e.hp/e.maxHp*100)}%`;
  document.getElementById('enemyHpText').textContent=`${Math.max(0,e.hp)} / ${e.maxHp}`;
  const badges=document.getElementById('enemyBadges'); badges.innerHTML="";
  if(e.def) badges.innerHTML+=`<span class="badge-status block">BLOCK ${e.def}</span>`;
  if(e.burn) badges.innerHTML+=`<span class="badge-status burn">BURN ${e.burn}</span>`;
  if(e.poison) badges.innerHTML+=`<span class="badge-status poison">POISON ${e.poison}</span>`;
  if(e.stun) badges.innerHTML+=`<span class="badge-status stun">STUNNED ${e.stun}t</span>`;
  if(e.weaken) badges.innerHTML+=`<span class="badge-status block">WEAK -${e.weaken}</span>`;
  if(state.dodge) badges.innerHTML+=`<span class="badge-status stun">DODGE</span>`;
  // room indicator
  const ri=document.getElementById('roomIndicator'); ri.innerHTML="";
  for(let f=1;f<=3;f++){
    for(let r=1;r<=4;r++){
      const dot=document.createElement('div'); dot.className='room-dot'+(r===4?' boss':'');
      if(f<state.floor || (f===state.floor && r<state.room)) dot.classList.add('cleared');
      if(f===state.floor && r===state.room) dot.classList.add('active');
      dot.innerHTML=`<span>${r===4?'♦':r}</span>`;
      dot.title=`Floor ${f} Room ${r}${r===4?' (Boss)':''}`;
      ri.appendChild(dot);
    }
    if(f<3){ const sep=document.createElement('span'); sep.style.color="#333"; sep.textContent="—"; ri.appendChild(sep); }
  }
  document.getElementById('turnLabel').textContent=`TURN ${state.turn}`;
  document.getElementById('progressFill').style.width=`${((state.floor-1)*4 + state.room)/12*100}%`;
  document.getElementById('deckCount').textContent=`${state.deck.length} cards`;
  renderShopMini(); renderDeckList();
}

function renderCardMini(id, opts={}){
  const card=CARD_DB[id]; const price=getScalingPrice(id); const own=state.counts[id]||0;
  const canAfford = state.gold >= price;
  const frame=svgCardFrame(card.cls,card.type);
  return `<div class="card ${card.cls} shop-card" style="min-height:148px">
    <div class="card-top"><div class="card-cost cost-${card.type.toLowerCase()}">${card.cost}</div><span class="card-type type-${card.type}">${card.type}</span><span class="card-id">${card.id}</span></div>
    <div class="card-art">${frame}</div>
    <div class="card-name">${card.icon} ${card.name}</div>
    <div class="card-desc">${card.desc}${own?` <span style="color:var(--gold)">×${own}/5</span>`:''}</div>
    <div style="display:flex;align-items:center;justify-content:space-between;padding:0 10px 8px">
      <span class="price">${price} Gold ${own>=5?'• MAX':own>=3?'• ↑':''}</span>
      <button class="btn small ${canAfford && own<5?'primary':''}" ${(!canAfford||own>=5)?'disabled':''} onclick="buyCard('${id}')">${own>=5?'OWNED':canAfford?'BUY':'NEED GOLD'}</button>
    </div>
  </div>`;
}
function renderShopMini(){
  const grid=document.getElementById('shopGrid'); const f=state.filter;
  let ids=Object.keys(CARD_DB);
  if(f!=="all") ids=ids.filter(id=>CARD_DB[id].type===f);
  // show limited preview (6)
  const preview = ids.slice(0,6);
  grid.innerHTML = preview.map(id=>renderCardMini(id)).join('') + `<button class="btn ghost small" style="grid-column:1/-1" onclick="openShop()">VIEW FULL BAZAAR — 36 CARDS →</button>`;
}
function renderDeckList(){
  const el=document.getElementById('deckList'); el.innerHTML="";
  const sorted=[...state.deck].sort();
  const map={}; sorted.forEach(id=>map[id]=(map[id]||0)+1);
  Object.entries(map).forEach(([id,c])=>{
    const chip=document.createElement('span'); chip.className='deck-chip'; chip.textContent=`${id} ×${c}`; el.appendChild(chip);
  });
  if(sorted.length===0) el.innerHTML=`<span style="color:#666;font-size:11px">No cards yet.</span>`;
}
function openShop(){
  const grid=document.getElementById('shopModalGrid'); const ids=Object.keys(CARD_DB);
  grid.innerHTML = ids.map(id=>renderCardMini(id,{modal:true})).join('');
  document.getElementById('overlayShop').classList.add('open');
}
function buyCard(id){
  const price=getScalingPrice(id); const own=state.counts[id]||0;
  if(own>=5) return toast("Max 5 copies per card.");
  if(state.gold < price) return toast("Not enough Gold.");
  state.gold -= price;
  state.deck.push(id); state.drawPile.push(id); // add to draw
  state.counts[id]=own+1;
  shuffle(state.drawPile);
  log(`<b>Bought ${CARD_DB[id].name}</b> for ${price} Gold.`);
  toast(`Added ${CARD_DB[id].name} to deck`);
  renderAll();
  // refresh modal if open
  if(document.getElementById('overlayShop').classList.contains('open')) openShop();
}
function log(msg){
  const el=document.getElementById('combatLog'); el.innerHTML+=`• ${msg}<br>`; el.scrollTop=el.scrollHeight;
}
function toast(msg){
  const t=document.getElementById('toast'); t.textContent=msg; t.classList.add('show'); setTimeout(()=>t.classList.remove('show'),1800);
}
function floatText(x,y,text,color){
  const d=document.createElement('div'); d.className='floating'; d.textContent=text; d.style.left=x+'px'; d.style.top=y+'px'; d.style.color=color;
  document.body.appendChild(d); setTimeout(()=>d.remove(),900);
}

function playCard(handIdx){
  const id=state.hand[handIdx]; const card=CARD_DB[id];
  if(state.energy < card.cost) return toast("Not enough Energy");
  // pay
  state.energy -= card.cost;
  // remove from hand to discard eventually
  state.hand.splice(handIdx,1);
  state.discard.push(id);
  // resolve
  const e=state.enemy; const rect=document.getElementById('enemyStage').getBoundingClientRect();
  // ATTACK
  let dmg=0;
  if(card.atk) dmg = card.atk;
  if(card.atkAll) dmg = card.atkAll;
  if(card.type==="Attack") dmg = card.atk || dmg;
  // special bonuses
  if(card.bonusUnblocked && (e.def===0 && state.tempBlock===0)) dmg += card.bonusUnblocked;
  if(card.type==="Attack" && dmg>0){
    let blocked = e.def||0;
    // boss1 block mechanic
    if(e.block && e.hp >=20) blocked = Math.max(blocked, e.block);
    let final = Math.max(0, dmg - blocked);
    if(e.hp<20 && e.block) final = dmg; // gatekeeper loses block
    if(card.unblockable) final = dmg;
    if(state.enemy) { e.hp -= final; }
    floatText(rect.left+rect.width/2, rect.top+90, `-${final}`, final>0?'#ff6b6b':'#888');
    log(`<b>${card.name}</b> deals <b>${final}</b> damage${blocked&&!card.unblockable?` (blocked ${Math.min(dmg,blocked)})`:''}.`);
    document.getElementById('enemyFigure').classList.add('hit'); setTimeout(()=>document.getElementById('enemyFigure').classList.remove('hit'),350);
  }
  if(card.aoe){
    // earthquake - already dealt to single, duplicate for flavor
    log(`Earthquake trembles the chamber.`);
  }
  if(card.atk===8){ // assassinate
    let final=8;
    e.hp -= final; floatText(rect.left+rect.width/2, rect.top+70, `-${final}`, '#ff4d6d'); log(`<b>Assassinate</b> pierces for 8 unblockable.`);
  }
  if(card.atk===2 && card.id==="SH-U02"){ e.hp -=2; floatText(rect.left+rect.width/2, rect.top+70, `-2`, '#c9a8ff'); log(`Teleport strikes for 2.`); }
  // burn/poison
  if(card.burn){ e.burn=(e.burn||0)+card.burn; log(`Applied ${card.burn} Burn.`); }
  if(card.burnAll){ e.burn=(e.burn||0)+card.burnAll; log(`All enemies burn ${card.burnAll}.`); }
  if(card.poison){ e.poison=(e.poison||0)+card.poison; log(`Applied ${card.poison} Poison.`);}
  if(card.aoeBurn){ e.burn=(e.burn||0)+card.aoeBurn; }
  // defense
  if(card.def){
    if(card.perm){ state.permDefense += card.def; log(`Gained ${card.def} <span style="color:#7eb8ff">permanent Block</span>.`); if(card.id==="BA-D02"){ // discard 1
      if(state.hand.length>0){ const disc=state.hand.pop(); state.discard.push(disc); log(`Discarded ${CARD_DB[disc].name}.`);}
    }}
    else { state.tempBlock += card.def; log(`Gained ${card.def} Block.`); }
  }
  if(card.block1){ state.tempBlock+=1; log(`Gained 1 Block.`); }
  if(card.stun){ e.stun=(e.stun||0)+card.stun; log(`Stunned ${e.name} for ${card.stun} turn.`); }
  if(card.energy){ state.energy = Math.min(state.maxEnergy, state.energy + card.energy); log(`Gained ${card.energy} ♦.`); }
  if(card.draw){
    // draw card
    drawUpToHand(); // will fill to hand size, but we want extra draw 1 beyond
    if(state.drawPile.length===0 && state.discard.length>0){ state.drawPile=shuffle([...state.discard]); state.discard=[]; }
    if(state.drawPile.length>0){ const nid=state.drawPile.pop(); state.hand.push(nid); log(`Drew ${CARD_DB[nid].name}.`); }
  }
  if(card.discardDraw){
    if(state.hand.length>0){ const disc=state.hand.pop(); state.discard.push(disc); }
    for(let i=0;i<2;i++){ if(state.drawPile.length===0 && state.discard.length>0){ state.drawPile=shuffle([...state.discard]); state.discard=[];} if(state.drawPile.length>0) state.hand.push(state.drawPile.pop()); }
    state.tempBlock+=1; log(`Hide: discarded 1, drew 2, +1 Block.`);
  }
  if(card.heal){ state.hp = Math.min(state.maxHp, state.hp + card.heal); log(`Healed ${card.heal} HP.`); floatText(rect.left+40, rect.top+40, `+${card.heal} HP`, '#5fbf5f'); }
  if(card.gold){ state.gold+=card.gold; log(`Gained ${card.gold} Gold.`); }
  if(card.weaken){ e.weaken=(e.weaken||0)+card.weaken; log(`Weakened enemy ATK -${card.weaken}.`); }
  if(card.silence){ e.weaken=(e.weaken||0)+3; log(`Silenced target.`); }
  if(card.dodge){ state.dodge=1; log(`Blur: next attack ignored.`); }
  // BA-A01 full HP draw
  if(card.id==="BA-A01" && state.hp===state.maxHp){
    if(state.drawPile.length===0 && state.discard.length>0){ state.drawPile=shuffle([...state.discard]); state.discard=[];}
    if(state.drawPile.length>0){ const nid=state.drawPile.pop(); state.hand.push(nid); log(`Slam: full HP → drew ${CARD_DB[nid].name}.`); }
  }
  if(card.id==="WI-U02"){ // burn all
    e.hp -=3; e.burn=(e.burn||0)+2; floatText(rect.left+rect.width/2, rect.top+80, `-3`, '#ff8a3d'); log(`Burn deals 3 +2 Burn to all.`);
  }
  // check enemy death immediate
  if(e.hp<=0){
    handleEnemyDeath();
    renderAll(); return;
  }
  renderAll();
}

function handleEnemyDeath(){
  const isBoss = state.room===4;
  const reward = isBoss?15:5;
  state.gold += reward;
  log(`<b style="color:#5fbf5f">✓ ${state.enemy.name} defeated! +${reward} Gold.</b>`);
  floatText(window.innerWidth/2, 180, `+${reward} GOLD`, '#f0d49a');
  // advance
  if(isBoss){
    if(state.floor===3){
      state.won=true;
      log(`<b style="color:var(--gold)">— VICTORY — Void Lord falls. Aethelgard is reclaimed.</b>`);
      setTimeout(()=>showVictory(),600);
      return;
    } else {
      state.floor++; state.room=1;
      toast(`Floor ${state.floor} — Deeper into the Void`);
    }
  } else {
    state.room++;
  }
  // heal small between? not per GDD, but keep defense? reset temp block
  state.tempBlock=0; state.dodge=0;
  spawnEnemy();
  // draw for new room
  drawUpToHand();
}

function enemyTurn(){
  const e=state.enemy;
  if(!e) return;
  // status tick before attack
  if(e.burn){ const dmg=e.burn; e.hp-=dmg; log(`Burn ticks for ${dmg}.`); floatText( document.getElementById('enemyStage').getBoundingClientRect().left+120, 200, `-${dmg} burn`, '#ffcc66'); e.burn=Math.max(0,e.burn-1); if(e.hp<=0){ handleEnemyDeath(); renderAll(); return; } }
  if(e.poison){ const dmg=e.poison; e.hp-=dmg; log(`Poison ticks for ${dmg}.`); e.poison=Math.max(0,e.poison-1); if(e.hp<=0){ handleEnemyDeath(); renderAll(); return; } }
  // boss regen
  if(e.regen){ e.hp = Math.min(e.maxHp, e.hp + e.regen); log(`Void Lord regenerates +${e.regen} HP.`); }
  if(e.stun && e.stun>0){ e.stun--; log(`${e.name} is stunned — no attack.`); return; }
  if(state.dodge){ state.dodge=0; log(`Dodged the attack!`); return; }
  // calculate damage
  let dmg = e.dmg;
  if(e.corrupted && state.energy <3) dmg=20;
  if(e.weaken) dmg = Math.max(0, dmg - e.weaken);
  if(e.name==="THE GATEKEEPER" && e.hp<20) dmg=12; // slightly less when broken? keep 15? leave
  const block = state.tempBlock + state.permDefense;
  const final = Math.max(0, dmg - block);
  // block absorbs
  if(state.tempBlock>0){
    const absorb=Math.min(state.tempBlock, dmg);
    state.tempBlock -= absorb;
    dmg-=absorb;
  }
  if(final>0){
    state.hp -= final;
    floatText(80, 180, `-${final}`, '#ff4d4d');
    document.getElementById('heroSprite').animate([{transform:'translateX(0)'},{transform:'translateX(6px)'},{transform:'translateX(0)'}],{duration:250});
    log(`<b>${e.name}</b> hits for ${final} ${block?`(blocked ${block-final+ (dmg?0:0)})`:''}.`);
  } else {
    log(`<b>${e.name}</b> attacks for ${e.dmg} — fully blocked!`);
  }
  if(state.hp<=0){ log(`<b style="color:#ff4d4d">☠ You have fallen. The Void consumes all.</b>`); setTimeout(()=>showDefeat(),400); }
}

function endTurn(){
  if(state.won || state.hp<=0) return;
  enemyTurn();
  if(state.hp<=0 || state.won){ renderAll(); return; }
  // discard down? For now keep hand, but discard excess? GDD says discard down to max if specified -> we keep 4
  // move hand to discard? Actually we keep hand, draw up to hand size
  // temp block decays? keep perm, clear temp? According spec: Shield Wall is for this turn only. So clear temp after enemy turn.
  state.tempBlock=0;
  state.energy = Math.min(state.maxEnergy, state.energy+1);
  state.turn++;
  drawUpToHand();
  log(`— Turn ${state.turn} — Recovered 1 ♦.`);
  renderAll();
}

function showVictory(){
  document.getElementById('overlayClass').classList.add('open');
  document.getElementById('overlayClass').innerHTML=`
    <div class="modal" style="text-align:center;padding:28px;background:radial-gradient(ellipse at 50% 0%, rgba(201,168,106,0.2), transparent 60%), linear-gradient(180deg,#17181e,#0f1014)">
      <div style="font-size:56px">👑</div>
      <h2 style="font-family:'Cinzel Decorative';color:var(--gold2);letter-spacing:0.12em;margin-top:8px">FLOOR CLEARED</h2>
      <p style="color:#b8b0a0;margin-top:8px">The Void Lord is slain. Gold: ${state.gold} • Deck: ${state.deck.length} cards</p>
      <div style="display:flex;gap:10px;justify-content:center;margin-top:18px">
        <button class="btn primary" onclick="location.reload()">PLAY AGAIN</button>
        <button class="btn" onclick="document.getElementById('overlayClass').classList.remove('open')">STAY</button>
      </div>
    </div>`;
}
function showDefeat(){
  document.getElementById('overlayClass').classList.add('open');
  document.getElementById('overlayClass').innerHTML=`
    <div class="modal" style="text-align:center;padding:28px;background:radial-gradient(ellipse at 50% 0%, rgba(180,40,40,0.2), transparent 60%), linear-gradient(180deg,#17181e,#0f1014)">
      <div style="font-size:56px">💀</div>
      <h2 style="font-family:'Cinzel Decorative';color:#e74c3c;letter-spacing:0.12em;margin-top:8px">YOU FELL</h2>
      <p style="color:#b8b0a0;margin-top:8px">Floor ${state.floor}-${state.room} • Survived ${state.turn} turns</p>
      <div style="display:flex;gap:10px;justify-content:center;margin-top:18px">
        <button class="btn primary" onclick="location.reload()">TRY AGAIN</button>
        <button class="btn" onclick="document.getElementById('overlayClass').classList.remove('open')">VIEW BOARD</button>
      </div>
    </div>`;
}

// INIT UI
function buildClassGrid(){
  const grid=document.getElementById('classGrid'); grid.innerHTML="";
  Object.entries(CLASSES).forEach(([key,c])=>{
    const d=document.createElement('div'); d.className='class-card'+(state.cls===key?' selected':'');
    d.innerHTML=`
      <div class="class-art" style="color:${c.color}">${c.sigil}</div>
      <div class="class-body">
        <h4 style="color:${c.color}">${c.name.toUpperCase()}</h4>
        <p>${c.desc}</p>
        <div class="class-stats">
          <span class="kbd">HP ${c.hp}</span><span class="kbd">♦ ${c.energy}</span><span class="kbd">HAND ${c.handSize}</span>
        </div>
        <div style="margin-top:10px">${svgCardBack(key)}</div>
      </div>
    `;
    d.onclick=()=>{ resetForClass(key); document.getElementById('overlayClass').classList.remove('open'); document.getElementById('btnCloseClass').style.display='block'; buildClassGrid(); };
    grid.appendChild(d);
  });
  const backPrev=document.getElementById('backPreview'); backPrev.innerHTML="";
  ["Bastion","Wither","Shroud"].forEach(k=>{
    const w=document.createElement('div'); w.innerHTML=svgCardBack(k); backPrev.appendChild(w);
  });
}
document.getElementById('btnClass').onclick=()=>document.getElementById('overlayClass').classList.add('open');
document.getElementById('btnCloseClass').onclick=()=>document.getElementById('overlayClass').classList.remove('open');
document.getElementById('btnShop').onclick=openShop;
document.getElementById('btnCloseShop').onclick=()=>document.getElementById('overlayShop').classList.remove('open');
document.getElementById('btnViewDeck').onclick=()=>{
  const grid=document.getElementById('deckModalGrid'); grid.innerHTML="";
  state.deck.forEach(id=>{
    const card=CARD_DB[id];
    const el=document.createElement('div'); el.className=`card ${card.cls}`;
    el.innerHTML=`<div class="card-top"><div class="card-cost cost-${card.type.toLowerCase()}">${card.cost}</div><span class="card-type type-${card.type}">${card.type}</span><span class="card-id">${card.id}</span></div><div class="card-art">${svgCardFrame(card.cls,card.type)}</div><div class="card-name">${card.icon} ${card.name}</div><div class="card-desc">${card.desc}</div>`;
    grid.appendChild(el);
  });
  if(state.deck.length===0) grid.innerHTML=`<div style="color:#888;padding:20px">Empty deck</div>`;
  document.getElementById('overlayDeck').classList.add('open');
};
document.getElementById('btnCloseDeck').onclick=()=>document.getElementById('overlayDeck').classList.remove('open');
document.getElementById('btnEndTurn').onclick=endTurn;
document.getElementById('btnDraw').onclick=()=>{
  if(state.energy<1) return toast("Need 1 ♦ to draw");
  state.energy-=1;
  if(state.drawPile.length===0 && state.discard.length>0){ state.drawPile=shuffle([...state.discard]); state.discard=[]; }
  if(state.drawPile.length>0){ const nid=state.drawPile.pop(); state.hand.push(nid); log(`Drew ${CARD_DB[nid].name} for 1 ♦.`); }
  renderAll();
};
document.getElementById('btnDiscard').onclick=()=>{
  while(state.hand.length>0) state.discard.push(state.hand.pop());
  drawUpToHand(); log(`Discarded hand, drew anew.`); renderAll();
};
document.getElementById('btnRestart').onclick=()=>{ resetForClass(state.cls); toast("Floor restarted"); };
document.querySelectorAll('.filter').forEach(b=>{
  b.onclick=()=>{ document.querySelectorAll('.filter').forEach(x=>x.classList.remove('active')); b.classList.add('active'); state.filter=b.dataset.filter; renderAll(); };
});
document.getElementById('overlayShop').onclick=(e)=>{ if(e.target.id==='overlayShop') e.currentTarget.classList.remove('open'); };
document.getElementById('overlayDeck').onclick=(e)=>{ if(e.target.id==='overlayDeck') e.currentTarget.classList.remove('open'); };

buildClassGrid();
resetForClass("Bastion");
// expose for inline handlers
window.buyCard=buyCard; window.openShop=openShop;
</script>
</body>
</html>