← Deckbuilder results

Deckbuilder

muse spark 1.1 · composer

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 67,422 bytes · SHA-256 d35c29ee4472
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Shadows of Aethelgard</title>
<style>
/* ── css: base ── */
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;800&family=JetBrains+Mono:wght@400;600&family=Instrument+Serif:ital@0;1&display=swap');
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;background:#0a0a0f;overflow:hidden}
body{font-family:'JetBrains Mono',monospace;color:#d8d0c0;user-select:none}
#app{position:relative;width:100vw;height:100vh;overflow:hidden;background:radial-gradient(120% 80% at 50% 0%,#1c1a24 0%,#0f0e14 35%,#08080c 100%)}
#bg-orbs{position:absolute;inset:0;pointer-events:none;overflow:hidden}
.orb{position:absolute;border-radius:50%;filter:blur(60px);opacity:.35}
.o1{width:600px;height:600px;background:radial-gradient(circle,#2a1a3a,transparent 70%);top:-100px;left:10%}
.o2{width:500px;height:500px;background:radial-gradient(circle,#1a2a2a,transparent 70%);top:30%;right:-80px}
.o3{width:700px;height:700px;background:radial-gradient(circle,#1a1f2a,transparent 70%);bottom:-150px;left:30%}

.screen{position:absolute;inset:0;display:none;flex-direction:column}
.screen.active{display:flex}

.btn-metal{font-family:'JetBrains Mono',monospace;background:linear-gradient(180deg,#2a2a32,#1a1a22);border:1px solid #3a3a4a;color:#c0b8a8;padding:8px 14px;letter-spacing:.08em;font-size:11px;cursor:pointer;transition:.2s;text-transform:uppercase}
.btn-metal:hover{background:linear-gradient(180deg,#3a3a4a,#2a2a32);border-color:#5a5a6a;color:#fff;box-shadow:0 0 12px rgba(255,255,255,.1)}
.btn-metal.small{padding:6px 10px;font-size:10px}
.btn-ghost{background:transparent;border:1px solid #2a2a3a;color:#5a5a6a;padding:6px 10px;font-size:10px;cursor:pointer;text-transform:uppercase;letter-spacing:.08em}
.btn-ghost:hover{border-color:#5a5a6a;color:#888}
.btn-ritual{background:radial-gradient(120% 120% at 50% 0%,#2a2040,#1a1428);border:1px solid #4a3a6a;color:#c9b8ff;padding:12px 18px;font-family:'Cinzel',serif;font-weight:700;letter-spacing:.12em;font-size:12px;cursor:pointer;box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 0 20px rgba(120,80,200,.2)}
.btn-ritual:hover{border-color:#7a5abb;box-shadow:0 0 22px rgba(120,80,200,.4)}
.btn-ritual.large{padding:16px 28px;font-size:14px}
.btn-ritual:disabled{opacity:.35;pointer-events:none}

.chip{background:#1a1a24;border:1px solid #2a2a3a;color:#6a6a7a;padding:5px 10px;font-size:10px;cursor:pointer;border-radius:999px;letter-spacing:.06em}
.chip.active,.chip:hover{border-color:#8a7bb0;color:#c9b8ff;background:#231a38}

/* MENU */
#screen-menu{align-items:center;justify-content:center}
.menu-vignette{position:absolute;inset:0;background:radial-gradient(80% 60% at 50% 20%,transparent 30%,rgba(0,0,0,.6) 100%)}
.menu-content{position:relative;z-index:2;width:min(1100px,94vw);display:flex;flex-direction:column;align-items:center;gap:28px;padding:24px 0}
.title-block{text-align:center}
.title-rune{font-family:'Cinzel',serif;color:#5a4a6a;letter-spacing:.6em;font-size:12px;margin-bottom:8px}
.game-title{font-family:'Instrument Serif',serif;font-size:clamp(46px,9vw,86px);line-height:.88;letter-spacing:-.02em;color:#e9e0d0;font-weight:400}
.game-title span{font-style:italic;color:#8a7bb0;font-family:'Instrument Serif',serif}
.subtitle{font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.22em;color:#6a657a;margin-top:10px;text-transform:uppercase}
.class-select{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;width:100%;margin-top:8px}
.class-card{position:relative;background:linear-gradient(180deg,rgba(30,28,38,.9),rgba(14,14,20,.95));border:1px solid #2a2a3a;padding:18px 16px 16px;border-radius:14px;cursor:pointer;transition:.25s;overflow:hidden}
.class-card:before{content:'';position:absolute;inset:0;border-radius:14px;background:radial-gradient(120% 80% at 50% 0%,rgba(255,255,255,.06),transparent 50%);pointer-events:none}
.class-card:hover{transform:translateY(-4px);border-color:#5a4a7a;box-shadow:0 16px 40px rgba(0,0,0,.5),0 0 0 1px rgba(140,120,200,.2) inset}
.class-icon-wrap{width:64px;height:64px;margin:0 auto 12px;border-radius:12px;display:grid;place-items:center;background:radial-gradient(circle at 50% 30%,#2a2a3a,#0f0f14);border:1px solid #2a2a3a}
.class-card h3{font-family:'Cinzel',serif;font-size:14px;letter-spacing:.14em;text-align:center;color:#e9e0d0}
.class-meta{display:flex;gap:6px;justify-content:center;margin:8px 0 10px;flex-wrap:wrap}
.class-meta span{font-size:9px;padding:3px 6px;border-radius:999px;background:#1a1a24;border:1px solid #2a2a3a;letter-spacing:.08em}
.class-meta .tag{color:#8a7bb0;border-color:#3a2a5a}
.class-card p{font-size:11px;line-height:1.5;color:#807a8a;text-align:center;min-height:32px}
.starter-preview{display:flex;gap:5px;justify-content:center;margin-top:10px;flex-wrap:wrap}
.starter-preview span{font-size:9px;background:#15151d;border:1px solid #252530;padding:3px 6px;border-radius:4px;color:#8a8490}
.menu-hint{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:#4a4a5a}

/* TOPBAR */
.topbar{display:flex;justify-content:space-between;align-items:center;padding:10px 16px;background:linear-gradient(180deg,rgba(16,16,22,.9),rgba(10,10,14,.9));border-bottom:1px solid #1f1f2a;backdrop-filter:blur(8px);gap:10px}
.tb-group{display:flex;align-items:center;gap:12px}
.tb-item{display:flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.08em}
.tb-item i{font-style:normal;color:#c4a84a}
.tb-item.gold{color:#e7c76a;font-weight:600}
.tb-item.floor{color:#8a8490}
.floor-dots{display:flex;gap:4px;margin-left:8px}
.dot{width:7px;height:7px;border-radius:50%;background:#1f1f2a;border:1px solid #2a2a3a}
.dot.done{background:#4a3a6a;border-color:#6a5a8a}
.dot.active{background:#8a7bb0;box-shadow:0 0 6px #8a7bb0}
.boss-hp{display:flex;align-items:center;gap:10px}
.boss-label{font-size:9px;letter-spacing:.14em;color:#c47a7a}
.hp-track{width:160px;height:8px;background:#101018;border:1px solid #2a1a24;border-radius:999px;overflow:hidden;position:relative}
.hp-track.big{width:260px;height:12px}
.hp-fill{height:100%;background:linear-gradient(90deg,#c94a4a,#e07050);transition:width .4s ease}
.hp-fill.enemy{background:linear-gradient(90deg,#7a1a3a,#c44);}

/* BATTLEFIELD */
.battlefield{flex:1;display:grid;grid-template-columns:240px 1fr 190px;gap:12px;padding:14px}
.pane{background:linear-gradient(180deg,rgba(20,20,28,.6),rgba(12,12,18,.5));border:1px solid #1e1e2a;border-radius:12px;backdrop-filter:blur(6px);position:relative;overflow:hidden}
.player-pane,.energy-pane{display:flex;flex-direction:column;padding:10px}
.entity-card{padding:8px}
.entity-portrait{width:100%;height:84px;border-radius:8px;background:radial-gradient(120% 120% at 50% 30%,#2a2a3a,#101018);border:1px solid #2a2a3a;display:grid;place-items:center;position:relative;overflow:hidden}
.entity-portrait svg{width:90%;height:90%}
.stat-row{display:flex;align-items:center;gap:6px;margin-top:8px}
.sl{font-size:9px;width:22px;color:#5a5a6a;letter-spacing:.08em}
.bar{flex:1;height:14px;background:#0c0c12;border:1px solid #1e1e2a;border-radius:999px;position:relative;overflow:hidden}
.bar.thin{height:10px}
.fill{position:absolute;left:0;top:0;bottom:0;transition:width .4s}
.fill.hp{background:linear-gradient(90deg,#3a9a5a,#6ac47a)}
.fill.def{background:linear-gradient(90deg,#4a7ab0,#7ab4e0)}
.bar-txt{position:absolute;inset:0;display:grid;place-items:center;font-size:9px;letter-spacing:.06em;color:#e0d8c8;text-shadow:0 1px 2px #000}
.status-strip{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px;min-height:20px}
.status-chip{font-size:8px;padding:2px 5px;border-radius:999px;border:1px solid;background:#15151a;display:flex;align-items:center;gap:3px}
.status-chip.burn{color:#ffb84a;border-color:#6a4a1a;background:rgba(80,50,10,.4)}
.status-chip.poison{color:#7aff8a;border-color:#1a4a2a;background:rgba(20,60,20,.4)}
.status-chip.stun{color:#8ab4ff;border-color:#1a2a5a;background:rgba(20,30,80,.4)}
.status-chip.block{color:#8ad4ff;border-color:#1a3a5a;background:rgba(20,50,80,.4)}

.deck-info{display:flex;align-items:center;gap:8px;margin-top:auto;padding-top:10px;border-top:1px solid #1a1a24}
.deck-info .label{font-size:8px;letter-spacing:.14em;color:#4a4a5a}
.cardback-mini{width:34px;height:44px;border-radius:4px;background:linear-gradient(160deg,#1a1a2a,#0a0a12);border:1px solid #2a2a3a;display:grid;place-items:center;font-size:11px;color:#5a5a6a;position:relative;overflow:hidden}
.cardback-mini.discard{background:linear-gradient(160deg,#1a1111,#0a0a12)}
.cardback-mini span{position:relative;z-index:1}

.center-pane{padding:0;display:flex;flex-direction:column}
.enemy-stage{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:14px;position:relative}
.enemy-glow{position:absolute;width:380px;height:380px;border-radius:50%;filter:blur(34px);opacity:.35;pointer-events:none;transition:.6s}
#enemy-sprite{width:200px;height:200px;display:grid;place-items:center;position:relative;z-index:1;transition:transform .15s}
#enemy-sprite.hit{transform:translateX(6px) scale(.98)}
#enemy-sprite.attacking{transform:translateX(-18px) scale(1.04)}
.enemy-hud{margin-top:8px;width:min(340px,88%);text-align:center;z-index:2}
.enemy-name{font-family:'Cinzel',serif;letter-spacing:.18em;font-size:13px;color:#e8d8c8;margin-bottom:6px}
.hp-track.big{width:100%;height:14px}
.enemy-intent{margin-top:8px;min-height:22px;display:flex;align-items:center;justify-content:center;gap:6px;font-size:10px;letter-spacing:.06em;color:#a0a0b0;background:rgba(0,0,0,.35);border:1px solid rgba(255,255,255,.06);border-radius:999px;padding:4px 10px}
.combat-log{position:absolute;left:12px;bottom:12px;width:220px;max-height:110px;overflow:hidden;display:flex;flex-direction:column;gap:3px;pointer-events:none}
.log-entry{font-size:9px;line-height:1.35;padding:4px 7px;border-radius:6px;background:rgba(0,0,0,.55);border:1px solid rgba(255,255,255,.06);color:#b0a8a0;animation:logIn .25s ease}
@keyframes logIn{from{transform:translateY(6px);opacity:0}to{transform:none;opacity:1}}

.energy-meter{display:flex;flex-direction:column;align-items:center;gap:10px}
.em-label{font-size:9px;letter-spacing:.24em;color:#6a6a7a}
.em-orbs{display:flex;flex-direction:column;gap:8px}
.eorb{width:36px;height:36px;border-radius:50%;border:1px solid #2a2a3a;background:radial-gradient(circle at 35% 30%,#2a2a3a,#0f0f14);display:grid;place-items:center;font-size:12px;color:#4a4a5a;transition:.25s;position:relative}
.eorb.filled{background:radial-gradient(circle at 35% 30%,#8a7bb0,#4a2a8a);border-color:#7a5abb;color:#e8d8ff;box-shadow:0 0 12px rgba(140,100,220,.5),inset 0 1px 0 rgba(255,255,255,.3)}
.eorb.filled:after{content:'';position:absolute;inset:-4px;border-radius:50%;border:1px solid rgba(140,100,220,.2)}
.em-num{font-size:12px;color:#8a7bb0;letter-spacing:.08em}
.turn-controls{margin-top:auto;display:flex;flex-direction:column;gap:8px;align-items:center}
.turn-controls .hint{font-size:9px;color:#4a4a5a;text-align:center}

/* HAND */
.hand-zone{height:212px;background:linear-gradient(180deg,rgba(10,10,14,.2),rgba(12,12,20,.7));border-top:1px solid #1e1e2a;padding:8px 16px;display:flex;justify-content:center;align-items:flex-end;overflow:visible}
.hand{display:flex;gap:10px;align-items:flex-end;justify-content:center}

/* CARDS */
.play-card{width:156px;height:192px;position:relative;flex-shrink:0;cursor:pointer;transform-origin:bottom center;transition:transform .2s,filter .2s,margin .2s;filter:drop-shadow(0 10px 20px rgba(0,0,0,.6))}
.play-card:hover{transform:translateY(-18px) scale(1.06) rotate(0deg)!important;z-index:30;filter:drop-shadow(0 22px 40px rgba(0,0,0,.8)) brightness(1.1)}
.play-card.disabled{filter:grayscale(.6) brightness(.6);pointer-events:none}
.play-card.exhausted{opacity:.4;pointer-events:none}
.play-card .card-frame{position:absolute;inset:0}
.play-card .card-inner{position:absolute;inset:6px 6px 8px 6px;display:flex;flex-direction:column}
.card-cost{position:absolute;left:-4px;top:-6px;width:28px;height:28px;border-radius:50%;display:grid;place-items:center;font-weight:800;font-size:12px;color:#e8e0d0;border:1px solid rgba(255,255,255,.15);z-index:4;box-shadow:0 2px 8px rgba(0,0,0,.5)}
.cost-atk{background:radial-gradient(circle at 35% 35%,#c46a3a,#8a2a1a)}
.cost-def{background:radial-gradient(circle at 35% 35%,#4a7ab0,#1a3a6a)}
.cost-other{background:radial-gradient(circle at 35% 35%,#7a5abb,#2a1a5a)}
.card-top{display:flex;justify-content:space-between;align-items:flex-start;padding:4px 4px 0 18px}
.card-type{font-size:7.5px;letter-spacing:.18em;text-transform:uppercase;color:rgba(255,255,255,.55);font-weight:700}
.card-name{font-family:'Cinzel',serif;font-size:10px;letter-spacing:.06em;color:#f0e6d0;font-weight:700;line-height:1.1;margin-top:2px;text-shadow:0 1px 2px rgba(0,0,0,.8)}
.card-art{height:68px;margin:4px 4px 0 4px;border-radius:5px;background:#0a0a0e;border:1px solid rgba(255,255,255,.08);overflow:hidden;position:relative;display:grid;place-items:center}
.card-art-emoji{font-size:28px;filter:drop-shadow(0 2px 6px rgba(0,0,0,.8))}
.card-stats{display:flex;gap:4px;justify-content:center;margin:5px 0 2px}
.stat-badge{font-size:8px;padding:2px 6px;border-radius:999px;border:1px solid;font-weight:700;letter-spacing:.06em}
.badge-atk{background:rgba(180,60,30,.25);border-color:rgba(220,100,60,.35);color:#ffb090}
.badge-def{background:rgba(50,110,180,.25);border-color:rgba(80,140,220,.35);color:#8ac4ff}
.badge-none{display:none}
.card-ability{flex:1;margin:2px 5px 4px;background:rgba(0,0,0,.35);border:1px solid rgba(255,255,255,.06);border-radius:5px;padding:5px 6px;font-size:8.2px;line-height:1.35;color:#b8b0a2}
.card-ability b{color:#e8d8b0}

/* CARD FRAMES SVG COLORS */
.frame-bastion .frame-bg{fill:url(#gBastion)}
.frame-wither .frame-bg{fill:url(#gWither)}
.frame-shroud .frame-bg{fill:url(#gShroud)}

/* SHOP */
#screen-shop{background:radial-gradient(100% 80% at 50% 0%,#1a1824,#08080c)}
.shop-bg{position:absolute;inset:0;opacity:.25;background:repeating-linear-gradient(90deg,rgba(255,255,255,.02) 0 1px,transparent 1px 80px),repeating-linear-gradient(0deg,rgba(255,255,255,.02) 0 1px,transparent 1px 80px)}
.shop-content{position:relative;z-index:1;width:min(1180px,96vw);margin:0 auto;padding:18px 16px;flex:1;display:flex;flex-direction:column;overflow:auto}
.shop-header{display:flex;align-items:center;gap:16px;margin-bottom:12px}
.shop-header h2{font-family:'Cinzel',serif;font-size:22px;letter-spacing:.12em;color:#e8e0d0}
.shop-gold{margin-left:auto;display:flex;align-items:center;gap:6px;color:#e7c76a;font-weight:700}
.shop-filters{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
.shop-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(166px,1fr));gap:14px;padding:6px 0 18px}
.shop-item{position:relative}
.shop-item .play-card{width:100%;height:212px;cursor:pointer}
.shop-item .play-card:hover{transform:translateY(-6px) scale(1.02)}
.price-tag{position:absolute;left:50%;bottom:-8px;transform:translateX(-50%);background:#1a1a24;border:1px solid #3a3a4a;color:#e7c76a;font-size:10px;padding:3px 8px;border-radius:999px;z-index:5;box-shadow:0 3px 10px rgba(0,0,0,.5)}
.price-tag.high{background:#2a1a2a;color:#c47a7a}
.price-tag.can{border-color:#e7c76a;box-shadow:0 0 8px rgba(231,199,106,.2)}
.shop-footer{border-top:1px solid #1e1e2a;padding-top:12px;margin-top:auto}
.heal-row{display:flex;align-items:center;gap:12px}
.heal-row .hint{font-size:10px;color:#5a5a6a}

/* END */
#screen-end{align-items:center;justify-content:center;background:radial-gradient(90% 70% at 50% 20%,#1e1a2e,#08080c)}
.end-card{background:linear-gradient(180deg,rgba(28,26,40,.95),rgba(14,14,20,.98));border:1px solid #2a2a4a;border-radius:18px;padding:28px 26px;width:min(480px,92vw);text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.6)}
.end-card h2{font-family:'Cinzel',serif;font-size:28px;letter-spacing:.14em;color:#e8e0d0;margin-bottom:8px}
.end-card p{font-size:12px;color:#8a8490;line-height:1.5;margin-bottom:14px}
.end-stats{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:16px 0;text-align:left}
.end-stat{background:#15151d;border:1px solid #1e1e2a;border-radius:8px;padding:8px 10px}
.end-stat b{display:block;font-size:10px;color:#8a7bb0;letter-spacing:.08em;margin-bottom:2px}

/* TOOLTIP */
#tooltip{position:fixed;pointer-events:none;z-index:9999;background:#15151d;border:1px solid #2a2a4a;color:#b0a8a0;font-size:10px;padding:8px 10px;border-radius:8px;max-width:240px;opacity:0;transform:translateY(6px);transition:.15s;box-shadow:0 8px 24px rgba(0,0,0,.6)}

/* Animations */
@keyframes floatY{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}
.enemy-float{animation:floatY 3.5s ease-in-out infinite}

/* Responsive */
@media (max-width:900px){
  .battlefield{grid-template-columns:1fr;grid-template-rows:auto auto auto;overflow:auto}
  .hand-zone{height:auto;min-height:200px;overflow-x:auto}
  .class-select{grid-template-columns:1fr}
}
</style>
</head>
<body>
<div id="app">
  <div id="bg-orbs"><div class="orb o1"></div><div class="orb o2"></div><div class="orb o3"></div></div>

  <!-- MENU -->
  <div id="screen-menu" class="screen active">
    <div class="menu-vignette"></div>
    <div class="menu-content">
      <div class="title-block">
        <div class="title-rune">ᛟ ᛝ ᛟ</div>
        <h1 class="game-title">Shadows<br><span>of Aethelgard</span></h1>
        <p class="subtitle">The Void consumes all. Three floors remain.</p>
      </div>
      <div class="class-select">
        <div class="class-card" data-class="Bastion">
          <div class="class-icon-wrap bastion-icon">
            <svg viewBox="0 0 80 80"><path d="M40 8 L68 22 L68 52 L40 72 L12 52 L12 22 Z" fill="#2a3540" stroke="#6b7c8d" stroke-width="2"></path><path d="M40 18 L58 28 L58 48 L40 60 L22 48 L22 28 Z" fill="#3a4a5a"></path></svg>
          </div>
          <h3>THE BASTION</h3>
          <div class="class-meta"><span class="hp">35 HP</span><span class="es">3 EN</span><span class="tag">Tank / AoE</span></div>
          <p>Immovable object. High defense, tremors that crack the earth.</p>
          <div class="starter-preview"><span>Slam</span><span>Shield Wall</span><span>Rally</span></div>
        </div>
        <div class="class-card" data-class="Wither">
          <div class="class-icon-wrap wither-icon">
            <svg viewBox="0 0 80 80"><circle cx="40" cy="40" r="28" fill="#2a2215" stroke="#c46a1f" stroke-width="2"></circle><path d="M28 32 Q40 18 52 32 Q52 50 40 58 Q28 50 28 32" fill="#8a3a0a"></path></svg>
          </div>
          <h3>THE WITHER</h3>
          <div class="class-meta"><span class="hp">28 HP</span><span class="es">3 EN</span><span class="tag">Burn / Rot</span></div>
          <p>Rot and flame. Enemies wither while you endure the ash.</p>
          <div class="starter-preview"><span>Fireball</span><span>Poison</span><span>Wither</span></div>
        </div>
        <div class="class-card" data-class="Shroud">
          <div class="class-icon-wrap shroud-icon">
            <svg viewBox="0 0 80 80"><path d="M40 10 L60 40 L40 70 L20 40 Z" fill="#1a1a2a" stroke="#8a7bb0" stroke-width="2"></path><circle cx="40" cy="40" r="8" fill="#c9b8ff"></circle></svg>
          </div>
          <h3>THE SHROUD</h3>
          <div class="class-meta"><span class="hp">26 HP</span><span class="es">4 EN</span><span class="tag">Crit / Evasion</span></div>
          <p>Shadow and speed. Strike unseen, vanish before retaliation.</p>
          <div class="starter-preview"><span>Backstab</span><span>Blur</span><span>Assassinate</span></div>
        </div>
      </div>
      <p class="menu-hint">Choose your vessel. The descent begins.</p>
    </div>
  </div>

  <!-- COMBAT -->
  <div id="screen-combat" class="screen">
    <div class="topbar">
      <div class="tb-group">
        <div class="tb-item gold"><i>◈</i><span id="ui-gold">0</span></div>
        <div class="tb-item floor"><i>◬</i><span id="ui-floor">F1 — R1</span>
          <div class="floor-dots" id="floor-dots"></div>
        </div>
      </div>
      <div class="tb-group">
        <div class="tb-item boss-hp" id="ui-boss-wrap" style="display:none"><div class="boss-label">VOID LORD</div><div class="hp-track"><div class="hp-fill" id="ui-boss-hp"></div></div></div>
      </div>
      <div class="tb-group">
        <button id="btn-shop" class="btn-metal small">SHOP ◈</button>
        <button id="btn-abandon" class="btn-ghost small">ABANDON</button>
      </div>
    </div>

    <div class="battlefield">
      <div class="pane player-pane">
        <div class="entity-card player-entity">
          <div class="entity-portrait" id="player-portrait"></div>
          <div class="entity-stats">
            <div class="stat-row"><span class="sl">HP</span><div class="bar"><div class="fill hp" id="p-hp-fill"></div><span class="bar-txt" id="p-hp-txt"></span></div></div>
            <div class="stat-row"><span class="sl">DEF</span><div class="bar thin"><div class="fill def" id="p-def-fill"></div><span class="bar-txt" id="p-def-txt">0</span></div></div>
            <div class="status-strip" id="p-status"></div>
          </div>
        </div>
        <div class="deck-info">
          <div class="cardback-mini" id="deck-count"><span id="ui-deck-n">0</span></div>
          <div class="label">DECK</div>
          <div class="cardback-mini discard" id="discard-count"><span id="ui-disc-n">0</span></div>
          <div class="label">VOID</div>
        </div>
      </div>

      <div class="pane center-pane">
        <div class="enemy-stage">
          <div class="enemy-glow" id="enemy-glow"></div>
          <div id="enemy-sprite"></div>
          <div class="enemy-hud">
            <div class="enemy-name" id="e-name"></div>
            <div class="hp-track big"><div class="hp-fill enemy" id="e-hp-fill"></div><span class="bar-txt" id="e-hp-txt"></span></div>
            <div class="enemy-intent" id="e-intent"></div>
            <div class="status-strip" id="e-status"></div>
          </div>
          <div class="combat-log" id="combat-log"></div>
        </div>
      </div>

      <div class="pane energy-pane">
        <div class="energy-meter">
          <div class="em-label">ENERGY</div>
          <div class="em-orbs" id="em-orbs"></div>
          <div class="em-num"><span id="ui-energy">3</span>/<span id="ui-energy-max">5</span></div>
        </div>
        <div class="turn-controls">
          <button id="btn-end" class="btn-ritual">END TURN ↻</button>
          <div class="hint" id="turn-hint">Play cards, then end turn</div>
        </div>
      </div>
    </div>

    <div class="hand-zone">
      <div class="hand" id="hand"></div>
    </div>
  </div>

  <!-- SHOP -->
  <div id="screen-shop" class="screen">
    <div class="shop-bg"></div>
    <div class="shop-content">
      <div class="shop-header">
        <h2>The Hollow Market</h2>
        <div class="shop-gold"><span>◈</span><span id="shop-gold">0</span></div>
        <button id="shop-leave" class="btn-metal">CONTINUE DESCENT →</button>
      </div>
      <div class="shop-filters">
        <button class="chip active" data-filter="all">All</button>
        <button class="chip" data-filter="Bastion">Bastion</button>
        <button class="chip" data-filter="Wither">Wither</button>
        <button class="chip" data-filter="Shroud">Shroud</button>
        <button class="chip" data-filter="Attack">Attack</button>
        <button class="chip" data-filter="Defense">Defense</button>
        <button class="chip" data-filter="Other">Utility</button>
      </div>
      <div class="shop-grid" id="shop-grid"></div>
      <div class="shop-footer">
        <div class="heal-row">
          <button id="btn-heal" class="btn-metal">Heal 10 HP — Cost 8 ◈</button>
          <span class="hint">Upgrades: 4th copy costs 10◈ (max 5 copies)</span>
        </div>
      </div>
    </div>
  </div>

  <!-- VICTORY / DEFEAT -->
  <div id="screen-end" class="screen">
    <div class="end-card">
      <h2 id="end-title"></h2>
      <p id="end-desc"></p>
      <div class="end-stats" id="end-stats"></div>
      <button id="btn-restart" class="btn-ritual large">RETURN TO VOID</button>
    </div>
  </div>

  <!-- TOOLTIP -->
  <div id="tooltip"></div>
</div>
<script>
'use strict';

// ── const: CARD_DB ──
const CARD_DB = {
  // BASTION
  'BA-A01': {id:'BA-A01', name:'Slam', class:'Bastion', type:'Attack', cost:2, atk:3, def:0, desc:'Deals 3 dmg. If 30+ HP, draw 1.', emoji:'🔨', art:'hammer'},
  'BA-A02': {id:'BA-A02', name:'Earthquake', class:'Bastion', type:'Attack', cost:3, atk:4, def:0, desc:'Deals 4 dmg to all enemies. AOE tremor.', emoji:'⛰️', art:'quake'},
  'BA-A03': {id:'BA-A03', name:'Stone Fist', class:'Bastion', type:'Attack', cost:1, atk:2, def:0, desc:'Deals 2 dmg. Reliable.', emoji:'🪨', art:'fist'},
  'BA-A04': {id:'BA-A04', name:'Boulder', class:'Bastion', type:'Attack', cost:4, atk:6, def:0, desc:'Deals 6 dmg. Massive.', emoji:'🗿', art:'boulder'},
  'BA-D01': {id:'BA-D01', name:'Shield Wall', class:'Bastion', type:'Defense', cost:0, atk:0, def:4, desc:'Gain 4 Block this turn.', emoji:'🛡️', art:'shield'},
  'BA-D02': {id:'BA-D02', name:'Iron Skin', class:'Bastion', type:'Defense', cost:2, atk:0, def:5, desc:'Gain 5 permanent Armor. Discard 1.', emoji:'⚙️', art:'anvil'},
  'BA-D03': {id:'BA-D03', name:'Stand Firm', class:'Bastion', type:'Defense', cost:1, atk:0, def:2, desc:'Gain 2 Block. Reduce next hit by 1.', emoji:'🦶', art:'stance'},
  'BA-D04': {id:'BA-D04', name:'Taunt', class:'Bastion', type:'Defense', cost:2, atk:0, def:3, desc:'Gain 3 Block. Stun enemy 1 turn.', emoji:'📢', art:'taunt'},
  'BA-U01': {id:'BA-U01', name:'Search', class:'Bastion', type:'Other', cost:2, atk:0, def:0, desc:'Draw 1. If hand >5, discard hand.', emoji:'🔍', art:'search'},
  'BA-U02': {id:'BA-U02', name:'Repair', class:'Bastion', type:'Other', cost:3, atk:0, def:0, desc:'Heal 6 HP.', emoji:'🔧', art:'repair'},
  'BA-U03': {id:'BA-U03', name:'Rally', class:'Bastion', type:'Other', cost:1, atk:0, def:0, desc:'Draw 1. Gain 1 Energy.', emoji:'⚑', art:'rally'},
  'BA-U04': {id:'BA-U04', name:'Loot', class:'Bastion', type:'Other', cost:2, atk:0, def:0, desc:'Gain 5 Gold.', emoji:'💰', art:'loot'},
  // WITHER
  'WI-A01': {id:'WI-A01', name:'Fireball', class:'Wither', type:'Attack', cost:2, atk:3, def:0, desc:'3 dmg + 1 Burn.', emoji:'🔥', art:'fireball'},
  'WI-A02': {id:'WI-A02', name:'Inferno', class:'Wither', type:'Attack', cost:3, atk:4, def:0, desc:'4 dmg + 3 Burn. Costs 3.', emoji:'🌋', art:'inferno'},
  'WI-A03': {id:'WI-A03', name:'Ember', class:'Wither', type:'Attack', cost:1, atk:2, def:0, desc:'2 dmg + lingering 1 dmg/turn (Burn).', emoji:'✨', art:'ember'},
  'WI-A04': {id:'WI-A04', name:'Pyre', class:'Wither', type:'Attack', cost:4, atk:5, def:0, desc:'5 dmg. All enemies +1 dmg next turn.', emoji:'☀️', art:'pyre'},
  'WI-D01': {id:'WI-D01', name:'Scorch Shield', class:'Wither', type:'Defense', cost:2, atk:0, def:3, desc:'Gain 3 Block. Block next Burn tick.', emoji:'🔥', art:'scorch'},
  'WI-D02': {id:'WI-D02', name:'Heat Resist', class:'Wither', type:'Defense', cost:1, atk:0, def:1, desc:'Gain 1 Block. Gain 1 Energy.', emoji:'♨️', art:'heat'},
  'WI-D03': {id:'WI-D03', name:'Smoke', class:'Wither', type:'Defense', cost:2, atk:0, def:4, desc:'Gain 4 Block. Resist Burn.', emoji:'💨', art:'smoke'},
  'WI-D04': {id:'WI-D04', name:'Endure', class:'Wither', type:'Defense', cost:3, atk:0, def:2, desc:'Gain 2 Block. Heal 2 HP.', emoji:'🌿', art:'endure'},
  'WI-U01': {id:'WI-U01', name:'Poison', class:'Wither', type:'Other', cost:2, atk:0, def:0, desc:'Apply 1 Poison.', emoji:'🧪', art:'poison'},
  'WI-U02': {id:'WI-U02', name:'Conflagrate', class:'Wither', type:'Other', cost:3, atk:0, def:0, desc:'3 dmg + 2 Burn to all.', emoji:'💥', art:'burn'},
  'WI-U03': {id:'WI-U03', name:'Slow', class:'Wither', type:'Other', cost:1, atk:0, def:0, desc:'Draw 1. Stun target next turn.', emoji:'🕸️', art:'slow'},
  'WI-U04': {id:'WI-U04', name:'Wither', class:'Wither', type:'Other', cost:2, atk:0, def:0, desc:'Heal 5 HP. -1 enemy Attack.', emoji:'🥀', art:'wither'},
  // SHROUD
  'SH-A01': {id:'SH-A01', name:'Backstab', class:'Shroud', type:'Attack', cost:2, atk:4, def:0, desc:'4 dmg. +1 if target no armor.', emoji:'🗡️', art:'backstab'},
  'SH-A02': {id:'SH-A02', name:'Shadow Bolt', class:'Shroud', type:'Attack', cost:3, atk:5, def:0, desc:'5 dmg. Ignores Block.', emoji:'🌑', art:'bolt'},
  'SH-A03': {id:'SH-A03', name:'Silence', class:'Shroud', type:'Attack', cost:1, atk:3, def:0, desc:'3 dmg. Silence enemy ability.', emoji:'🤫', art:'silence'},
  'SH-A04': {id:'SH-A04', name:'Vengeance', class:'Shroud', type:'Attack', cost:3, atk:6, def:0, desc:'6 dmg. Gain 2 Energy.', emoji:'⚔️', art:'vengeance'},
  'SH-D01': {id:'SH-D01', name:'Blur', class:'Shroud', type:'Defense', cost:3, atk:0, def:2, desc:'Gain 2 Block. Ignore next attack.', emoji:'👁️', art:'blur'},
  'SH-D02': {id:'SH-D02', name:'Step', class:'Shroud', type:'Defense', cost:2, atk:0, def:3, desc:'Gain 3 Block. Gain 1 Energy.', emoji:'👣', art:'step'},
  'SH-D03': {id:'SH-D03', name:'Evasion', class:'Shroud', type:'Defense', cost:1, atk:0, def:1, desc:'Gain 1 Block. Skip enemy action.', emoji:'💨', art:'evasion'},
  'SH-D04': {id:'SH-D04', name:'Phantom', class:'Shroud', type:'Defense', cost:2, atk:0, def:4, desc:'Gain 4 Block. Ghost 1 turn.', emoji:'👻', art:'phantom'},
  'SH-U01': {id:'SH-U01', name:'Dash', class:'Shroud', type:'Other', cost:1, atk:0, def:0, desc:'Gain 1 Energy. Evade attack.', emoji:'⚡', art:'dash'},
  'SH-U02': {id:'SH-U02', name:'Teleport', class:'Shroud', type:'Other', cost:2, atk:0, def:0, desc:'Back row. Deal 2 dmg.', emoji:'🌀', art:'teleport'},
  'SH-U03': {id:'SH-U03', name:'Hide', class:'Shroud', type:'Other', cost:2, atk:0, def:0, desc:'1 Block. Discard 1, draw 2.', emoji:'🌫️', art:'hide'},
  'SH-U04': {id:'SH-U04', name:'Assassinate', class:'Shroud', type:'Other', cost:3, atk:0, def:0, desc:'Deal 8 dmg unblockable.', emoji:'☠️', art:'assassinate'},
};

// ── const: CLASS_DATA ──
const CLASS_DATA = {
  Bastion: {maxHp:35, startEnergy:3, maxEnergy:5, handSize:4, color:'#6b7c8d', accent:'#8aa0b0', deck:['BA-A01','BA-A01','BA-A03','BA-A03','BA-D01','BA-D01','BA-D03','BA-U03']},
  Wither: {maxHp:28, startEnergy:3, maxEnergy:5, handSize:4, color:'#c46a1f', accent:'#e0923a', deck:['WI-A01','WI-A01','WI-A03','WI-A03','WI-D02','WI-D01','WI-U03','WI-U04']},
  Shroud: {maxHp:26, startEnergy:4, maxEnergy:5, handSize:5, color:'#8a7bb0', accent:'#c9b8ff', deck:['SH-A01','SH-A01','SH-A03','SH-A03','SH-D02','SH-D03','SH-U01','SH-U04']},
};

// ── const: ENEMY_POOL ──
const ENEMY_POOL = {
  1: [{name:'Hollow Rat', hp:20, dmg:3, emoji:'🐀', type:'beast'}, {name:'Dusk Goblin', hp:22, dmg:4, emoji:'👺', type:'goblin'}, {name:'Void Mote', hp:18, dmg:3, emoji:'👁️', type:'void'}],
  2: [{name:'Vampire Knight', hp:30, dmg:5, emoji:'🧛', type:'vamp'}, {name:'Bog Ogre', hp:36, dmg:6, emoji:'👹', type:'ogre'}, {name:'Acolyte', hp:28, dmg:5, emoji:'🧙', type:'cult'}],
  3: [{name:'Void Warrior', hp:40, dmg:8, emoji:'⛓️', type:'warrior'}, {name:'Cultist Lord', hp:42, dmg:7, emoji:'🪬', type:'cult'}, {name:'Wraith', hp:38, dmg:9, emoji:'👻', type:'wraith'}],
};

// ── const: BOSSES ──
const BOSSES = {
  1: {name:'The Gatekeeper', hp:80, dmg:15, emoji:'🗿', mech:'gatekeeper', desc:'Blocks 5 dmg per turn while HP>20'},
  2: {name:'The Corrupted', hp:80, dmg:18, emoji:'💀', mech:'corrupted', desc:'Deals 20 if you have <3 Energy'},
  3: {name:'The Void Lord', hp:70, dmg:16, emoji:'🌌', mech:'voidlord', desc:'Regenerates 10 HP per turn. Cannot be blocked? Actually he regens.'},
};

// ── function: svgCardFrame ──
function svgCardFrame(cls){
  // returns SVG string with unique frame per deck
  if(cls==='Bastion'){
    return `
    <svg class="card-frame" viewBox="0 0 160 200" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
      <defs>
        <linearGradient id="gBastion" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0%" stop-color="#2e3a4a"/><stop offset="100%" stop-color="#151a22"/>
        </linearGradient>
        <linearGradient id="gbSt" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#5a6a7a" stop-opacity=".9"/><stop offset="100%" stop-color="#2a333e"/></linearGradient>
      </defs>
      <rect class="frame-bg" x="2" y="2" width="156" height="196" rx="10" />
      <!-- heavy stone border -->
      <path d="M8 2 L152 2 L152 10 L146 14 L14 14 L8 10 Z" fill="#3a4a5a" stroke="#6b7c8d" stroke-width="0.6" opacity=".9"/>
      <path d="M2 12 Q2 2 12 2" fill="none" stroke="#6b7c8d" stroke-width="1.2"/>
      <path d="M158 12 Q158 2 148 2" fill="none" stroke="#6b7c8d" stroke-width="1.2"/>
      <!-- rivets -->
      <circle cx="14" cy="8" r="2" fill="#8aa0b0"/><circle cx="146" cy="8" r="2" fill="#8aa0b0"/>
      <circle cx="8" cy="30" r="1.6" fill="#4a5a6a"/><circle cx="152" cy="30" r="1.6" fill="#4a5a6a"/>
      <circle cx="8" cy="170" r="1.6" fill="#4a5a6a"/><circle cx="152" cy="170" r="1.6" fill="#4a5a6a"/>
      <rect x="6" y="18" width="148" height="1" fill="#2a3a4a" opacity=".6"/>
      <rect x="6" y="180" width="148" height="1" fill="#2a3a4a" opacity=".6"/>
    </svg>`;
  } else if(cls==='Wither'){
    return `
    <svg class="card-frame" viewBox="0 0 160 200" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
      <defs>
        <linearGradient id="gWither" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#3a2410"/><stop offset="100%" stop-color="#1a1208"/></linearGradient>
      </defs>
      <rect class="frame-bg" x="2" y="2" width="156" height="196" rx="14" />
      <path d="M10 6 Q20 2 30 6 T50 8 T70 6 T90 8 T110 6 T130 8 T150 6" fill="none" stroke="#8a5a2a" stroke-width="1" stroke-linecap="round" opacity=".6"/>
      <path d="M8 18 Q12 28 8 38 Q12 48 8 58 Q12 68 8 78" fill="none" stroke="#6a3a1a" stroke-width="0.8" opacity=".5"/>
      <path d="M152 18 Q148 28 152 38 Q148 48 152 58 Q148 68 152 78" fill="none" stroke="#6a3a1a" stroke-width="0.8" opacity=".5"/>
      <path d="M8 190 Q20 194 32 190 T54 192 T76 190 T98 192 T120 190 T148 190" fill="none" stroke="#8a5a2a" stroke-width="1" opacity=".6"/>
      <!-- thorn dots -->
      <circle cx="18" cy="24" r="1.2" fill="#c47a2a" opacity=".8"/><circle cx="142" cy="24" r="1.2" fill="#c47a2a" opacity=".8"/>
    </svg>`;
  } else {
    return `
    <svg class="card-frame" viewBox="0 0 160 200" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
      <defs>
        <linearGradient id="gShroud" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#201830"/><stop offset="100%" stop-color="#0c0a14"/></linearGradient>
      </defs>
      <rect class="frame-bg" x="2" y="2" width="156" height="196" rx="2" />
      <path d="M80 2 L152 18 L152 184 L80 198 L8 184 L8 18 Z" fill="none" stroke="#4a3a6a" stroke-width="0.8" opacity=".8"/>
      <path d="M80 6 L144 20 L144 180 L80 194 L16 180 L16 20 Z" fill="none" stroke="#8a7bb0" stroke-width="0.35" opacity=".35" stroke-dasharray="4 4"/>
      <circle cx="80" cy="8" r="1.5" fill="#c9b8ff" opacity=".9"/><circle cx="80" cy="192" r="1.5" fill="#c9b8ff" opacity=".9"/>
      <path d="M30 12 L38 18 L30 24" fill="none" stroke="#5a4a7a" stroke-width=".7"/>
      <path d="M130 12 L122 18 L130 24" fill="none" stroke="#5a4a7a" stroke-width=".7"/>
    </svg>`;
  }
}

// ── function: svgCardBack ──
function svgCardBack(cls){
  if(cls==='Bastion'){
    return `<svg viewBox="0 0 100 140" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="140" rx="10" fill="#151a22"/><rect x="5" y="5" width="90" height="130" rx="8" fill="none" stroke="#3a4a5a" stroke-width="1.2"/><path d="M50 22 L74 36 L74 68 L50 86 L26 68 L26 36 Z" fill="#2a3540" stroke="#6b7c8d" stroke-width="1.5"/><path d="M50 32 L66 40 L66 62 L50 72 L34 62 L34 40 Z" fill="#3a4a5a"/><circle cx="50" cy="105" r="10" fill="#1a222e" stroke="#4a5a6a"/></svg>`;
  } else if(cls==='Wither'){
    return `<svg viewBox="0 0 100 140" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="140" rx="14" fill="#1a1208"/><rect x="5" y="5" width="90" height="130" rx="12" fill="none" stroke="#5a3a1a" stroke-width="1"/><path d="M50 25 Q64 38 58 56 Q50 70 42 56 Q36 38 50 25" fill="#6a2a0a" stroke="#c46a1f" stroke-width="1"/><circle cx="50" cy="100" r="12" fill="#2a1a0a" stroke="#8a5a1a"/><path d="M44 96 Q50 90 56 96 Q56 104 50 108 Q44 104 44 96" fill="#c46a1f"/></svg>`;
  } else {
    return `<svg viewBox="0 0 100 140" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="140" rx="3" fill="#0c0a14"/><rect x="5" y="5" width="90" height="130" rx="2" fill="none" stroke="#4a3a6a"/><path d="M50 18 L70 55 L50 92 L30 55 Z" fill="#1a1a2a" stroke="#8a7bb0" stroke-width="1"/><circle cx="50" cy="56" r="10" fill="#1a1030" stroke="#c9b8ff"/><circle cx="50" cy="56" r="3" fill="#c9b8ff"/><path d="M46 110 L50 118 L54 110 Z" fill="#2a2040"/></svg>`;
  }
}

// ── function: enemySVG ──
function enemySVG(type, boss){
  const size=200;
  if(boss){
    if(boss.mech==='gatekeeper'){
      return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><circle cx="100" cy="105" r="70" fill="#1a2230" stroke="#4a5a6a" stroke-width="2"/><rect x="70" y="30" width="60" height="20" rx="4" fill="#2a3a4a" stroke="#5a6a7a"/><circle cx="80" cy="95" r="8" fill="#6ab0ff" opacity=".8"/><circle cx="120" cy="95" r="8" fill="#6ab0ff" opacity=".8"/><path d="M80 125 Q100 138 120 125" stroke="#3a4a5a" fill="none" stroke-width="3"/></svg>`;
    } else if(boss.mech==='corrupted'){
      return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><g><path d="M70 70 L130 70 L135 140 L65 140 Z" fill="#22101a" stroke="#7a2a4a" stroke-width="2"/><circle cx="85" cy="95" r="10" fill="#ff4a6a" opacity=".9"/><circle cx="115" cy="95" r="10" fill="#ff4a6a" opacity=".9"/><path d="M70 50 Q100 30 130 50 L120 70 L80 70 Z" fill="#3a1020" stroke="#5a1a2a"/></g></svg>`;
    } else {
      return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><g><circle cx="100" cy="100" r="66" fill="#100a1e" stroke="#5a3a9a" stroke-width="2" stroke-dasharray="6 4"/><circle cx="100" cy="100" r="36" fill="#1e1036" stroke="#8a5acd" stroke-width="1.5"/><circle cx="86" cy="92" r="7" fill="#c9b8ff"/><circle cx="114" cy="92" r="7" fill="#c9b8ff"/><path d="M82 120 Q100 130 118 120" stroke="#6a4ab0" fill="none" stroke-width="2"/></g></svg>`;
    }
  } else {
    const emojis={beast:'🐀', goblin:'👺', void:'🌀', vamp:'🦇', ogre:'👹', cult:'🔮', warrior:'⚔️', wraith:'👻'};
    // abstract low-poly forms
    if(type==='beast') return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><ellipse cx="100" cy="130" rx="50" ry="34" fill="#1e2a1e" stroke="#3a5a3a" stroke-width="2"/><circle cx="100" cy="80" r="28" fill="#2a3a2a" stroke="#4a6a4a" stroke-width="1.5"/><circle cx="86" cy="78" r="4" fill="#8aff8a"/><circle cx="114" cy="78" r="4" fill="#8aff8a"/></svg>`;
    if(type==='goblin') return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><path d="M70 90 L130 90 L120 150 L80 150 Z" fill="#2a2a1e" stroke="#6a6a3a" stroke-width="2"/><circle cx="100" cy="70" r="30" fill="#3a3a22" stroke="#6a6a3a" stroke-width="1.5"/><path d="M80 62 L72 48 L88 60" fill="#4a4a2a" stroke="#6a6a3a"/></svg>`;
    if(type==='void') return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><circle cx="100" cy="100" r="46" fill="#1a1a30" stroke="#5a5acd" stroke-width="2"/><circle cx="100" cy="100" r="18" fill="#0a0a1a" stroke="#8a8aff" stroke-width="1"/><circle cx="100" cy="100" r="35" fill="none" stroke="#4a4aff" stroke-dasharray="8 6" opacity=".6"/></svg>`;
    if(type==='vamp') return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><path d="M60 80 Q100 40 140 80 L130 150 L70 150 Z" fill="#1a0a20" stroke="#6a2a6a" stroke-width="2"/><path d="M60 80 Q30 70 40 110 Q70 100 60 80" fill="#2a1030"/><path d="M140 80 Q170 70 160 110 Q130 100 140 80" fill="#2a1030"/><circle cx="84" cy="92" r="6" fill="#ff4a7a"/><circle cx="116" cy="92" r="6" fill="#ff4a7a"/></svg>`;
    if(type==='ogre') return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><rect x="65" y="70" width="70" height="80" rx="14" fill="#2a2210" stroke="#8a6a2a" stroke-width="2"/><circle cx="100" cy="56" r="28" fill="#3a2e18" stroke="#8a6a2a" stroke-width="2"/><rect x="70" y="92" width="12" height="6" fill="#ffaa5a"/><rect x="118" y="92" width="12" height="6" fill="#ffaa5a"/></svg>`;
    if(type==='cult') return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><path d="M80 60 L120 60 L130 140 L70 140 Z" fill="#1a102a" stroke="#5a3a8a" stroke-width="2"/><circle cx="100" cy="46" r="22" fill="#2a1a3a" stroke="#6a4aaa" stroke-width="1.5"/><path d="M100 20 L114 48 L86 48 Z" fill="#6a4aaa" opacity=".7"/></svg>`;
    if(type==='warrior') return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><path d="M70 60 L130 60 L135 130 L65 130 Z" fill="#1a1a24" stroke="#6a6a7a" stroke-width="2"/><circle cx="100" cy="46" r="20" fill="#2a2a34" stroke="#7a7a8a" stroke-width="2"/><rect x="46" y="90" width="14" height="40" rx="4" fill="#2a2a3a" stroke="#5a5a6a"/></svg>`;
    return `<svg viewBox="0 0 200 200" width="${size}" height="${size}"><path d="M100 30 Q160 60 140 120 Q100 150 60 120 Q40 60 100 30" fill="#0f0f18" stroke="#5a5a7a" stroke-width="2"/><circle cx="85" cy="80" r="8" fill="#aaffff" opacity=".7"/><circle cx="115" cy="80" r="8" fill="#aaffff" opacity=".7"/></svg>`;
  }
}

// ── function: createCardElement ──
function createCardElement(cardId, opts={}){
  const data = CARD_DB[cardId];
  if(!data) return document.createElement('div');
  const el = document.createElement('div');
  el.className = `play-card frame-${data.class.toLowerCase()} ${opts.disabled?'disabled':''} ${opts.shop?'shop-card':''}`;
  el.dataset.id = cardId;
  el.dataset.cost = data.cost;
  // rotate slightly for hand
  if(opts.rotate!==undefined) el.style.transform = `rotate(${opts.rotate}deg)`;
  const costType = data.type==='Attack'?'atk':data.type==='Defense'?'def':'other';
  const artContent = `<div class="card-art-emoji">${data.emoji}</div>`;
  el.innerHTML = `
    ${svgCardFrame(data.class)}
    <div class="card-cost cost-${costType}">${data.cost}</div>
    <div class="card-inner">
      <div class="card-top"><div><div class="card-type">${data.type} • ${data.class}</div><div class="card-name">${data.name}</div></div></div>
      <div class="card-art">${artContent}</div>
      <div class="card-stats">
        ${data.atk?`<span class="stat-badge badge-atk">ATK ${data.atk}</span>`:`<span class="stat-badge badge-none"></span>`}
        ${data.def?`<span class="stat-badge badge-def">DEF ${data.def}</span>`:`<span class="stat-badge badge-none"></span>`}
      </div>
      <div class="card-ability">${data.desc}</div>
    </div>
  `;
  if(opts.shop){
    // price tag handled outside
  }
  return el;
}

// ── function: renderHand ──
function renderHand(hand, energy){
  const handEl = document.getElementById('hand');
  handEl.innerHTML='';
  const n = hand.length;
  const mid = (n-1)/2;
  hand.forEach((cid, i)=>{
    const data = CARD_DB[cid];
    const disabled = data.cost > energy || window.gameState?.player?.stunned;
    const rotate = n<=1?0:(i-mid)*5;
    const card = createCardElement(cid,{disabled, rotate});
    card.style.marginLeft = i===0?'0':'-8px';
    card.addEventListener('click', ()=>playCard(i));
    card.addEventListener('mouseenter', (e)=>showTooltip(e, cid));
    card.addEventListener('mouseleave', hideTooltip);
    handEl.appendChild(card);
  });
}

// ── function: showTooltip ──
function showTooltip(e, cardId){
  const data = CARD_DB[cardId];
  if(!data) return;
  const tt = document.getElementById('tooltip');
  tt.innerHTML = `<b>${data.name}</b> [${data.id}]<br><span style="color:#8a7bb0">${data.type} • Cost ${data.cost}</span><br><br>${data.desc}<br><br>Atk:${data.atk} Def:${data.def} Class:${data.class}`;
  tt.style.left = (e.clientX+12)+'px';
  tt.style.top = (e.clientY-80)+'px';
  tt.style.opacity='1';
  tt.style.transform='translateY(0)';
}

// ── function: hideTooltip ──
function hideTooltip(){
  const tt=document.getElementById('tooltip');
  tt.style.opacity='0';
  tt.style.transform='translateY(6px)';
}

// ── function: updateUI ──
function updateUI(){
  const s = window.gameState;
  if(!s) return;
  document.getElementById('ui-gold').textContent=s.gold;
  document.getElementById('shop-gold').textContent=s.gold;
  document.getElementById('ui-floor').textContent=`F${s.floor} — R${s.room} / ${s.isBoss? 'BOSS' : '3'}`;
  // dots
  const dotsEl=document.getElementById('floor-dots');
  dotsEl.innerHTML='';
  for(let i=1;i<=4;i++){
    const d=document.createElement('div');
    d.className='dot'+(i<s.room?' done': i===s.room?' active':'');
    dotsEl.appendChild(d);
  }
  document.getElementById('ui-boss-wrap').style.display = s.isBoss?'flex':'none';
  // player
  const p = s.player;
  const pHpPct = Math.max(0, p.hp/p.maxHp*100);
  document.getElementById('p-hp-fill').style.width=pHpPct+'%';
  document.getElementById('p-hp-txt').textContent=`${p.hp}/${p.maxHp}`;
  const defTotal = p.tempDef + p.permDef;
  const defPct = Math.min(100, defTotal*8);
  document.getElementById('p-def-fill').style.width=defPct+'%';
  document.getElementById('p-def-txt').textContent=defTotal;
  document.getElementById('ui-deck-n').textContent=s.drawPile.length;
  document.getElementById('ui-disc-n').textContent=s.discard.length;
  document.getElementById('ui-energy').textContent=p.energy;
  document.getElementById('ui-energy-max').textContent=p.maxEnergy;
  const eorbs=document.getElementById('em-orbs'); eorbs.innerHTML='';
  for(let i=0;i<p.maxEnergy;i++){
    const orb=document.createElement('div');
    orb.className='eorb'+(i<p.energy?' filled':'');
    orb.textContent=i<p.energy?'◍':'○';
    eorbs.appendChild(orb);
  }
  // status
  renderStatuses('p-status', p.statuses);
  renderStatuses('e-status', s.enemy.statuses);
  // enemy
  document.getElementById('e-name').textContent=s.enemy.name;
  const eHpPct=Math.max(0,s.enemy.hp/s.enemy.maxHp*100);
  document.getElementById('e-hp-fill').style.width=eHpPct+'%';
  document.getElementById('e-hp-txt').textContent=`${s.enemy.hp}/${s.enemy.maxHp}`;
  document.getElementById('ui-boss-hp').style.width=eHpPct+'%';
  document.getElementById('enemy-sprite').innerHTML = enemySVG(s.enemy.type, s.isBoss?BOSSES[s.floor]:null);
  document.getElementById('enemy-glow').style.background = s.isBoss?'radial-gradient(circle,#5a2a6a,transparent 70%)':'radial-gradient(circle,#1a2a3a,transparent 70%)';
  // intent
  let intent='';
  if(s.enemy.statuses.stun>0) intent=`💫 STUNNED (${s.enemy.statuses.stun})`;
  else if(p.hasEvasion) intent='🌀 YOU EVADE — enemy misses';
  else intent=`⚔️ Attack ${s.enemy.dmg} — ${s.enemy.mech?BOSSES[s.floor].desc:''}`;
  document.getElementById('e-intent').textContent=intent;
  // hand
  renderHand(s.hand, p.energy);
}

// ── function: renderStatuses ──
function renderStatuses(containerId, statuses){
  const el=document.getElementById(containerId);
  el.innerHTML='';
  const map={
    burn:{label:'BURN', cls:'burn'},
    poison:{label:'POISON', cls:'poison'},
    stun:{label:'STUN', cls:'stun'},
    blockBoost:{label:'+BLOCK', cls:'block'},
    evade:{label:'EVADE', cls:'block'},
    blur:{label:'BLUR', cls:'block'},
  };
  Object.entries(statuses).forEach(([k,v])=>{
    if(!v) return;
    if(k==='tempDef' || k==='permDef') return;
    const chip=document.createElement('div');
    chip.className=`status-chip ${map[k]?.cls||'block'}`;
    chip.textContent=`${map[k]?.label||k.toUpperCase()} ${v>1?'×'+v:''}`;
    el.appendChild(chip);
  });
}

// ── function: log ──
function log(msg, type='info'){
  const c=document.getElementById('combat-log');
  const e=document.createElement('div');
  e.className='log-entry';
  if(type==='dmg') e.style.borderColor='rgba(200,80,80,.3)';
  if(type==='heal') e.style.borderColor='rgba(80,200,120,.3)';
  if(type==='def') e.style.borderColor='rgba(80,140,200,.3)';
  e.textContent=msg;
  c.appendChild(e);
  while(c.children.length>5) c.removeChild(c.firstChild);
}

// ── function: initGameState ──
function initGameState(className){
  const cdata = CLASS_DATA[className];
  const deck = [...cdata.deck];
  // add some duplicates to reach like 8-10
  window.gameState = {
    className,
    gold: 0,
    floor: 1,
    room: 1,
    isBoss: false,
    isInShop: false,
    kills: 0,
    totalDamageDealt:0,
    cardsPlayed:0,
    deckMaster:[...deck], // all owned cards
    inventory:{}, // count per card Id
    drawPile:[],
    discard:[],
    hand:[],
    player:{
      maxHp:cdata.maxHp,
      hp:cdata.maxHp,
      energy:cdata.startEnergy,
      maxEnergy:cdata.maxEnergy,
      handSize:cdata.handSize,
      tempDef:0,
      permDef:0,
      statuses:{burn:0, poison:0, stun:0, evade:0, blur:0},
      hasEvasion:false,
    },
    enemy:null,
  };
  // inventory counts
  deck.forEach(id=>{ window.gameState.inventory[id]=(window.gameState.inventory[id]||0)+1; });
  startCombat();
}

// ── function: generateEnemy ──
function generateEnemy(floor, isBoss){
  if(isBoss){
    const b = BOSSES[floor];
    return {
      name:b.name,
      hp:b.hp,
      maxHp:b.hp,
      dmg:b.dmg,
      type:null,
      mech:b.mech,
      blockPerTurn:0,
      statuses:{burn:0, poison:0, stun:0, silence:0},
    };
  } else {
    const pool = ENEMY_POOL[floor];
    const t = pool[Math.floor(Math.random()*pool.length)];
    const variance = Math.floor(Math.random()*5)-2;
    return {
      name:t.name,
      hp:t.hp+variance,
      maxHp:t.hp+variance,
      dmg:t.dmg,
      type:t.type,
      mech:null,
      statuses:{burn:0, poison:0, stun:0, silence:0},
    };
  }
}

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

// ── function: startCombat ──
function startCombat(){
  const s = window.gameState;
  s.isBoss = (s.room===4);
  s.enemy = generateEnemy(s.floor, s.isBoss);
  if(s.isBoss && s.floor===1) s.enemy.blockPerTurn=5;
  s.drawPile = shuffle([...s.deckMaster]);
  s.discard = [];
  s.hand = [];
  s.player.tempDef=0;
  // keep permDef? Actually permDef persists? Let's keep across combats for Bastion flavor but reset temp only. We'll keep permDef.
  s.player.statuses={burn:0, poison:0, stun:0, evade:0, blur:0};
  s.player.hasEvasion=false;
  s.player.energy = CLASS_DATA[s.className].startEnergy;
  // draw initial
  drawCards(s.player.handSize);
  // set portrait
  const portrait = document.getElementById('player-portrait');
  const cl = s.className;
  if(cl==='Bastion') portrait.innerHTML=`<svg viewBox="0 0 80 80"><rect x="16" y="18" width="48" height="48" rx="6" fill="#2a3540" stroke="#6b7c8d" stroke-width="2"/><path d="M32 30 L48 30 L48 46 L32 46 Z" fill="#3a4a5a"/></svg>`;
  else if(cl==='Wither') portrait.innerHTML=`<svg viewBox="0 0 80 80"><circle cx="40" cy="40" r="26" fill="#2a2215" stroke="#c46a1f" stroke-width="2"/><path d="M34 36 Q40 52 46 36" stroke="#e0904a" fill="none" stroke-width="2"/></svg>`;
  else portrait.innerHTML=`<svg viewBox="0 0 80 80"><path d="M40 12 L56 40 L40 62 L24 40 Z" fill="#1a1a2a" stroke="#8a7bb0" stroke-width="2"/><circle cx="40" cy="40" r="6" fill="#c9b8ff"/></svg>`;

  log(`Entered ${s.isBoss?'BOSS':`Room ${s.room}`} — ${s.enemy.name} appears!`);
  updateUI();
  document.querySelectorAll('.screen').forEach(sc=>sc.classList.remove('active'));
  document.getElementById('screen-combat').classList.add('active');
  document.getElementById('enemy-sprite').classList.add('enemy-float');
}

// ── function: drawCards ──
function drawCards(n){
  const s=window.gameState;
  for(let i=0;i<n;i++){
    if(s.drawPile.length===0){
      if(s.discard.length===0) break;
      s.drawPile = shuffle([...s.discard]);
      s.discard=[];
      log('Reshuffled discard into deck.', 'def');
    }
    const c = s.drawPile.pop();
    if(s.hand.length < s.player.handSize + 2){ // allow slight overflow for draw effects
      s.hand.push(c);
    } else {
      s.discard.push(c);
    }
  }
}

// ── function: playCard ──
function playCard(index){
  const s = window.gameState;
  const p = s.player;
  const enemy = s.enemy;
  if(index<0 || index>=s.hand.length) return;
  const cid = s.hand[index];
  const card = CARD_DB[cid];
  if(p.energy < card.cost){ log(`Not enough energy for ${card.name}`, 'def'); return; }
  if(p.statuses.stun>0){ log('You are stunned!', 'def'); return; }
  // pay
  p.energy -= card.cost;
  s.hand.splice(index,1);
  s.discard.push(cid);
  s.cardsPlayed++;

  // resolve per card
  resolveCardEffect(card, cid);

  // check enemy death
  if(enemy.hp <=0){
    enemyDefeated();
    return;
  }
  updateUI();
}

// ── function: resolveCardEffect ──
function resolveCardEffect(card, cid){
  const s = window.gameState;
  const p = s.player;
  const e = s.enemy;
  // Attack
  if(card.type==='Attack'){
    let dmg = card.atk;
    // Backstab bonus
    if(card.id==='SH-A01' && (e.statuses.shield||0)===0 && e.blockPerTurn===0) dmg+=1;
    // apply gatekeeper block
    let effectiveDmg = dmg;
    if(s.isBoss && s.floor===1 && e.hp>20){
      const blocked = Math.min(5, effectiveDmg);
      effectiveDmg -= blocked;
      log(`Gatekeeper blocks ${blocked} dmg!`, 'def');
    }
    // Shadow Bolt ignores block — otherwise block per turn already handled
    // apply burn/poison? Actually defense block not for enemy, but they have blockPerTurn
    e.hp -= effectiveDmg;
    s.totalDamageDealt+=effectiveDmg;
    log(`${card.name} hits ${e.name} for ${effectiveDmg}!`, 'dmg');
    document.getElementById('enemy-sprite').classList.remove('hit'); void document.getElementById('enemy-sprite').offsetWidth; document.getElementById('enemy-sprite').classList.add('hit');
    setTimeout(()=>document.getElementById('enemy-sprite').classList.remove('hit'),180);
    // specific effects
    if(card.id==='BA-A01' && p.hp>=30){ drawCards(1); log('Slam draws 1 (high HP)!','def'); }
    if(card.id==='BA-A02'){ /* AOE but single enemy now */ }
    if(card.id==='WI-A01'){ e.statuses.burn=(e.statuses.burn||0)+1; log('Burn applied!','dmg'); }
    if(card.id==='WI-A02'){ e.statuses.burn=(e.statuses.burn||0)+3; }
    if(card.id==='WI-A03'){ e.statuses.burn=(e.statuses.burn||0)+1; }
    if(card.id==='WI-A04'){ e.statuses.burn=(e.statuses.burn||0)+1; log('Pyre: enemy will burn next turn','dmg'); }
    if(card.id==='WI-A04' || card.id==='WI-A02'){} // extra
    if(card.id==='SH-A03'){ e.statuses.silence=(e.statuses.silence||0)+1; log('Silenced!','def'); }
    if(card.id==='SH-A04'){ p.energy=Math.min(p.maxEnergy, p.energy+2); log('Vengeance +2 Energy','def'); }
  }
  // Defense
  else if(card.type==='Defense'){
    // permanent vs temp
    if(card.id==='BA-D02'){ // permanent
      p.permDef+=card.def;
      // discard 1 card
      if(s.hand.length>0){ const disc=s.hand.pop(); s.discard.push(disc); log('Iron Skin discards a card','def'); }
    } else {
      p.tempDef+=card.def;
    }
    log(`${card.name} +${card.def} Block`,'def');
    if(card.id==='BA-D04'){ e.statuses.stun=(e.statuses.stun||0)+1; log('Enemy stunned!','def'); }
    if(card.id==='WI-D02'){ p.energy=Math.min(p.maxEnergy,p.energy+1); log('+1 Energy','def'); }
    if(card.id==='WI-D04'){ p.hp=Math.min(p.maxHp,p.hp+2); log('Heal 2','heal'); }
    if(card.id==='SH-D01'){ p.statuses.blur=1; log('Blur: next attack ignored','def'); }
    if(card.id==='SH-D02'){ p.energy=Math.min(p.maxEnergy,p.energy+1); }
    if(card.id==='SH-D03'){ p.hasEvasion=true; log('Evasion active','def'); }
    if(card.id==='SH-D04'){ p.statuses.blur=1; p.hasEvasion=true; }
    // Stand Firm reduces next hit by 1 -> use blockBoost status
    if(card.id==='BA-D03'){ p.statuses.blockBoost=(p.statuses.blockBoost||0)+1; }
  }
  // Other
  else {
    if(card.id==='BA-U01'){ drawCards(1); if(s.hand.length>5){ const count=s.hand.length; s.discard.push(...s.hand); s.hand=[]; log(`Search discards ${count} cards (hand>5)`,'def'); } }
    if(card.id==='BA-U02'){ p.hp=Math.min(p.maxHp,p.hp+6); log('Repair +6 HP','heal'); }
    if(card.id==='BA-U03'){ drawCards(1); p.energy=Math.min(p.maxEnergy,p.energy+1); }
    if(card.id==='BA-U04'){ s.gold+=5; log('+5 Gold','heal'); }
    if(card.id==='WI-U01'){ e.statuses.poison=(e.statuses.poison||0)+1; log('Poison applied','dmg'); }
    if(card.id==='WI-U02'){ const dmg=3; e.hp-=dmg; e.statuses.burn=(e.statuses.burn||0)+2; s.totalDamageDealt+=dmg; log(`Conflagrate ${dmg} dmg +2 Burn to all`,'dmg'); }
    if(card.id==='WI-U03'){ drawCards(1); e.statuses.stun=(e.statuses.stun||0)+1; log('Slow stuns!','def'); }
    if(card.id==='WI-U04'){ p.hp=Math.min(p.maxHp,p.hp+5); e.dmg=Math.max(1,e.dmg-1); log('Wither heal 5 and weaken','heal'); }
    if(card.id==='SH-U01'){ p.energy=Math.min(p.maxEnergy,p.energy+1); p.hasEvasion=true; log('Dash evade','def'); }
    if(card.id==='SH-U02'){ e.hp-=2; s.totalDamageDealt+=2; log('Teleport 2 dmg','dmg'); }
    if(card.id==='SH-U03'){ p.tempDef+=1; if(s.hand.length>0){ const d=s.hand.pop(); s.discard.push(d);} drawCards(2); log('Hide: discard 1 draw 2','def'); }
    if(card.id==='SH-U04'){ // 8 dmg unblockable
      let dmg=8;
      if(s.isBoss && s.floor===1 && e.hp>20) dmg=Math.max(0,dmg-5);
      e.hp-=dmg; s.totalDamageDealt+=dmg; log(`Assassinate ${dmg} unblockable!`,'dmg');
    }
  }
}

// ── function: enemyTurn ──
function enemyTurn(){
  const s=window.gameState;
  const p=s.player;
  const e=s.enemy;
  // apply burn/poison on enemy before attack
  if(e.statuses.burn>0){ const dmg=e.statuses.burn; e.hp-=dmg; log(`${e.name} burns for ${dmg}`,'dmg'); }
  if(e.statuses.poison>0){ const dmg=e.statuses.poison; e.hp-=dmg; log(`${e.name} poisoned ${dmg}`,'dmg'); }

  if(e.hp<=0){ enemyDefeated(); return; }

  // stun check
  if(e.statuses.stun>0){ log(`${e.name} is stunned, skips turn`,'def'); e.statuses.stun--; }
  else if(e.statuses.silence>0){ log(`${e.name} silenced, weak attack`,'def'); e.statuses.silence--; // halve damage?
    const dmg=Math.ceil(e.dmg/2);
    applyDamageToPlayer(dmg);
  } else {
    // boss mechs
    let dmg = e.dmg;
    if(s.isBoss){
      if(s.floor===2 && p.energy<3){ dmg=20; log('Corrupted bursts for 20 (low energy!)','dmg'); }
      if(s.floor===3){ /* regen already handled after */ }
    }
    // player evasion
    if(p.hasEvasion || p.statuses.blur>0 || p.statuses.evade>0){
      log(`${e.name} attack evaded!`,'def');
      p.hasEvasion=false;
      if(p.statuses.blur>0) p.statuses.blur--;
      if(p.statuses.evade>0) p.statuses.evade--;
    } else {
      applyDamageToPlayer(dmg);
    }
  }
  // tick burn on player
  if(p.statuses.burn>0){ p.hp-=p.statuses.burn; log(`You burn for ${p.statuses.burn}`,'dmg'); }
  if(p.statuses.poison>0){ p.hp-=p.statuses.poison; log(`Poison ${p.statuses.poison}`,'dmg'); }

  // boss 3 regen
  if(s.isBoss && s.floor===3){ e.hp=Math.min(e.maxHp, e.hp+10); log('Void Lord regenerates 10 HP!','heal'); }

  // end of turn: recover energy, discard, draw
  // temp def decays? per spec shield wall etc this turn only -> we clear tempDef each enemy turn start? But we already used it. Let's clear half? Actually spec: Gain for this turn. So clear tempDef after enemy turn.
  p.tempDef = Math.floor(p.tempDef*0.3); // some decay, but for simplicity clear most: if temp cards are like one turn, set to 0 unless perm.
  // Actually we want to clear tempDef that is not permanent. Let's just set tempDef=0 after each enemy turn, but keep some if Stand Firm? We'll just clear.
  p.tempDef=0;
  p.statuses.blockBoost=0;
  p.energy = Math.min(p.maxEnergy, p.energy+1);
  // discard hand and draw new
  s.discard.push(...s.hand);
  s.hand=[];
  drawCards(p.handSize);
  // tick stun on player
  if(p.statuses.stun>0) p.statuses.stun--;

  updateUI();
  // check player death
  if(p.hp<=0){ gameOver(false); }
}

// ── function: applyDamageToPlayer ──
function applyDamageToPlayer(raw){
  const s=window.gameState;
  const p=s.player;
  const totalDef = p.tempDef + p.permDef + (p.statuses.blockBoost||0);
  const mitigated = Math.max(0, raw - totalDef);
  // reduce temp def first
  let remain = raw;
  if(p.tempDef>0){ const used=Math.min(p.tempDef, remain); p.tempDef-=used; remain-=used; }
  if(p.permDef>0 && remain>0){ const used=Math.min(p.permDef, remain); p.permDef-=used; remain-=used; }
  // if still positive, apply to HP with blockBoost reducing 1
  let final = remain;
  if(p.statuses.blockBoost>0) final=Math.max(0, final-1);
  p.hp -= final;
  log(`${s.enemy.name} hits for ${raw} → ${final} after ${totalDef} block`,'dmg');
  const sprite=document.getElementById('enemy-sprite');
  sprite.classList.remove('attacking'); void sprite.offsetWidth; sprite.classList.add('attacking');
  setTimeout(()=>sprite.classList.remove('attacking'),220);
}

// ── function: enemyDefeated ──
function enemyDefeated(){
  const s=window.gameState;
  const reward = s.isBoss?15:5;
  s.gold+=reward;
  s.kills++;
  log(`Victory! +${reward} Gold`,'heal');
  // progress
  if(s.isBoss){
    if(s.floor===3){ gameOver(true); return; }
    s.floor++;
    s.room=1;
  } else {
    s.room++;
  }
  // maybe shop after each fight
  setTimeout(()=>openShop(), 600);
}

// ── function: openShop ──
function openShop(){
  const s=window.gameState;
  s.isInShop=true;
  renderShop('all');
  document.querySelectorAll('.screen').forEach(sc=>sc.classList.remove('active'));
  document.getElementById('screen-shop').classList.add('active');
}

// ── function: renderShop ──
function renderShop(filter='all'){
  const grid=document.getElementById('shop-grid');
  grid.innerHTML='';
  const s=window.gameState;
  const allIds = Object.keys(CARD_DB);
  let list = allIds.filter(id=>{
    const owned = s.inventory[id]||0;
    return owned<5;
  });
  if(filter!=='all'){
    list = list.filter(id=>{
      const c=CARD_DB[id];
      return c.class===filter || c.type===filter;
    });
  }
  // shuffle a bit but keep
  list = shuffle([...list]).slice(0,18);
  list.forEach(id=>{
    const card=CARD_DB[id];
    const owned=s.inventory[id]||0;
    let price = 3 + card.cost*2 + owned*2;
    if(owned>=3) price=10 + (owned-3)*2;
    if(owned>=4) price=Math.min(50, price+5);
    const wrap=document.createElement('div');
    wrap.className='shop-item';
    const el = createCardElement(id,{shop:true});
    if(s.gold < price) el.classList.add('disabled');
    const tag=document.createElement('div');
    tag.className='price-tag'+(s.gold>=price?' can':'')+(price>=10?' high':'');
    tag.textContent=`${price} ◈ ${owned?`(${owned}/5)`:''}`;
    wrap.appendChild(el);
    wrap.appendChild(tag);
    wrap.addEventListener('click', ()=>{
      if(s.gold>=price){
        s.gold-=price;
        s.deckMaster.push(id);
        s.inventory[id]=owned+1;
        log(`Bought ${card.name}`,'heal');
        renderShop(filter);
        updateUI();
        // visual
        el.style.transform='scale(0.8)';
        setTimeout(()=>renderShop(filter),120);
      }
    });
    wrap.addEventListener('mouseenter', (e)=>showTooltip(e,id));
    wrap.addEventListener('mouseleave', hideTooltip);
    grid.appendChild(wrap);
  });
}

// ── function: gameOver ──
function gameOver(victory){
  const s=window.gameState;
  document.querySelectorAll('.screen').forEach(sc=>sc.classList.remove('active'));
  const end=document.getElementById('screen-end');
  end.classList.add('active');
  document.getElementById('end-title').textContent = victory?'Shadows Banished':'You Fell Into Void';
  document.getElementById('end-desc').textContent = victory?'You have slain the Void Lord and reclaimed Aethelgard. The three floors are cleared, the hollow market closes forever.':'Your vessel shattered. The Void claims another wanderer.';
  document.getElementById('end-stats').innerHTML = `
    <div class="end-stat"><b>Floors Reached</b>${s.floor}/3 — Room ${s.room}</div>
    <div class="end-stat"><b>Gold Amassed</b>${s.gold} ◈</div>
    <div class="end-stat"><b>Enemies Slain</b>${s.kills}</div>
    <div class="end-stat"><b>Damage Dealt</b>${s.totalDamageDealt}</div>
    <div class="end-stat"><b>Deck Size</b>${s.deckMaster.length} cards</div>
    <div class="end-stat"><b>Class</b>${s.className}</div>
  `;
  // confetti-ish if victory
  if(victory){
    document.getElementById('end-title').style.color='#c9b8ff';
  }
}

// ── main ──
let shopFilter='all';
function switchScreen(id){
  document.querySelectorAll('.screen').forEach(sc=>sc.classList.remove('active'));
  document.getElementById(id).classList.add('active');
}

// class select
document.querySelectorAll('.class-card').forEach(card=>{
  card.addEventListener('click', ()=>{
    const cls = card.dataset.class;
    initGameState(cls);
  });
});

// end turn
document.getElementById('btn-end').addEventListener('click', ()=>{
  const s=window.gameState;
  if(!s || s.isInShop) return;
  enemyTurn();
});

// shop
document.getElementById('btn-shop').addEventListener('click', ()=>{ if(window.gameState) openShop(); });
document.getElementById('shop-leave').addEventListener('click', ()=>{
  const s=window.gameState;
  s.isInShop=false;
  startCombat();
});
document.getElementById('btn-abandon').addEventListener('click', ()=>{
  if(confirm('Abandon descent?')){ document.querySelectorAll('.screen').forEach(sc=>sc.classList.remove('active')); document.getElementById('screen-menu').classList.add('active'); }
});
document.getElementById('btn-restart').addEventListener('click', ()=>{
  document.querySelectorAll('.screen').forEach(sc=>sc.classList.remove('active'));
  document.getElementById('screen-menu').classList.add('active');
});
document.getElementById('btn-heal').addEventListener('click', ()=>{
  const s=window.gameState;
  if(s.gold>=8 && s.player.hp < s.player.maxHp){ s.gold-=8; s.player.hp=Math.min(s.player.maxHp, s.player.hp+10); updateUI(); document.getElementById('shop-gold').textContent=s.gold; log('Healed 10','heal'); }
});
document.querySelectorAll('.chip').forEach(ch=>{
  ch.addEventListener('click', ()=>{
    document.querySelectorAll('.chip').forEach(x=>x.classList.remove('active'));
    ch.classList.add('active');
    renderShop(ch.dataset.filter);
  });
});

// tooltip follow
let lastMouse={x:0,y:0};
window.addEventListener('mousemove', e=>{ lastMouse=e; const tt=document.getElementById('tooltip'); if(tt.style.opacity==='1'){ tt.style.left=(e.clientX+12)+'px'; tt.style.top=(e.clientY-80)+'px'; }});

// initial log
console.log('Shadows of Aethelgard loaded — 36 cards, 3 bosses, 3 frames');
</script>
</body>
</html>
<!-- agent-meta {"model":"meta/muse-spark-1.1","provider":"openrouter","persona":"composer","sessionId":"480eb690-fd48-457a-81ce-0ab1eb059055","tokensIn":430779,"tokensOut":27373,"tokensTotal":458152,"turns":16,"toolCalls":15,"failedToolCalls":1,"timestamp":"2026-07-17T04:27:23.556Z"} -->