← Deckbuilder results

Deckbuilder

muse spark 1.2 contributor · 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 71,354 bytes · SHA-256 3d1edd927adb
<!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;600;700;900&family=Cinzel+Decorative:wght@700&family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
<style>
:root{
  --gold:#c9a86a; --gold2:#8c6b2e; --goldGlow:rgba(201,168,106,.25);
  --bg:#0a0c10; --panel:#141821; --panel2:#1c212e;
  --red:#c0392b; --blue:#3a7bd5; --purple:#7b5ea7; --green:#4a9a6a;
  --text:#e8e0c8; --muted:#8a7f6e; --dim:#5a5448;
  --attack:#e74c3c; --defense:#5dade2; --other:#a569bd;
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--text);font-family:'Cormorant Garamond',serif;overflow-x:hidden;min-height:100vh}
::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-thumb{background:#2a2f3a;border-radius:99px}
button{cursor:pointer;font-family:inherit}

/* BG texture */
.bg-ornament{position:fixed;inset:0;pointer-events:none;opacity:.07;background:
  radial-gradient(ellipse at 20% 0%, #c9a86a 0%, transparent 50%),
  radial-gradient(ellipse at 80% 100%, #3a1a3a 0%, transparent 50%),
  repeating-linear-gradient(90deg, transparent, transparent 120px, rgba(201,168,106,.03) 121px);
  z-index:0}
.bg-vignette{position:fixed;inset:0;pointer-events:none;background:radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,.75) 100%);z-index:1}

/* Top bar */
.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 18px;background:linear-gradient(180deg,#0f1218 0%, #0a0c10 100%);border-bottom:1px solid rgba(201,168,106,.2);backdrop-filter:blur(8px)}
.brand{display:flex;align-items:center;gap:10px}
.brand-mark{width:34px;height:34px;border:1px solid var(--gold);display:grid;place-items:center;background:radial-gradient(circle at 30% 30%, #2a2416, #0a0c10);transform:rotate(45deg)}
.brand-mark span{transform:rotate(-45deg);font-family:'Cinzel Decorative',serif;font-size:14px;color:var(--gold)}
.brand h1{font-family:'Cinzel',serif;font-weight:900;letter-spacing:.14em;font-size:14px;color:var(--gold);line-height:1}
.brand p{font-size:10px;letter-spacing:.18em;color:var(--muted);text-transform:uppercase;margin-top:2px}
.top-stats{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.stat-pill{display:flex;align-items:center;gap:8px;padding:7px 12px;background:var(--panel);border:1px solid rgba(201,168,106,.15);border-radius:999px;min-width:0}
.stat-pill .ico{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;font-size:11px;border:1px solid rgba(201,168,106,.25)}
.stat-pill b{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--gold)}
.stat-pill span{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.floor-track{display:flex;align-items:center;gap:6px;padding:6px 10px;background:rgba(0,0,0,.4);border:1px solid rgba(201,168,106,.12);border-radius:999px}
.dot{width:8px;height:8px;border-radius:50%;background:#2a2f3a;border:1px solid rgba(255,255,255,.08)}
.dot.on{background:var(--gold);box-shadow:0 0 8px var(--gold)}
.dot.boss{width:10px;height:10px;border-radius:2px;transform:rotate(45deg)}
.dot.boss.on{background:#e74c3c;box-shadow:0 0 10px #e74c3c}

/* Layout */
.app{position:relative;z-index:2;max-width:1440px;margin:0 auto;padding:14px 14px 18px;display:grid;grid-template-columns:320px 1fr 300px;gap:14px}
@media(max-width:1100px){.app{grid-template-columns:1fr;}.hide-m{display:none}}
.panel{background:linear-gradient(180deg, var(--panel) 0%, #0f131b 100%);border:1px solid rgba(201,168,106,.14);border-radius:14px;overflow:hidden;box-shadow:0 10px 30px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.04)}
.panel-head{padding:12px 14px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid rgba(201,168,106,.1);background:linear-gradient(180deg, rgba(201,168,106,.07), transparent)}
.panel-head h3{font-family:'Cinzel',serif;font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--gold)}
.panel-head small{font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--muted)}

/* Left: Hand & Deck */
.hand-area{padding:12px;display:grid;gap:10px}
.hand-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.card{position:relative;height:168px;border-radius:12px;overflow:hidden;cursor:pointer;transition:transform .15s, box-shadow .15s, filter .15s;background:#0f1218;border:1px solid rgba(255,255,255,.06)}
.card:hover{transform:translateY(-3px) scale(1.01);box-shadow:0 12px 24px rgba(0,0,0,.6), 0 0 0 1px rgba(201,168,106,.25)}
.card.disabled{opacity:.45;filter:grayscale(.3);cursor:not-allowed;transform:none}
.card .card-bg{position:absolute;inset:0}
.card .card-inner{position:absolute;inset:0;padding:10px 10px 8px;display:flex;flex-direction:column}
.card-top{display:flex;justify-content:space-between;align-items:flex-start;gap:6px}
.card-cost{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;font-family:'Cinzel',serif;font-weight:800;font-size:13px;color:#fff;border:2px solid rgba(255,255,255,.9);box-shadow:0 2px 8px rgba(0,0,0,.5);flex-shrink:0}
.card-type{font-family:'JetBrains Mono',monospace;font-size:8px;letter-spacing:.14em;text-transform:uppercase;padding:4px 7px;border-radius:999px;border:1px solid rgba(255,255,255,.18);color:#fff;backdrop-filter:blur(6px)}
.card-name{font-family:'Cinzel',serif;font-weight:700;font-size:11px;letter-spacing:.06em;color:#fff;line-height:1.1;margin-top:6px;text-shadow:0 1px 6px rgba(0,0,0,.8)}
.card-ability{font-size:11px;line-height:1.3;color:#e8e0c8;margin-top:4px;flex:1}
.card-stats{display:flex;gap:6px;margin-top:6px}
.stat-badge{flex:1;display:flex;align-items:center;justify-content:center;gap:4px;padding:5px 4px;border-radius:8px;font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:700;border:1px solid rgba(255,255,255,.12)}
.card-id{position:absolute;bottom:6px;right:8px;font-family:'JetBrains Mono',monospace;font-size:7px;letter-spacing:.1em;color:rgba(255,255,255,.45)}
.card-frame-svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;opacity:.95}

/* Center arena */
.arena{padding:14px;display:flex;flex-direction:column;gap:12px;min-height:560px}
.arena-top{display:flex;gap:10px;align-items:center;justify-content:space-between;flex-wrap:wrap}
.enemy-card{position:relative;background:radial-gradient(ellipse at 50% 0%, rgba(201,168,106,.08), transparent 60%), linear-gradient(180deg, #151a24, #0e1118);border:1px solid rgba(201,168,106,.14);border-radius:16px;padding:14px;overflow:hidden}
.enemy-visual{position:relative;height:220px;border-radius:12px;overflow:hidden;background:radial-gradient(ellipse at 50% 30%, rgba(231,76,60,.18), transparent 60%), radial-gradient(ellipse at 50% 90%, rgba(0,0,0,.8), transparent 70%), linear-gradient(180deg, #1a1f2e 0%, #0a0c10 100%);border:1px solid rgba(255,255,255,.06);display:grid;place-items:center}
.enemy-sprite{width:140px;height:140px;filter:drop-shadow(0 10px 20px rgba(0,0,0,.7));transition:transform .2s}
.enemy-sprite.hit{animation:hit .35s}
@keyframes hit{0%{transform:translateX(0)}25%{transform:translateX(-6px) rotate(-2deg)}50%{transform:translateX(6px) rotate(2deg)}75%{transform:translateX(-4px)}100%{transform:translateX(0)}}
.floating-dmg{position:absolute;left:50%;top:40%;transform:translate(-50%,-50%);font-family:'Cinzel',serif;font-weight:900;font-size:28px;color:#ff6b6b;text-shadow:0 2px 12px rgba(0,0,0,.8);pointer-events:none;animation:floatUp .9s forwards}
@keyframes floatUp{0%{opacity:0;transform:translate(-50%,-10%) scale(.8)}15%{opacity:1}100%{opacity:0;transform:translate(-50%,-70%) scale(1.1)}}
.hp-bar{height:10px;background:#0a0c10;border-radius:999px;overflow:hidden;border:1px solid rgba(255,255,255,.08);position:relative}
.hp-fill{height:100%;border-radius:999px;transition:width .4s ease;background:linear-gradient(90deg, #c0392b, #e74c3c);box-shadow:0 0 10px rgba(231,76,60,.5)}
.hp-fill.boss{background:linear-gradient(90deg, #6a1b1b, #e74c3c 60%, #ff9a3c)}
.hp-text{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--muted)}
.enemy-name{font-family:'Cinzel',serif;font-weight:700;letter-spacing:.08em;font-size:13px;color:#fff}
.enemy-meta{font-size:11px;color:var(--muted)}
.status-row{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
.status{font-family:'JetBrains Mono',monospace;font-size:9px;letter-spacing:.08em;text-transform:uppercase;padding:4px 7px;border-radius:999px;border:1px solid rgba(255,255,255,.12);display:flex;align-items:center;gap:4px}
.status.burn{background:rgba(231,76,60,.15);color:#ff9a7a;border-color:rgba(231,76,60,.3)}
.status.poison{background:rgba(74,154,106,.15);color:#7ee0a0;border-color:rgba(74,154,106,.3)}
.status.stun{background:rgba(93,173,226,.15);color:#8ec8ff;border-color:rgba(93,173,226,.3)}
.status.block{background:rgba(201,168,106,.12);color:var(--gold);border-color:rgba(201,168,106,.25)}

/* Right panel */
.right-stack{display:flex;flex-direction:column;gap:14px}
.hero-panel{padding:14px}
.hero-row{display:flex;gap:12px;align-items:center}
.hero-avatar{width:56px;height:56px;border-radius:12px;border:1px solid rgba(201,168,106,.25);overflow:hidden;background:radial-gradient(circle at 30% 30%, #2a2416, #0a0c10);display:grid;place-items:center;position:relative}
.hero-avatar svg{width:42px;height:42px}
.hero-name{font-family:'Cinzel',serif;font-weight:700;font-size:13px;letter-spacing:.06em;color:var(--gold)}
.hero-class{font-size:11px;color:var(--muted);letter-spacing:.08em;text-transform:uppercase}
.meter{margin-top:10px}
.meter-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}
.meter-head span{font-family:'JetBrains Mono',monospace;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.meter-head b{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--text)}
.energy-orbs{display:flex;gap:6px;flex-wrap:wrap}
.orb{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;font-size:12px;border:1px solid rgba(201,168,106,.25);background:radial-gradient(circle at 30% 30%, #3a2e16, #1a160c);color:var(--gold);box-shadow:inset 0 1px 0 rgba(255,255,255,.15), 0 2px 8px rgba(0,0,0,.4);transition:all .2s}
.orb.on{background:radial-gradient(circle at 30% 30%, #ffd27a, #c9a86a 60%, #8c6b2e);color:#1a160c;border-color:#ffd27a;box-shadow:0 0 12px rgba(201,168,106,.6), inset 0 1px 0 rgba(255,255,255,.6)}
.orb.empty{opacity:.25}
.shop-preview{padding:12px}
.shop-grid{display:grid;grid-template-columns:1fr;gap:10px;max-height:420px;overflow:auto;padding-right:4px}
.shop-card{position:relative;height:112px;border-radius:12px;overflow:hidden;border:1px solid rgba(255,255,255,.06);background:#0f1218;display:flex;cursor:pointer;transition:transform .15s, border-color .15s}
.shop-card:hover{transform:translateY(-1px);border-color:rgba(201,168,106,.3)}
.shop-card .shop-info{flex:1;padding:10px;position:relative;z-index:1}
.shop-price{position:absolute;top:8px;right:8px;background:linear-gradient(180deg, #c9a86a, #8c6b2e);color:#0a0c10;font-family:'JetBrains Mono',monospace;font-weight:800;font-size:11px;padding:4px 8px;border-radius:999px;border:1px solid rgba(255,255,255,.2)}
.btn{appearance:none;border:1px solid rgba(201,168,106,.25);background:linear-gradient(180deg, #2a2f3a, #1a1f2a);color:var(--text);padding:10px 14px;border-radius:10px;font-family:'Cinzel',serif;font-weight:700;letter-spacing:.08em;font-size:11px;text-transform:uppercase;transition:all .15s;box-shadow:0 4px 12px rgba(0,0,0,.3), inset 0 1px 0 rgba(255,255,255,.06)}
.btn:hover{border-color:var(--gold);box-shadow:0 6px 18px rgba(0,0,0,.4), 0 0 0 1px rgba(201,168,106,.15);transform:translateY(-1px)}
.btn:active{transform:translateY(0)}
.btn.primary{background:linear-gradient(180deg, #c9a86a, #a6863d);color:#0a0c10;border-color:#e8c88a;box-shadow:0 6px 18px rgba(201,168,106,.3)}
.btn.ghost{background:transparent}
.btn.small{padding:7px 10px;font-size:10px}
.btn:disabled{opacity:.4;cursor:not-allowed;transform:none}
.controls{display:flex;gap:8px;flex-wrap:wrap}
.log{height:120px;overflow:auto;background:rgba(0,0,0,.35);border:1px solid rgba(255,255,255,.06);border-radius:10px;padding:8px 10px;font-family:'JetBrains Mono',monospace;font-size:11px;line-height:1.5;color:#c8c0a8}
.log b{color:var(--gold)}
.log .dmg{color:#ff7a7a}
.log .heal{color:#7ee0a0}
.log .block{color:#8ec8ff}

/* Overlays */
.overlay{position:fixed;inset:0;z-index:40;display:none;place-items:center;padding:18px;background:rgba(8,10,18,.42);backdrop-filter:blur(7px) saturate(1.08)}
.overlay.open{display:grid}
.modal{width:min(980px,100%);max-height:92vh;overflow:auto;background:radial-gradient(ellipse at 50% 0%, rgba(201,168,106,.22), transparent 60%), radial-gradient(ellipse at 85% 100%, rgba(123,94,167,.14), transparent 50%), linear-gradient(180deg, #242b44 0%, #1a1f33 55%, #141827 100%);border:1px solid rgba(201,168,106,.42);border-radius:18px;box-shadow:0 30px 80px rgba(0,0,0,.7), 0 0 0 1px rgba(201,168,106,.2) inset, 0 0 50px rgba(201,168,106,.16)}
.modal-head{position:sticky;top:0;z-index:1;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px 18px;background:linear-gradient(180deg, #151a24, #131824);border-bottom:1px solid rgba(201,168,106,.14)}
.modal-head h2{font-family:'Cinzel',serif;font-weight:800;letter-spacing:.12em;font-size:14px;color:var(--gold)}
.class-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;padding:16px}
@media(max-width:800px){.class-grid{grid-template-columns:1fr}}
.class-card{position:relative;border-radius:16px;overflow:hidden;border:1px solid rgba(201,168,106,.14);background:linear-gradient(180deg, #1a1f2e, #0f131b);padding:14px;cursor:pointer;transition:transform .15s, border-color .15s, box-shadow .15s}
.class-card:hover{transform:translateY(-2px);border-color:rgba(201,168,106,.35);box-shadow:0 12px 30px rgba(0,0,0,.5)}
.class-card.selected{border-color:var(--gold);box-shadow:0 0 0 1px rgba(201,168,106,.3), 0 12px 30px rgba(0,0,0,.6)}
.class-icon{width:64px;height:64px;border-radius:14px;display:grid;place-items:center;border:1px solid rgba(201,168,106,.18);margin-bottom:10px}
.class-card h3{font-family:'Cinzel',serif;font-size:13px;letter-spacing:.1em;color:#fff}
.class-card p{font-size:12px;line-height:1.5;color:var(--muted);margin-top:6px;min-height:54px}
.class-stats{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
.mini-stat{font-family:'JetBrains Mono',monospace;font-size:10px;padding:5px 8px;border-radius:999px;border:1px solid rgba(255,255,255,.08);background:rgba(0,0,0,.25);color:var(--muted)}
.shop-modal-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;padding:16px}
@media(max-width:900px){.shop-modal-grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:560px){.shop-modal-grid{grid-template-columns:1fr}}
.deck-list{display:flex;flex-wrap:wrap;gap:6px;padding:12px}
.deck-chip{font-family:'JetBrains Mono',monospace;font-size:10px;padding:6px 8px;border-radius:999px;border:1px solid rgba(201,168,106,.18);background:rgba(201,168,106,.08);color:var(--gold)}
.toast{position:fixed;left:50%;bottom:18px;transform:translateX(-50%);z-index:50;background:#151a24;border:1px solid rgba(201,168,106,.25);color:var(--text);padding:10px 14px;border-radius:999px;font-family:'JetBrains Mono',monospace;font-size:11px;box-shadow:0 10px 30px rgba(0,0,0,.5);display:none}
.toast.show{display:block;animation:pop .3s}
@keyframes pop{0%{transform:translateX(-50%) translateY(6px) scale(.98);opacity:0}100%{transform:translateX(-50%) translateY(0) scale(1);opacity:1}}
.kbd{font-family:'JetBrains Mono',monospace;font-size:10px;padding:2px 6px;border-radius:6px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.06);color:var(--muted)}
</style>
</head>
<body>
<div class="bg-ornament"></div>
<div class="bg-vignette"></div>

<header class="topbar">
  <div class="brand">
    <div class="brand-mark"><span>Æ</span></div>
    <div>
      <h1>SHADOWS OF AETHELGARD</h1>
      <p>Void Consumes — Heroes Rise</p>
    </div>
  </div>
  <div class="top-stats">
    <div class="stat-pill" title="Gold"><div class="ico" style="background:radial-gradient(circle at 30% 30%, #ffd27a, #8c6b2e);color:#1a160c">◈</div><b id="goldVal">0</b><span>Gold</span></div>
    <div class="stat-pill" title="Floor"><div class="ico" style="background:#1a1f2e;color:var(--gold);border-color:rgba(201,168,106,.3)">◉</div><b id="floorVal">1 — 1/4</b><span>Floor</span></div>
    <div class="floor-track" id="floorTrack"></div>
    <button class="btn small" id="btnShop">Shop</button>
    <button class="btn small ghost" id="btnRestart">Restart</button>
  </div>
</header>

<main class="app">
  <!-- Left -->
  <section class="panel">
    <div class="panel-head"><h3>Hand</h3><small id="handInfo">0 / 4</small></div>
    <div class="hand-area">
      <div class="hand-grid" id="handGrid"></div>
      <div style="display:flex;gap:8px;flex-wrap:wrap">
        <button class="btn small" id="btnDraw">Draw <span class="kbd">D</span></button>
        <button class="btn small ghost" id="btnViewDeck">View Deck</button>
        <span id="deckCount" style="margin-left:auto;font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--muted)"></span>
      </div>
      <div class="deck-list" id="deckChips"></div>
    </div>
  </section>

  <!-- Center -->
  <section class="panel arena">
    <div class="arena-top">
      <div>
        <div style="font-family:'Cinzel',serif;font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--gold)">Combat Chamber</div>
        <div id="roomLabel" style="font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--muted)">Floor 1 — Room 1 • Goblin Scout</div>
      </div>
      <div style="display:flex;gap:8px;align-items:center">
        <span class="kbd">Space</span><span style="font-size:11px;color:var(--muted)">End Turn</span>
      </div>
    </div>

    <div class="enemy-card">
      <div style="display:flex;justify-content:space-between;gap:10px;align-items:center;margin-bottom:10px">
        <div>
          <div class="enemy-name" id="enemyName">—</div>
          <div class="enemy-meta" id="enemyMeta">—</div>
        </div>
        <div style="text-align:right">
          <div class="hp-text"><span id="enemyHpText">0/0</span></div>
          <div style="font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--muted)">ATK <b id="enemyAtk" style="color:#ff9a7a">0</b> <span id="enemySpecial" style="margin-left:6px;color:var(--gold)"></span></div>
        </div>
      </div>
      <div class="hp-bar"><div class="hp-fill" id="enemyHpFill" style="width:100%"></div></div>
      <div class="enemy-visual" id="enemyVisual">
        <svg class="enemy-sprite" id="enemySprite" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"></svg>
        <div id="floatingLayer" style="position:absolute;inset:0;pointer-events:none"></div>
      </div>
      <div class="status-row" id="enemyStatus"></div>
      <div class="status-row" id="playerStatus"></div>
    </div>

    <div class="controls">
      <button class="btn primary" id="btnEndTurn">End Turn — Enemy Attacks</button>
      <button class="btn" id="btnNextRoom" style="display:none">Advance → Next Room</button>
      <button class="btn ghost" id="btnHealHint">Heal 6 HP costs 3 Energy (Repair)</button>
    </div>
    <div class="log" id="combatLog"></div>
  </section>

  <!-- Right -->
  <div class="right-stack">
    <section class="panel hero-panel">
      <div class="hero-row">
        <div class="hero-avatar" id="heroAvatar"></div>
        <div>
          <div class="hero-name" id="heroName">The Bastion</div>
          <div class="hero-class" id="heroClass">Immovable Object</div>
          <div style="font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--muted);margin-top:2px" id="heroStatsLine">35 HP • 3 Energy • Hand 4</div>
        </div>
      </div>
      <div class="meter">
        <div class="meter-head"><span>Health</span><b id="hpVal">35 / 35</b></div>
        <div class="hp-bar"><div class="hp-fill" id="playerHpFill" style="width:100%;background:linear-gradient(90deg, #2ecc71, #27ae60)"></div></div>
      </div>
      <div class="meter">
        <div class="meter-head"><span>Defense (Block)</span><b id="defVal">0</b></div>
        <div class="hp-bar" style="height:8px"><div id="defFill" style="height:100%;width:0%;background:linear-gradient(90deg, #5dade2, #2e86c1);border-radius:999px;transition:width .3s"></div></div>
        <div style="font-size:11px;color:var(--muted);margin-top:4px">Blocks incoming damage. Temporary defense fades next turn unless Iron Skin.</div>
      </div>
      <div class="meter">
        <div class="meter-head"><span>Energy</span><b id="energyVal">3 / 5</b></div>
        <div class="energy-orbs" id="energyOrbs"></div>
        <div style="font-size:11px;color:var(--muted);margin-top:6px">+1 Energy at end of turn. Some cards grant extra.</div>
      </div>
    </section>

    <section class="panel shop-preview">
      <div class="panel-head" style="margin:-12px -12px 12px"><h3>Quick Shop</h3><small id="shopHint">1 Gold / card</small></div>
      <div class="shop-grid" id="quickShop" style="max-height:360px"></div>
      <button class="btn small" style="width:100%;margin-top:10px" id="btnOpenShop2">Open Full Shop</button>
    </section>
  </div>
</main>

<!-- Class Select Overlay -->
<div class="overlay open" id="overlayClass">
  <div class="modal">
    <div class="modal-head">
      <h2>Choose Your Hero — Shadows of Aethelgard</h2>
      <span style="font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--muted)">3 Floors • 9 Rooms + 3 Bosses • Survive the Void</span>
    </div>
    <div style="padding:14px 18px 0;color:var(--muted);font-size:12px;line-height:1.6">
      Dark Fantasy Deckbuilder. Manage Health & Energy. Play cards to attack, block, and inflict Burn/Poison/Stun. Earn Gold to expand your deck. Each class has a unique SVG frame and card back.
    </div>
    <div class="class-grid">
      <div class="class-card selected" data-class="bastion">
        <div class="class-icon" style="background:radial-gradient(circle at 30% 30%, #3a4a5a, #1a1f2a);border-color:rgba(93,173,226,.3)"><span style="font-size:28px">🛡️</span></div>
        <h3>The Bastion</h3>
        <p><b style="color:#5dade2">Immovable Object.</b> High Defense, AoE. Starts 35 HP / 3 Energy. Shield Wall, Iron Skin, Earthquake.</p>
        <div class="class-stats"><span class="mini-stat">HP 35</span><span class="mini-stat">ENG 3</span><span class="mini-stat">DEF Focus</span></div>
        <div style="margin-top:10px;display:flex;gap:8px">
          <div style="flex:1;height:54px;border-radius:10px;overflow:hidden;border:1px solid rgba(255,255,255,.06)" id="previewBastion"></div>
          <div style="flex:1;height:54px;border-radius:10px;overflow:hidden;border:1px solid rgba(255,255,255,.06)" id="previewBastionBack"></div>
        </div>
        <div style="font-family:'JetBrains Mono',monospace;font-size:9px;color:var(--muted);margin-top:6px">Frame: Riveted Stone • Back: Bastion Sigil</div>
      </div>
      <div class="class-card" data-class="wither">
        <div class="class-icon" style="background:radial-gradient(circle at 30% 30%, #3a2a1a, #1a120a);border-color:rgba(231,76,60,.3)"><span style="font-size:28px">🔥</span></div>
        <h3>The Wither</h3>
        <p><b style="color:#e74c3c">Rot and Burn.</b> Burn/Poison, Debuffs. Low HP, High AoE. Fireball, Inferno, Wither.</p>
        <div class="class-stats"><span class="mini-stat">HP 28</span><span class="mini-stat">ENG 4</span><span class="mini-stat">Burn</span></div>
        <div style="margin-top:10px;display:flex;gap:8px">
          <div style="flex:1;height:54px;border-radius:10px;overflow:hidden;border:1px solid rgba(255,255,255,.06)" id="previewWither"></div>
          <div style="flex:1;height:54px;border-radius:10px;overflow:hidden;border:1px solid rgba(255,255,255,.06)" id="previewWitherBack"></div>
        </div>
        <div style="font-family:'JetBrains Mono',monospace;font-size:9px;color:var(--muted);margin-top:6px">Frame: Thorn & Ember • Back: Wither Bloom</div>
      </div>
      <div class="class-card" data-class="shroud">
        <div class="class-icon" style="background:radial-gradient(circle at 30% 30%, #2a1a3a, #0f0a1a);border-color:rgba(165,105,189,.35)"><span style="font-size:28px">🗡️</span></div>
        <h3>The Shroud</h3>
        <p><b style="color:#a569bd">Shadow & Speed.</b> Evasion, Assassination. High Crit, Low Defense. Backstab, Blur, Assassinate.</p>
        <div class="class-stats"><span class="mini-stat">HP 26</span><span class="mini-stat">ENG 5</span><span class="mini-stat">Evasion</span></div>
        <div style="margin-top:10px;display:flex;gap:8px">
          <div style="flex:1;height:54px;border-radius:10px;overflow:hidden;border:1px solid rgba(255,255,255,.06)" id="previewShroud"></div>
          <div style="flex:1;height:54px;border-radius:10px;overflow:hidden;border:1px solid rgba(255,255,255,.06)" id="previewShroudBack"></div>
        </div>
        <div style="font-family:'JetBrains Mono',monospace;font-size:9px;color:var(--muted);margin-top:6px">Frame: Obsidian Dagger • Back: Shroud Veil</div>
      </div>
    </div>
    <div style="padding:14px 18px 18px;display:flex;gap:10px;justify-content:flex-end;align-items:center;flex-wrap:wrap">
      <span style="font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--muted)">Starter deck: 8 cards • Shop: 36 cards • 5 copies max</span>
      <button class="btn primary" id="btnStart">Enter Aethelgard</button>
    </div>
  </div>
</div>

<!-- Shop Overlay -->
<div class="overlay" id="overlayShop">
  <div class="modal">
    <div class="modal-head"><h2>Card Shop — 1 Gold per card <span style="font-weight:400;text-transform:none;letter-spacing:0;color:var(--muted);font-size:11px">(4th copy 10G, 5th 15G)</span></h2><button class="btn small" id="btnCloseShop">Close</button></div>
    <div style="padding:12px 16px;display:flex;gap:8px;flex-wrap:wrap;align-items:center">
      <span style="font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--muted)">Filter:</span>
      <button class="btn small" data-filter="all">All</button>
      <button class="btn small ghost" data-filter="Attack">Attack</button>
      <button class="btn small ghost" data-filter="Defense">Defense</button>
      <button class="btn small ghost" data-filter="Other">Other</button>
      <span style="margin-left:auto;font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--gold)">Gold: <b id="shopGold">0</b></span>
    </div>
    <div class="shop-modal-grid" id="shopGrid"></div>
  </div>
</div>

<!-- Deck Overlay -->
<div class="overlay" id="overlayDeck">
  <div class="modal">
    <div class="modal-head"><h2>Your Deck & Collection</h2><button class="btn small" id="btnCloseDeck">Close</button></div>
    <div id="deckView" style="padding:16px;display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:10px"></div>
  </div>
</div>

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

<script>
const CLASSES = {
  bastion: { name:'The Bastion', subtitle:'Immovable Object', hp:35, energy:3, maxEnergy:5, handSize:4, color:'#5dade2' },
  wither:  { name:'The Wither',  subtitle:'Rot and Burn', hp:28, energy:4, maxEnergy:5, handSize:4, color:'#e74c3c' },
  shroud:  { name:'The Shroud',  subtitle:'Shadow and Speed', hp:26, energy:5, maxEnergy:5, handSize:4, color:'#a569bd' }
};

// 36 cards
const CARDS = {
  // Bastion
  'BA-A01':{id:'BA-A01', cls:'bastion', type:'Attack', cost:2, atk:3, def:0, name:'Slam', ability:'Deals 3 damage. If you have 30+ HP, draw 1 card.', rarity:'Common'},
  'BA-A02':{id:'BA-A02', cls:'bastion', type:'Attack', cost:3, atk:4, def:0, name:'Earthquake', ability:'Deals 4 damage to ALL enemies.', rarity:'Common'},
  'BA-A03':{id:'BA-A03', cls:'bastion', type:'Attack', cost:1, atk:2, def:0, name:'Stone Fist', ability:'Deals 2 damage.', rarity:'Common'},
  'BA-A04':{id:'BA-A04', cls:'bastion', type:'Attack', cost:4, atk:6, def:0, name:'Boulder', ability:'Deals 6 damage. Heavy.', rarity:'Common'},
  'BA-D01':{id:'BA-D01', cls:'bastion', type:'Defense', cost:0, atk:0, def:4, name:'Shield Wall', ability:'Gain 4 Defense this turn.', rarity:'Common'},
  'BA-D02':{id:'BA-D02', cls:'bastion', type:'Defense', cost:2, atk:0, def:5, name:'Iron Skin', ability:'Gain 5 Defense permanently. Discard 1 card.', rarity:'Common'},
  'BA-D03':{id:'BA-D03', cls:'bastion', type:'Defense', cost:1, atk:0, def:2, name:'Stand Firm', ability:'Gain 2 Defense. Blocks 1 incoming damage.', rarity:'Common'},
  'BA-D04':{id:'BA-D04', cls:'bastion', type:'Defense', cost:2, atk:0, def:3, name:'Taunt', ability:'Gain 3 Defense. Stun enemy 1 turn.', rarity:'Common'},
  'BA-U01':{id:'BA-U01', cls:'bastion', type:'Other', cost:2, atk:0, def:0, name:'Search', ability:'Draw 1 card.', rarity:'Common'},
  'BA-U02':{id:'BA-U02', cls:'bastion', type:'Other', cost:3, atk:0, def:0, name:'Repair', ability:'Heal 6 HP.', rarity:'Common'},
  'BA-U03':{id:'BA-U03', cls:'bastion', type:'Other', cost:1, atk:0, def:0, name:'Rally', ability:'Draw 1 card. Gain 1 Energy.', rarity:'Common'},
  'BA-U04':{id:'BA-U04', cls:'bastion', type:'Other', cost:2, atk:0, def:0, name:'Loot', ability:'Gain 5 Gold.', rarity:'Common'},
  // Wither
  'WI-A01':{id:'WI-A01', cls:'wither', type:'Attack', cost:2, atk:3, def:0, name:'Fireball', ability:'Deals 3 damage. Apply 1 Burn.', rarity:'Common'},
  'WI-A02':{id:'WI-A02', cls:'wither', type:'Attack', cost:3, atk:4, def:0, name:'Inferno', ability:'Deals 4 damage. Apply 3 Burn.', rarity:'Common'},
  'WI-A03':{id:'WI-A03', cls:'wither', type:'Attack', cost:1, atk:2, def:0, name:'Ember', ability:'Deals 2 damage. 1 Burn/turn.', rarity:'Common'},
  'WI-A04':{id:'WI-A04', cls:'wither', type:'Attack', cost:4, atk:5, def:0, name:'Pyre', ability:'Deals 5 damage. All enemies +1 Burn next turn.', rarity:'Common'},
  'WI-D01':{id:'WI-D01', cls:'wither', type:'Defense', cost:2, atk:0, def:3, name:'Scorch Shield', ability:'Gain 3 Defense.', rarity:'Common'},
  'WI-D02':{id:'WI-D02', cls:'wither', type:'Defense', cost:1, atk:0, def:1, name:'Heat Resist', ability:'Gain 1 Defense. Gain 1 Energy.', rarity:'Common'},
  'WI-D03':{id:'WI-D03', cls:'wither', type:'Defense', cost:2, atk:0, def:4, name:'Smoke', ability:'Gain 4 Defense.', rarity:'Common'},
  'WI-D04':{id:'WI-D04', cls:'wither', type:'Defense', cost:3, atk:0, def:2, name:'Endure', ability:'Gain 2 Defense. Heal 2 HP.', rarity:'Common'},
  'WI-U01':{id:'WI-U01', cls:'wither', type:'Other', cost:2, atk:0, def:0, name:'Poison', ability:'Apply 1 Poison.', rarity:'Common'},
  'WI-U02':{id:'WI-U02', cls:'wither', type:'Other', cost:3, atk:0, def:0, name:'Burn', ability:'Deal 3 damage + 2 Burn to all.', rarity:'Common'},
  'WI-U03':{id:'WI-U03', cls:'wither', type:'Other', cost:1, atk:0, def:0, name:'Slow', ability:'Draw 1 card. Stun target.', rarity:'Common'},
  'WI-U04':{id:'WI-U04', cls:'wither', type:'Other', cost:2, atk:0, def:0, name:'Wither', ability:'Heal 5 HP. Reduce enemy ATK by 1.', rarity:'Common'},
  // Shroud
  'SH-A01':{id:'SH-A01', cls:'shroud', type:'Attack', cost:2, atk:4, def:0, name:'Backstab', ability:'Deals 4 damage. +1 if target has no Armor.', rarity:'Common'},
  'SH-A02':{id:'SH-A02', cls:'shroud', type:'Attack', cost:3, atk:5, def:0, name:'Shadow Bolt', ability:'Deals 5 damage. Cannot be blocked.', rarity:'Common'},
  'SH-A03':{id:'SH-A03', cls:'shroud', type:'Attack', cost:1, atk:3, def:0, name:'Silence', ability:'Deals 3 damage. Silence target.', rarity:'Common'},
  'SH-A04':{id:'SH-A04', cls:'shroud', type:'Attack', cost:3, atk:6, def:0, name:'Vengeance', ability:'Deals 6 damage. Gain 2 Energy.', rarity:'Common'},
  'SH-D01':{id:'SH-D01', cls:'shroud', type:'Defense', cost:3, atk:0, def:2, name:'Blur', ability:'Gain 2 Defense. Ignore next attack.', rarity:'Common'},
  'SH-D02':{id:'SH-D02', cls:'shroud', type:'Defense', cost:2, atk:0, def:3, name:'Step', ability:'Gain 3 Defense. Gain 1 Energy.', rarity:'Common'},
  'SH-D03':{id:'SH-D03', cls:'shroud', type:'Defense', cost:1, atk:0, def:1, name:'Evasion', ability:'Gain 1 Defense. Skip enemy action.', rarity:'Common'},
  'SH-D04':{id:'SH-D04', cls:'shroud', type:'Defense', cost:2, atk:0, def:4, name:'Phantom', ability:'Gain 4 Defense. Ghost form.', rarity:'Common'},
  'SH-U01':{id:'SH-U01', cls:'shroud', type:'Other', cost:1, atk:0, def:0, name:'Dash', ability:'Gain 1 Energy. Skip enemy attack.', rarity:'Common'},
  'SH-U02':{id:'SH-U02', cls:'shroud', type:'Other', cost:2, atk:0, def:0, name:'Teleport', ability:'Deal 2 damage. Gain 1 Defense.', rarity:'Common'},
  'SH-U03':{id:'SH-U03', cls:'shroud', type:'Other', cost:2, atk:0, def:0, name:'Hide', ability:'Gain 1 Defense. Draw 2, discard 1.', rarity:'Common'},
  'SH-U04':{id:'SH-U04', cls:'shroud', type:'Other', cost:3, atk:0, def:0, name:'Assassinate', ability:'Deal 8 damage. Unblockable.', rarity:'Common'},
};

function starterDeck(cls){
  if(cls==='bastion') return ['BA-A01','BA-A03','BA-D01','BA-D01','BA-D03','BA-U03','BA-U02','BA-A02'];
  if(cls==='wither') return ['WI-A01','WI-A03','WI-D02','WI-D03','WI-U01','WI-U03','WI-A02','WI-D01'];
  return ['SH-A01','SH-A03','SH-D03','SH-D02','SH-U01','SH-A02','SH-U03','SH-D04'];
}

// SVG frames per deck type
function frameSVG(cls, type){
  if(cls==='bastion'){
    const col = type==='Attack' ? '#c0392b' : type==='Defense' ? '#2e86c1' : '#8e6aa8';
    return `<svg class="card-frame-svg" viewBox="0 0 200 168" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <defs><linearGradient id="gB${type}" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#2a3442"/><stop offset="100%" stop-color="#0f1218"/></linearGradient>
      <linearGradient id="rimB" x1="0" y1="0" x2="1" y2="0"><stop offset="0%" stop-color="#8a9bb0"/><stop offset="50%" stop-color="#d0d8e2"/><stop offset="100%" stop-color="#6b7a8e"/></linearGradient></defs>
      <rect x="2" y="2" width="196" height="164" rx="12" fill="url(#gB${type})" stroke="url(#rimB)" stroke-width="1.2"/>
      <rect x="7" y="7" width="186" height="154" rx="9" fill="none" stroke="${col}" stroke-width="1" opacity=".35"/>
      <!-- rivets -->
      <circle cx="14" cy="14" r="2.2" fill="#9fb0c3" stroke="#5a6a7e" stroke-width=".6"/><circle cx="186" cy="14" r="2.2" fill="#9fb0c3" stroke="#5a6a7e" stroke-width=".6"/><circle cx="14" cy="154" r="2.2" fill="#9fb0c3" stroke="#5a6a7e" stroke-width=".6"/><circle cx="186" cy="154" r="2.2" fill="#9fb0c3" stroke="#5a6a7e" stroke-width=".6"/>
      <path d="M 18 8 H 182 M 18 160 H 182 M 8 18 V 150 M 192 18 V 150" stroke="rgba(255,255,255,.08)" stroke-width=".8" stroke-dasharray="4 4"/>
      <!-- top crest -->
      <path d="M 92 10 L 100 4 L 108 10 L 104 14 L 96 14 Z" fill="#c9a86a" stroke="#8c6b2e" stroke-width=".7"/>
      <rect x="68" y="22" width="64" height="46" rx="3" fill="rgba(255,255,255,.06)" stroke="rgba(255,255,255,.08)"/>
      <text x="100" y="44" text-anchor="middle" font-family="Cinzel" font-size="7" fill="rgba(255,255,255,.18)" letter-spacing="1">AETHELGARD</text>
    </svg>`;
  }
  if(cls==='wither'){
    const col = type==='Attack' ? '#e74c3c' : type==='Defense' ? '#e67e22' : '#4a9a6a';
    return `<svg class="card-frame-svg" viewBox="0 0 200 168" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
      <defs><radialGradient id="gW${type}" cx="50%" cy="0%"><stop offset="0%" stop-color="#3a1a12"/><stop offset="100%" stop-color="#0f1210"/></radialGradient></defs>
      <rect x="2" y="2" width="196" height="164" rx="12" fill="url(#gW${type})" stroke="#6b3a1f" stroke-width="1.2"/>
      <rect x="7" y="7" width="186" height="154" rx="10" fill="none" stroke="${col}" stroke-width="1.1" opacity=".5"/>
      <!-- thorns -->
      <path d="M 10 28 L 16 22 L 14 30 Z M 184 32 L 190 26 L 188 34 Z M 12 138 L 18 144 L 14 146 Z M 186 140 L 192 146 L 188 148 Z" fill="#8c4a2a" stroke="#5a2a12" stroke-width=".5"/>
      <path d="M 30 8 C 40 14, 60 6, 72 12 M 128 8 C 138 14, 158 6, 170 12 M 30 160 C 40 154, 60 162, 72 156 M 128 160 C 138 154, 158 162, 170 156" stroke="#7a3a1a" stroke-width="1.1" fill="none" opacity=".6"/>
      <ellipse cx="100" cy="46" rx="38" ry="22" fill="rgba(231,76,60,.08)" stroke="rgba(231,76,60,.15)"/>
      <path d="M 92 14 C 96 10, 104 10, 108 14 L 106 18 L 94 18 Z" fill="#c0392b" opacity=".9"/>
    </svg>`;
  }
  // shroud
  const col = type==='Attack' ? '#a569bd' : type==='Defense' ? '#5dade2' : '#7ee0a0';
  return `<svg class="card-frame-svg" viewBox="0 0 200 168" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
    <defs><linearGradient id="gS${type}" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#1a1026"/><stop offset="100%" stop-color="#0a0c14"/></linearGradient></defs>
    <rect x="2" y="2" width="196" height="164" rx="12" fill="url(#gS${type})" stroke="#4a3a6a" stroke-width="1.2"/>
    <rect x="7" y="7" width="186" height="154" rx="10" fill="none" stroke="${col}" stroke-width="1" opacity=".45"/>
    <!-- dagger corners -->
    <path d="M 12 12 L 22 12 L 12 22 Z M 188 12 L 178 12 L 188 22 Z M 12 156 L 22 156 L 12 146 Z M 188 156 L 178 156 L 188 146 Z" fill="#7a5ea7" opacity=".7"/>
    <path d="M 100 8 L 104 14 L 100 20 L 96 14 Z" fill="#c9a86a" opacity=".9"/>
    <rect x="70" y="24" width="60" height="42" rx="6" fill="rgba(165,105,189,.07)" stroke="rgba(165,105,189,.14)"/>
  </svg>`;
}

function backSVG(cls){
  if(cls==='bastion'){
    return `<svg viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:100%">
      <rect width="200" height="120" rx="10" fill="#1a2332"/><rect x="6" y="6" width="188" height="108" rx="8" fill="none" stroke="#5dade2" stroke-width="1.2" opacity=".5"/>
      <circle cx="100" cy="60" r="28" fill="none" stroke="#c9a86a" stroke-width="1.4"/><circle cx="100" cy="60" r="20" fill="none" stroke="rgba(201,168,106,.4)" stroke-width=".8"/>
      <path d="M 88 48 L 100 36 L 112 48 L 108 72 L 92 72 Z" fill="#9fb0c3" stroke="#5a6a7e" stroke-width="1"/><rect x="96" y="56" width="8" height="14" rx="2" fill="#2a3442"/>
      <text x="100" y="102" text-anchor="middle" font-family="Cinzel" font-size="7" fill="#c9a86a" letter-spacing="2">BASTION</text>
    </svg>`;
  }
  if(cls==='wither'){
    return `<svg viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:100%">
      <rect width="200" height="120" rx="10" fill="#1f1410"/><rect x="6" y="6" width="188" height="108" rx="8" fill="none" stroke="#e74c3c" stroke-width="1.2" opacity=".5"/>
      <g opacity=".9"><circle cx="100" cy="58" r="26" fill="none" stroke="#e67e22" stroke-width="1.2"/><path d="M 100 32 C 112 38, 118 52, 100 74 C 82 52, 88 38, 100 32 Z" fill="#c0392b" stroke="#ff9a3c" stroke-width=".8"/><circle cx="100" cy="54" r="6" fill="#ffd27a"/></g>
      <path d="M 40 20 Q 60 30 40 44 M 160 20 Q 140 30 160 44 M 40 100 Q 60 90 40 76 M 160 100 Q 140 90 160 76" stroke="#7a3a1a" stroke-width="1.2" fill="none" opacity=".7"/>
      <text x="100" y="102" text-anchor="middle" font-family="Cinzel" font-size="7" fill="#e67e22" letter-spacing="2">WITHER</text>
    </svg>`;
  }
  return `<svg viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:100%">
    <rect width="200" height="120" rx="10" fill="#140f1f"/><rect x="6" y="6" width="188" height="108" rx="8" fill="none" stroke="#a569bd" stroke-width="1.2" opacity=".5"/>
    <path d="M 100 28 L 106 54 L 100 84 L 94 54 Z" fill="#c9a86a" stroke="#8c6b2e" stroke-width=".8"/><path d="M 94 40 L 106 40 L 102 46 L 98 46 Z" fill="#2a1a3a"/>
    <circle cx="100" cy="60" r="24" fill="none" stroke="rgba(165,105,189,.5)" stroke-width=".8" stroke-dasharray="4 3"/><circle cx="100" cy="60" r="30" fill="none" stroke="rgba(165,105,189,.2)" stroke-width=".6"/>
    <text x="100" y="102" text-anchor="middle" font-family="Cinzel" font-size="7" fill="#a569bd" letter-spacing="2">SHROUD</text>
  </svg>`;
}

function enemySVG(type){
  if(type==='boss1') return `<g><ellipse cx="100" cy="165" rx="58" ry="10" fill="rgba(0,0,0,.5)"/><rect x="62" y="38" width="76" height="98" rx="14" fill="#3a4555" stroke="#8a9bb0" stroke-width="3"/><rect x="72" y="52" width="56" height="56" rx="8" fill="#1a2332" stroke="#5dade2" stroke-width="1.5"/><circle cx="86" cy="72" r="7" fill="#ff3b3b"/><circle cx="114" cy="72" r="7" fill="#ff3b3b"/><rect x="88" y="86" width="24" height="6" rx="3" fill="#c9a86a"/><rect x="46" y="70" width="22" height="46" rx="6" fill="#2a3442" stroke="#8a9bb0" stroke-width="2"/><rect x="132" y="70" width="22" height="46" rx="6" fill="#2a3442" stroke="#8a9bb0" stroke-width="2"/><path d="M 100 14 L 112 26 L 100 38 L 88 26 Z" fill="#c9a86a" stroke="#8c6b2e" stroke-width="1.5"/></g>`;
  if(type==='boss2') return `<g><ellipse cx="100" cy="165" rx="62" ry="11" fill="rgba(0,0,0,.55)"/><path d="M 60 50 C 60 30, 140 30, 140 50 L 130 130 L 70 130 Z" fill="#2a0f1f" stroke="#a569bd" stroke-width="2.5"/><circle cx="100" cy="72" r="28" fill="#1a0a14" stroke="#c0392b" stroke-width="2"/><circle cx="88" cy="70" r="8" fill="#ff3b3b"/><circle cx="112" cy="70" r="8" fill="#ff3b3b"/><path d="M 86 88 Q 100 102 114 88" stroke="#ff9a7a" stroke-width="2.5" fill="none" stroke-linecap="round"/><path d="M 70 20 L 100 8 L 130 20 L 122 36 L 78 36 Z" fill="#4a1a3a" stroke="#a569bd" stroke-width="1.5"/></g>`;
  if(type==='boss3') return `<g><ellipse cx="100" cy="165" rx="66" ry="12" fill="rgba(120,20,120,.25)"/><circle cx="100" cy="78" r="46" fill="#0f0a1f" stroke="#7b5ea7" stroke-width="3"/><circle cx="100" cy="78" r="34" fill="none" stroke="rgba(165,105,189,.4)" stroke-width="1" stroke-dasharray="6 4"/><circle cx="100" cy="78" r="12" fill="#a569bd" opacity=".9"/><circle cx="100" cy="78" r="6" fill="#ffd27a"/><path d="M 100 18 L 108 36 L 100 32 L 92 36 Z" fill="#c9a86a"/><path d="M 40 70 Q 20 78 40 90 M 160 70 Q 180 78 160 90" stroke="#7b5ea7" stroke-width="2" fill="none" opacity=".6"/></g>`;
  if(type==='f2') return `<g><ellipse cx="100" cy="160" rx="48" ry="9" fill="rgba(0,0,0,.45)"/><rect x="70" y="48" width="60" height="78" rx="12" fill="#2a1a2a" stroke="#8c2a2a" stroke-width="2"/><circle cx="100" cy="68" r="22" fill="#3a1a1a" stroke="#c0392b" stroke-width="1.5"/><circle cx="92" cy="66" r="5" fill="#ff6b6b"/><circle cx="108" cy="66" r="5" fill="#ff6b6b"/><rect x="82" y="78" width="36" height="4" rx="2" fill="#5a1a1a"/><rect x="54" y="74" width="20" height="36" rx="6" fill="#1a0f0f" stroke="#5a1a1a" stroke-width="1.5"/><rect x="126" y="74" width="20" height="36" rx="6" fill="#1a0f0f" stroke="#5a1a1a" stroke-width="1.5"/></g>`;
  if(type==='f3') return `<g><ellipse cx="100" cy="160" rx="50" ry="9" fill="rgba(0,0,0,.5)"/><path d="M 68 44 L 132 44 L 122 132 L 78 132 Z" fill="#0f1a2a" stroke="#3a5a7a" stroke-width="2"/><circle cx="100" cy="66" r="20" fill="#1a2a3a" stroke="#5dade2" stroke-width="1.5"/><circle cx="93" cy="64" r="4.5" fill="#7ec8ff"/><circle cx="107" cy="64" r="4.5" fill="#7ec8ff"/><path d="M 90 76 Q 100 84 110 76" stroke="#5dade2" stroke-width="1.8" fill="none" stroke-linecap="round"/><rect x="58" y="68" width="16" height="40" rx="5" fill="#0a1420" stroke="#3a5a7a" stroke-width="1.2"/><rect x="126" y="68" width="16" height="40" rx="5" fill="#0a1420" stroke="#3a5a7a" stroke-width="1.2"/></g>`;
  return `<g><ellipse cx="100" cy="160" rx="44" ry="8" fill="rgba(0,0,0,.45)"/><circle cx="100" cy="72" r="26" fill="#2a2f1a" stroke="#7a8a3a" stroke-width="2"/><circle cx="91" cy="68" r="5" fill="#ffcc3a"/><circle cx="109" cy="68" r="5" fill="#ffcc3a"/><ellipse cx="100" cy="82" rx="8" ry="5" fill="#1a1f0a"/><rect x="74" y="96" width="52" height="42" rx="10" fill="#3a3a2a" stroke="#6a6a4a" stroke-width="1.5"/><rect x="56" y="88" width="18" height="30" rx="6" fill="#2a2a1a"/><rect x="126" y="88" width="18" height="30" rx="6" fill="#2a2a1a"/></g>`;
}

// State
let state = {
  cls:'bastion',
  hp:35, maxHp:35, energy:3, maxEnergy:5, handSize:4, gold:0,
  defense:0, permDefense:0, tempDefense:0,
  floor:1, room:1, // room 1-4 per floor (4=boss)
  inventory:{}, deck:[], hand:[], discard:[],
  enemy:null,
  statuses:{ burn:0, poison:0, stun:0, evade:0, silence:0 },
  enemyStatuses:{ burn:0, poison:0, stun:0 },
  log:[],
  filter:'all'
};

function shopCost(cardId){
  const cnt = state.inventory[cardId]||0;
  if(cnt>=5) return 999;
  if(cnt>=3) return cnt===3 ? 10 : 15;
  return 5; // base 5 to make economy meaningful; GDD says 1 but scaled for playability
}

function initGame(cls){
  state.cls=cls;
  const c = CLASSES[cls];
  state.maxHp=c.hp; state.hp=c.hp; state.energy=c.energy; state.maxEnergy=c.maxEnergy; state.handSize=c.handSize;
  state.gold=0; state.defense=0; state.permDefense=0; state.tempDefense=0; state.floor=1; state.room=1;
  state.inventory={}; state.deck=[]; state.hand=[]; state.discard=[]; state.statuses={burn:0,poison:0,stun:0,evade:0,silence:0}; state.enemyStatuses={burn:0,poison:0,stun:0};
  const starter = starterDeck(cls);
  starter.forEach(id=>{ state.inventory[id]=(state.inventory[id]||0)+1; state.deck.push(id); });
  // shuffle deck
  shuffle(state.deck);
  logClear(); log(`Awakened as <b>${c.name}</b> — ${c.subtitle}. The Void awaits.`);
  startRoom();
  updateAll();
}

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]]; } }

function makeEnemy(){
  const isBoss = state.room===4;
  if(isBoss){
    if(state.floor===1) return { name:'The Gatekeeper', hp:80, maxHp:80, dmg:15, type:'boss1', special:'Blocks 5 dmg/turn until 20 HP', defense:5 };
    if(state.floor===2) return { name:'The Corrupted', hp:80, maxHp:80, dmg:15, type:'boss2', special:'Deals 20 if you have <3 Energy', defense:0 };
    return { name:'The Void Lord', hp:80, maxHp:80, dmg:15, type:'boss3', special:'Regens 10 HP/turn • Cannot block', defense:0, regen:10 };
  }
  if(state.floor===1) return { name:['Goblin Scout','Void Rat','Hollow Thrall'][Math.floor(Math.random()*3)], hp:20, maxHp:20, dmg:3, type:'f1', defense:0 };
  if(state.floor===2) return { name:['Vampire Knight','Ogre Brute','Dusk Cultist'][Math.floor(Math.random()*3)], hp:30, maxHp:30, dmg:5, type:'f2', defense:0 };
  return { name:['Void Warrior','Cultist Lord','Abyssal Knight'][Math.floor(Math.random()*3)], hp:40, maxHp:40, dmg:8, type:'f3', defense:0 };
}

function startRoom(){
  state.enemy = makeEnemy();
  state.enemyStatuses={burn:0,poison:0,stun:0};
  state.tempDefense=0;
  // draw initial hand
  state.hand=[]; state.discard=[];
  // ensure deck has cards
  if(state.deck.length<4){ state.deck.push(...state.discard); state.discard=[]; shuffle(state.deck); }
  drawCards(state.handSize);
  document.getElementById('btnNextRoom').style.display='none';
  document.getElementById('btnEndTurn').style.display='';
  log(`<b>Floor ${state.floor} — Room ${state.room}/4</b> • Encounter: <b>${state.enemy.name}</b> (${state.enemy.hp} HP, ${state.enemy.dmg} ATK)`);
  if(state.enemy.special) log(`Boss mechanic: ${state.enemy.special}`);
}

function drawCards(n){
  for(let i=0;i<n;i++){
    if(state.hand.length>=5) break;
    if(state.deck.length===0){
      if(state.discard.length===0) break;
      state.deck.push(...state.discard); state.discard=[]; shuffle(state.deck);
    }
    const id = state.deck.pop();
    if(id) state.hand.push(id);
  }
}

function playCard(index){
  const id = state.hand[index];
  const card = CARDS[id];
  if(!card) return;
  if(state.energy < card.cost){ toast(`Not enough Energy (${state.energy}/${card.cost})`); return; }
  if(state.enemy && state.enemy.hp<=0) return;
  state.energy -= card.cost;
  // remove from hand to discard after resolution
  state.hand.splice(index,1);
  state.discard.push(id);

  // resolve
  let dmg = card.atk;
  let healed = 0;
  let gainedDef = card.def;

  if(card.type==='Attack'){
    // special: SH-A01 +1 if no armor
    if(card.id==='SH-A01' && (state.enemy.defense||0)===0) dmg+=1;
    // apply block on enemy (Gatekeeper)
    let effective = dmg;
    if(state.enemy.defense) effective = Math.max(0, dmg - state.enemy.defense);
    // SH-A02 and SH-U04 unblockable handled via ignoring defense
    if(card.id==='SH-A02' || card.id==='SH-U04') effective = dmg;
    state.enemy.hp = Math.max(0, state.enemy.hp - effective);
    spawnDamage(effective);
    log(`<span class="dmg">▶ ${card.name}</span> hits for <b>${effective}</b>${effective!==dmg?` <span style="color:var(--muted)">(${dmg}−${dmg-effective} block)</span>`:''}.`);
    // status applies
    if(card.id==='WI-A01'){ state.enemyStatuses.burn=(state.enemyStatuses.burn||0)+1; log(`Burn +1 on enemy.`); }
    if(card.id==='WI-A02'){ state.enemyStatuses.burn=(state.enemyStatuses.burn||0)+3; log(`Burn +3 on enemy.`); }
    if(card.id==='WI-A03'){ state.enemyStatuses.burn=(state.enemyStatuses.burn||0)+1; }
    if(card.id==='WI-U02'){ state.enemyStatuses.burn=(state.enemyStatuses.burn||0)+2; state.enemy.hp=Math.max(0,state.enemy.hp-3); log(`Burn deals 3 + 2 Burn stacks.`); }
    if(card.id==='WI-U01'){ state.enemyStatuses.poison=(state.enemyStatuses.poison||0)+1; log(`Poison +1.`); }
    if(card.id==='WI-A04'){ state.enemyStatuses.burn=(state.enemyStatuses.burn||0)+1; }
    if(card.id==='BA-A01' && state.hp>=30){ drawCards(1); log(`Slam: 30+ HP — drew 1 card.`); }
    if(card.id==='BA-A02'){ /* already AoE single enemy */ }
    if(card.id==='SH-A04'){ state.energy=Math.min(state.maxEnergy, state.energy+2); log(`Vengeance: +2 Energy.`); }
    if(card.id==='SH-U02'){ state.tempDefense+=1; log(`Teleport: +1 Defense.`); state.enemy.hp=Math.max(0,state.enemy.hp-2); }
  } else if(card.type==='Defense'){
    if(card.id==='BA-D02'){ state.permDefense+=5; state.tempDefense+=5; log(`<span class="block">🛡️ Iron Skin</span> +5 permanent Defense. Discarded 1 card.`); if(state.hand.length>0){ const disc = state.hand.pop(); state.discard.push(disc); } }
    else { state.tempDefense+=gainedDef; log(`<span class="block">🛡️ ${card.name}</span> +${gainedDef} Defense.`); }
    if(card.id==='BA-D04'){ state.enemyStatuses.stun=1; log(`Taunt: enemy Stunned 1 turn.`); }
    if(card.id==='WI-D02'){ state.energy=Math.min(state.maxEnergy, state.energy+1); log(`Heat Resist: +1 Energy.`); }
    if(card.id==='WI-D04'){ healed=2; }
    if(card.id==='SH-D02'){ state.energy=Math.min(state.maxEnergy, state.energy+1); log(`Step: +1 Energy.`); }
    if(card.id==='SH-D01'){ state.statuses.evade=1; log(`Blur: next attack ignored.`); }
    if(card.id==='SH-D03'){ state.enemyStatuses.stun=1; log(`Evasion: enemy action skipped.`); }
  } else if(card.type==='Other'){
    if(card.id==='BA-U01'){ drawCards(1); log(`Search: drew 1 card.`); }
    if(card.id==='BA-U02'){ healed=6; log(`<span class="heal">Repair: healed 6 HP.</span>`); }
    if(card.id==='BA-U03'){ drawCards(1); state.energy=Math.min(state.maxEnergy, state.energy+1); log(`Rally: drew 1, +1 Energy.`); }
    if(card.id==='BA-U04'){ state.gold+=5; log(`<span class="heal">Loot: +5 Gold.</span>`); }
    if(card.id==='WI-U03'){ drawCards(1); state.enemyStatuses.stun=1; log(`Slow: drew 1, Stunned enemy.`); }
    if(card.id==='WI-U04'){ healed=5; state.enemy.dmg=Math.max(1, state.enemy.dmg-1); log(`Wither: healed 5, enemy ATK -1.`); }
    if(card.id==='SH-U01'){ state.energy=Math.min(state.maxEnergy, state.energy+1); state.enemyStatuses.stun=1; log(`Dash: +1 Energy, enemy skipped.`); }
    if(card.id==='SH-U03'){ state.tempDefense+=1; drawCards(2); if(state.hand.length>0){ const disc=state.hand.pop(); state.discard.push(disc); log(`Hide: +1 Def, drew 2, discarded 1.`);} }
    if(card.id==='SH-U04'){ state.enemy.hp=Math.max(0, state.enemy.hp-8); spawnDamage(8); log(`<span class="dmg">Assassinate: 8 unblockable damage!</span>`); }
    if(card.id==='WI-U02'){ /* already */ }
    if(card.id==='SH-U02'){ /* handled */ }
  }
  if(healed){ state.hp=Math.min(state.maxHp, state.hp+healed); log(`<span class="heal">Healed ${healed} HP → ${state.hp}/${state.maxHp}</span>`); }
  // check enemy death
  if(state.enemy.hp<=0){
    const gold = state.room===4 ? 15 : 5;
    state.gold+=gold;
    log(`<b style="color:#ffd27a">★ ${state.enemy.name} defeated! +${gold} Gold.</b>`);
    document.getElementById('btnNextRoom').style.display='';
    document.getElementById('btnEndTurn').style.display='none';
    // clear stun etc
  }
  updateAll();
}

function enemyTurn(){
  if(!state.enemy || state.enemy.hp<=0) return;
  // tick burn/poison on enemy at start of enemy turn
  if(state.enemyStatuses.burn>0){ const dmg=state.enemyStatuses.burn; state.enemy.hp=Math.max(0,state.enemy.hp-dmg); log(`<span class="dmg">Burn</span> ticks ${dmg} on ${state.enemy.name}.`); }
  if(state.enemyStatuses.poison>0){ const dmg=state.enemyStatuses.poison; state.enemy.hp=Math.max(0,state.enemy.hp-dmg); log(`<span style="color:#7ee0a0">Poison</span> ticks ${dmg} on ${state.enemy.name}.`); }
  if(state.enemy.hp<=0){ const gold=state.room===4?15:5; state.gold+=gold; log(`<b style="color:#ffd27a">★ ${state.enemy.name} succumbed to DoT! +${gold} Gold.</b>`); document.getElementById('btnNextRoom').style.display=''; document.getElementById('btnEndTurn').style.display='none'; updateAll(); return; }

  // stun check
  if(state.enemyStatuses.stun>0){ log(`Enemy is <b>Stunned</b> — no attack.`); state.enemyStatuses.stun--; updateAll(); return; }
  if(state.statuses.evade>0){ log(`<span class="block">Blur/Evasion</span> — enemy attack ignored.`); state.statuses.evade=0; updateAll(); return; }

  // boss mechanics
  let dmg = state.enemy.dmg;
  if(state.enemy.type==='boss2' && state.energy<3){ dmg=20; log(`<b style="color:#ff6b6b">The Corrupted surges! 20 damage (you had <3 Energy)</b>`); }
  // Void Lord regen
  if(state.enemy.type==='boss3'){ state.enemy.hp=Math.min(state.enemy.maxHp, state.enemy.hp+10); log(`<span style="color:#a569bd">Void Lord regenerates +10 HP → ${state.enemy.hp}</span>`); }

  // player block
  const totalDef = state.permDefense + state.tempDefense;
  let taken = Math.max(0, dmg - totalDef);
  // consume temp defense first
  if(totalDef>0){
    log(`Incoming ${dmg} − ${totalDef} Defense = <b>${taken}</b> damage taken.`);
    // temp defense is consumed
    if(state.tempDefense>=dmg) state.tempDefense-=dmg;
    else { const rem = dmg - state.tempDefense; state.tempDefense=0; /* perm stays */ }
  } else {
    log(`Enemy attacks for <b>${taken}</b> damage.`);
  }
  if(taken>0){
    state.hp=Math.max(0, state.hp-taken);
    document.getElementById('enemySprite').classList.remove('hit'); void document.getElementById('enemySprite').offsetWidth; document.getElementById('enemySprite').classList.add('hit');
    // flash player hp
  }
  // Gatekeeper defense refresh handled via temp? Keep 5 block per turn until 20 HP
  if(state.enemy.type==='boss1' && state.enemy.hp>=20){ state.enemy.defense=5; } else if(state.enemy.type==='boss1'){ state.enemy.defense=0; }

  if(state.hp<=0){ log(`<b style="color:#ff6b6b">☠ You have fallen. The Void consumes Aethelgard.</b>`); setTimeout(()=>{ alert('Game Over — You died. Restarting...'); initGame(state.cls); }, 600); }
}

function endTurn(){
  if(state.enemy && state.enemy.hp<=0){ toast('Advance to next room'); return; }
  enemyTurn();
  // regen energy
  state.energy = Math.min(state.maxEnergy, state.energy+1);
  // temp defense decays (except perm)
  // keep perm, reset temp to 0 (or keep if Iron Skin? we kept perm separate)
  state.tempDefense = 0;
  // draw up to hand size
  const need = state.handSize - state.hand.length;
  if(need>0) drawCards(need);
  // burn/poison on player? Not implemented as player DoT from enemies; could add
  updateAll();
}

function nextRoom(){
  if(state.enemy.hp>0) return;
  if(state.floor===3 && state.room===4){
    log(`<b style="color:#ffd27a">★★★ VICTORY — Void Lord slain! Aethelgard is reclaimed. ★★★</b>`);
    toast('Victory! Returning to menu...');
    setTimeout(()=>{ document.getElementById('overlayClass').classList.add('open'); }, 1200);
    return;
  }
  state.room++;
  if(state.room>4){ state.floor++; state.room=1; log(`<b>— Ascended to Floor ${state.floor} —</b>`); }
  startRoom(); updateAll();
}

// UI
function cardHTML(id, interactive=true, shopMode=false){
  const c = CARDS[id];
  const costBg = c.cost===0 ? '#2ecc71' : c.cost<=2 ? '#c9a86a' : '#e74c3c';
  const typeCol = c.type==='Attack' ? 'rgba(231,76,60,.9)' : c.type==='Defense' ? 'rgba(93,173,226,.9)' : 'rgba(165,105,189,.9)';
  const canPlay = state.energy >= c.cost && state.enemy && state.enemy.hp>0;
  return `<div class="card ${!canPlay && interactive ? 'disabled':''}" data-id="${id}" style="${shopMode?'height:112px':''}">
    <div class="card-bg">${frameSVG(c.cls, c.type)}</div>
    <div class="card-inner">
      <div class="card-top">
        <div class="card-cost" style="background:${costBg}">${c.cost}</div>
        <div class="card-type" style="background:${typeCol}">${c.type}</div>
      </div>
      <div class="card-name">${c.name}</div>
      <div class="card-ability">${c.ability}</div>
      <div class="card-stats">
        ${c.atk?`<span class="stat-badge" style="background:rgba(231,76,60,.15);color:#ff9a7a;border-color:rgba(231,76,60,.25)">⚔ ${c.atk}</span>`:''}
        ${c.def?`<span class="stat-badge" style="background:rgba(93,173,226,.15);color:#8ec8ff;border-color:rgba(93,173,226,.25)">🛡 ${c.def}</span>`:''}
        ${!c.atk&&!c.def?`<span class="stat-badge" style="background:rgba(165,105,189,.12);color:#d2b4de">✦ Other</span>`:''}
      </div>
      <div class="card-id">${c.id} • ${c.cls.toUpperCase()}</div>
    </div>
  </div>`;
}

function updateAll(){
  const cls = CLASSES[state.cls];
  document.getElementById('goldVal').textContent = state.gold;
  document.getElementById('shopGold').textContent = state.gold;
  document.getElementById('floorVal').textContent = `${state.floor} — ${state.room}/4`;
  document.getElementById('handInfo').textContent = `${state.hand.length} / ${state.handSize}`;
  document.getElementById('deckCount').textContent = `Deck: ${state.deck.length} • Discard: ${state.discard.length}`;
  document.getElementById('hpVal').textContent = `${state.hp} / ${state.maxHp}`;
  document.getElementById('playerHpFill').style.width = `${Math.max(0, state.hp/state.maxHp*100)}%`;
  document.getElementById('defVal').textContent = `${state.permDefense + state.tempDefense}`;
  document.getElementById('defFill').style.width = `${Math.min(100, (state.permDefense+state.tempDefense)*12)}%`;
  document.getElementById('energyVal').textContent = `${state.energy} / ${state.maxEnergy}`;
  document.getElementById('heroName').textContent = cls.name;
  document.getElementById('heroClass').textContent = cls.subtitle;
  document.getElementById('heroStatsLine').textContent = `${state.maxHp} HP • ${state.maxEnergy} Energy • Hand ${state.handSize}`;
  document.getElementById('heroAvatar').innerHTML = backSVG(state.cls);
  // energy orbs
  const orbs = document.getElementById('energyOrbs'); orbs.innerHTML='';
  for(let i=0;i<state.maxEnergy;i++){
    const on = i < state.energy;
    const d = document.createElement('div'); d.className='orb '+(on?'on':'empty'); d.textContent='◈'; orbs.appendChild(d);
  }
  // floor track
  const track = document.getElementById('floorTrack'); track.innerHTML='';
  for(let f=1;f<=3;f++){
    for(let r=1;r<=4;r++){
      const isBoss = r===4;
      const dot=document.createElement('div'); dot.className='dot '+(isBoss?'boss':'')+ ((f<state.floor || (f===state.floor && r<state.room)) ? ' on' : (f===state.floor && r===state.room ? ' on' : ''));
      dot.title=`Floor ${f} Room ${r}${isBoss?' Boss':''}`; track.appendChild(dot);
    }
    if(f<3){ const sep=document.createElement('span'); sep.style.cssText='width:10px;height:1px;background:rgba(201,168,106,.2)'; track.appendChild(sep); }
  }
  // enemy
  if(state.enemy){
    document.getElementById('enemyName').textContent = state.enemy.name;
    document.getElementById('enemyMeta').textContent = state.enemy.type.startsWith('boss') ? 'BOSS' : `Floor ${state.floor} Enemy`;
    document.getElementById('enemyHpText').textContent = `${Math.max(0,state.enemy.hp)}/${state.enemy.maxHp}`;
    document.getElementById('enemyAtk').textContent = state.enemy.dmg;
    document.getElementById('enemySpecial').textContent = state.enemy.special ? '• '+state.enemy.special : '';
    document.getElementById('enemyHpFill').style.width = `${Math.max(0, state.enemy.hp/state.enemy.maxHp*100)}%`;
    document.getElementById('enemyHpFill').className = 'hp-fill '+(state.enemy.type.startsWith('boss')?'boss':'');
    document.getElementById('enemySprite').innerHTML = enemySVG(state.enemy.type);
    document.getElementById('roomLabel').textContent = `Floor ${state.floor} — Room ${state.room}/4 • ${state.enemy.name}`;
    // statuses
    const es = document.getElementById('enemyStatus'); es.innerHTML='';
    if(state.enemyStatuses.burn) es.innerHTML+=`<span class="status burn">🔥 Burn ${state.enemyStatuses.burn}</span>`;
    if(state.enemyStatuses.poison) es.innerHTML+=`<span class="status poison">☠ Poison ${state.enemyStatuses.poison}</span>`;
    if(state.enemyStatuses.stun) es.innerHTML+=`<span class="status stun">◎ Stunned</span>`;
    if(state.enemy.defense) es.innerHTML+=`<span class="status block">🛡 Block ${state.enemy.defense}</span>`;
    const ps = document.getElementById('playerStatus'); ps.innerHTML='';
    if(state.permDefense||state.tempDefense) ps.innerHTML+=`<span class="status block">🛡 Defense ${state.permDefense+state.tempDefense}${state.permDefense?' ('+state.permDefense+' perm)':''}</span>`;
    if(state.statuses.evade) ps.innerHTML+=`<span class="status stun">◐ Evade</span>`;
  }
  // hand
  const hg=document.getElementById('handGrid'); hg.innerHTML='';
  state.hand.forEach((id, idx)=>{
    const wrap=document.createElement('div'); wrap.innerHTML=cardHTML(id, true, false);
    const el=wrap.firstElementChild; el.addEventListener('click',()=>playCard(idx)); hg.appendChild(el);
  });
  if(state.hand.length===0) hg.innerHTML=`<div style="grid-column:1/-1;padding:18px;text-align:center;color:var(--muted);font-size:12px;border:1px dashed rgba(201,168,106,.15);border-radius:12px">No cards in hand — End Turn to draw.</div>`;
  // deck chips
  const chips=document.getElementById('deckChips'); chips.innerHTML='';
  Object.entries(state.inventory).sort((a,b)=>a[0].localeCompare(b[0])).forEach(([id,cnt])=>{
    const chip=document.createElement('span'); chip.className='deck-chip'; chip.textContent=`${id} ×${cnt}`; chips.appendChild(chip);
  });
  // quick shop (3 random)
  const qs=document.getElementById('quickShop'); qs.innerHTML='';
  const allIds = Object.keys(CARDS);
  // show 4 shop cards sorted by class relevance
  const sorted = [...allIds].sort((a,b)=>{
    const ca=CARDS[a], cb=CARDS[b];
    if(ca.cls===state.cls && cb.cls!==state.cls) return -1;
    if(cb.cls===state.cls && ca.cls!==state.cls) return 1;
    return a.localeCompare(b);
  }).slice(0,4);
  sorted.forEach(id=>{
    const cost=shopCost(id);
    const wrap=document.createElement('div'); wrap.style.position='relative';
    wrap.innerHTML=cardHTML(id,false,true)+`<div class="shop-price">${cost>=999?'MAX':cost+'◈'}</div>`;
    wrap.style.cursor='pointer';
    wrap.addEventListener('click',()=>buyCard(id));
    if(cost>state.gold || cost>=999) wrap.style.opacity='.55';
    qs.appendChild(wrap);
  });
  // full shop grid
  renderShop();
  // deck view
  renderDeckView();
}

function renderShop(){
  const grid=document.getElementById('shopGrid'); grid.innerHTML='';
  const filter = state.filter;
  Object.values(CARDS).filter(c=> filter==='all' || c.type===filter).forEach(c=>{
    const cost=shopCost(c.id);
    const owned=state.inventory[c.id]||0;
    const canBuy = cost<=state.gold && owned<5;
    const el=document.createElement('div'); el.style.position='relative'; el.style.opacity = canBuy? '1' : '.6';
    el.innerHTML = cardHTML(c.id,false,true)+`<div class="shop-price">${cost>=999?'MAX':cost+'◈'}</div>
      <div style="padding:8px 10px;display:flex;justify-content:space-between;align-items:center;gap:6px">
        <span style="font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--muted)">Owned ×${owned}/5</span>
        <button class="btn small ${canBuy?'primary':''}" ${canBuy?'':'disabled'}>${canBuy?'Buy':'No Gold'}</button>
      </div>`;
    el.querySelector('button').addEventListener('click',()=>buyCard(c.id));
    grid.appendChild(el);
  });
}

function renderDeckView(){
  const view=document.getElementById('deckView'); view.innerHTML='';
  const entries = Object.entries(state.inventory).sort((a,b)=>a[0].localeCompare(b[0]));
  if(entries.length===0){ view.innerHTML='<div style="color:var(--muted)">Empty deck</div>'; return; }
  entries.forEach(([id,cnt])=>{
    for(let i=0;i<cnt;i++){
      const w=document.createElement('div'); w.innerHTML=cardHTML(id,false,false); view.appendChild(w.firstElementChild);
    }
  });
  // also show draw pile order preview
  const info=document.createElement('div'); info.style.cssText='grid-column:1/-1;padding:10px;border:1px solid rgba(201,168,106,.12);border-radius:10px;background:rgba(0,0,0,.2);font-family:JetBrains Mono,monospace;font-size:11px;color:var(--muted)';
  info.textContent=`Draw pile: ${state.deck.length} cards • Hand: ${state.hand.length} • Discard: ${state.discard.length} • Total cards: ${Object.values(state.inventory).reduce((a,b)=>a+b,0)}`;
  view.prepend(info);
}

function buyCard(id){
  const cost=shopCost(id);
  if(cost>=999){ toast('Max 5 copies reached'); return; }
  if(state.gold < cost){ toast(`Need ${cost} Gold (you have ${state.gold})`); return; }
  state.gold-=cost;
  state.inventory[id]=(state.inventory[id]||0)+1;
  state.discard.push(id); // goes to discard, will be drawn later
  log(`<b style="color:#ffd27a">Shop:</b> Bought <b>${CARDS[id].name}</b> (${id}) for ${cost} Gold.`);
  toast(`Bought ${CARDS[id].name} for ${cost} Gold`);
  updateAll();
}

function spawnDamage(val){
  const layer=document.getElementById('floatingLayer');
  const el=document.createElement('div'); el.className='floating-dmg'; el.textContent='-'+val;
  layer.appendChild(el); setTimeout(()=>el.remove(),900);
}

function log(msg){
  state.log.push(msg);
  const el=document.getElementById('combatLog');
  el.innerHTML = state.log.slice(-40).map(m=>`<div>${m}</div>`).join('');
  el.scrollTop = el.scrollHeight;
}
function logClear(){ state.log=[]; document.getElementById('combatLog').innerHTML=''; }
function toast(msg){
  const t=document.getElementById('toast'); t.textContent=msg; t.classList.add('show'); clearTimeout(t._tm); t._tm=setTimeout(()=>t.classList.remove('show'),2200);
}

// Previews
function renderPreviews(){
  document.getElementById('previewBastion').innerHTML = frameSVG('bastion','Defense') + `<div style="position:absolute;inset:0;display:grid;place-items:center;font-family:Cinzel,serif;font-size:10px;color:#fff;letter-spacing:.1em">STONE FRAME</div>`;
  document.getElementById('previewBastionBack').innerHTML = backSVG('bastion');
  document.getElementById('previewWither').innerHTML = frameSVG('wither','Attack') + `<div style="position:absolute;inset:0;display:grid;place-items:center;font-family:Cinzel,serif;font-size:10px;color:#fff;letter-spacing:.1em">THORN FRAME</div>`;
  document.getElementById('previewWitherBack').innerHTML = backSVG('wither');
  document.getElementById('previewShroud').innerHTML = frameSVG('shroud','Other') + `<div style="position:absolute;inset:0;display:grid;place-items:center;font-family:Cinzel,serif;font-size:10px;color:#fff;letter-spacing:.1em">VEIL FRAME</div>`;
  document.getElementById('previewShroudBack').innerHTML = backSVG('shroud');
  // need positioning for preview frames
  ['previewBastion','previewWither','previewShroud'].forEach(id=>{
    const el=document.getElementById(id); el.style.position='relative';
  });
}

// Events
document.querySelectorAll('.class-card').forEach(c=>{
  c.addEventListener('click',()=>{
    document.querySelectorAll('.class-card').forEach(x=>x.classList.remove('selected'));
    c.classList.add('selected');
  });
});
document.getElementById('btnStart').addEventListener('click',()=>{
  const sel=document.querySelector('.class-card.selected'); const cls=sel?sel.dataset.class:'bastion';
  document.getElementById('overlayClass').classList.remove('open');
  initGame(cls);
});
document.getElementById('btnShop').addEventListener('click',()=>document.getElementById('overlayShop').classList.add('open'));
document.getElementById('btnOpenShop2').addEventListener('click',()=>document.getElementById('overlayShop').classList.add('open'));
document.getElementById('btnCloseShop').addEventListener('click',()=>document.getElementById('overlayShop').classList.remove('open'));
document.getElementById('overlayShop').addEventListener('click',e=>{ if(e.target.id==='overlayShop') e.currentTarget.classList.remove('open'); });
document.getElementById('btnViewDeck').addEventListener('click',()=>document.getElementById('overlayDeck').classList.add('open'));
document.getElementById('btnCloseDeck').addEventListener('click',()=>document.getElementById('overlayDeck').classList.remove('open'));
document.getElementById('overlayDeck').addEventListener('click',e=>{ if(e.target.id==='overlayDeck') e.currentTarget.classList.remove('open'); });
document.getElementById('btnEndTurn').addEventListener('click',endTurn);
document.getElementById('btnNextRoom').addEventListener('click',nextRoom);
document.getElementById('btnDraw').addEventListener('click',()=>{ if(state.hand.length<5){ drawCards(1); updateAll(); } else toast('Hand full (5 max)'); });
document.getElementById('btnRestart').addEventListener('click',()=>{ document.getElementById('overlayClass').classList.add('open'); });
document.querySelectorAll('[data-filter]').forEach(b=>{
  b.addEventListener('click',()=>{
    document.querySelectorAll('[data-filter]').forEach(x=>{ x.classList.add('ghost'); x.classList.remove('primary'); });
    b.classList.remove('ghost');
    state.filter=b.dataset.filter; renderShop();
  });
});
document.addEventListener('keydown',e=>{
  if(e.code==='Space'){ e.preventDefault(); if(document.getElementById('overlayClass').classList.contains('open')) return; if(state.enemy && state.enemy.hp>0) endTurn(); else nextRoom(); }
  if(e.key.toLowerCase()==='d' && !document.getElementById('overlayClass').classList.contains('open')){ drawCards(1); updateAll(); }
  if(e.key==='Escape'){ document.querySelectorAll('.overlay.open').forEach(o=>o.classList.remove('open')); }
});

// init previews and default state
renderPreviews();
updateAll();
</script>
</body>
</html>
<!-- agent-meta {"model":"meta/muse-spark-1.2-contributor","provider":"openrouter","persona":"full","sessionId":"ab9fc161-3154-4fde-b4c4-06ccee947ceb","tokensIn":360765,"tokensOut":28683,"tokensTotal":389448,"cost":0.018936862,"turns":10,"toolCalls":9,"failedToolCalls":0,"timestamp":"2026-08-23T02:26:02.456Z"} -->