← Deckbuilder results

Deckbuilder

Qwen 3.6 35b a3b · 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 82,914 bytes · SHA-256 5b57ef6febae
<!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: layout ── */
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #0a0a0f; }
#app { width: 100%; height: 100%; position: relative; }
#game-container { width: 100%; height: 100%; position: absolute; top: 0; left: 0; }
#game-canvas { width: 100%; height: 100%; display: block; }
#ui-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10; }
#card-backdrop { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 5; }
.screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 100; transition: opacity 0.5s; }
.screen.hidden { display: none; }
/* ── css: start-screen ── */
#start-screen {
  background: radial-gradient(ellipse at center, #1a1025 0%, #0a0a0f 70%);
  font-family: 'Georgia', serif;
}
#start-screen::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence baseFrequency='0.9' numOctaves='4'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");
  pointer-events: none;
  opacity: 0.5;
}
.game-title {
  font-size: 3.5rem;
  color: #c9a84c;
  text-shadow: 0 0 30px rgba(201, 168, 76, 0.5), 0 0 60px rgba(201, 168, 76, 0.2);
  letter-spacing: 0.15em;
  margin-bottom: 0.5rem;
  position: relative;
  z-index: 1;
}
.subtitle {
  font-size: 1.2rem;
  color: #6b5b8a;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  margin-bottom: 3rem;
  position: relative;
  z-index: 1;
}
.class-select {
  display: flex;
  gap: 1.5rem;
  margin-bottom: 2.5rem;
  position: relative;
  z-index: 1;
}
.class-option {
  background: linear-gradient(145deg, #1a1a2e, #16162a);
  border: 1px solid #2a2a4a;
  border-radius: 8px;
  padding: 1.5rem;
  width: 200px;
  cursor: pointer;
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}
.class-option::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  opacity: 0;
  transition: opacity 0.3s;
  pointer-events: none;
}
.class-option:hover {
  transform: translateY(-4px);
  border-color: #c9a84c;
}
.class-option:hover::before { opacity: 0.1; }
.class-option.selected {
  border-color: #c9a84c;
  box-shadow: 0 0 20px rgba(201, 168, 76, 0.3);
}
.class-option.selected::before { opacity: 0.1; }
.class-option.bastion-option.selected { border-color: #4a8fa8; box-shadow: 0 0 20px rgba(74, 143, 168, 0.3); }
.class-option.wither-option.selected { border-color: #c44a4a; box-shadow: 0 0 20px rgba(196, 74, 74, 0.3); }
.class-option.shroud-option.selected { border-color: #8a4ac4; box-shadow: 0 0 20px rgba(138, 74, 196, 0.3); }
.class-icon {
  width: 60px;
  height: 60px;
  margin: 0 auto 1rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.bastion-icon {
  background: radial-gradient(circle, #4a8fa8, #2a5a6a);
  box-shadow: 0 0 15px rgba(74, 143, 168, 0.4);
}
.wither-icon {
  background: radial-gradient(circle, #c44a4a, #6a2a2a);
  box-shadow: 0 0 15px rgba(196, 74, 74, 0.4);
}
.shroud-icon {
  background: radial-gradient(circle, #8a4ac4, #4a2a6a);
  box-shadow: 0 0 15px rgba(138, 74, 196, 0.4);
}
.class-option h3 {
  color: #c9a84c;
  font-size: 1.1rem;
  margin-bottom: 0.5rem;
  text-align: center;
  letter-spacing: 0.1em;
}
.class-desc {
  color: #8a7a9a;
  font-size: 0.8rem;
  text-align: center;
  margin-bottom: 0.8rem;
  line-height: 1.4;
}
.class-stats {
  color: #6b5b8a;
  font-size: 0.75rem;
  text-align: center;
  letter-spacing: 0.05em;
}
.btn-start {
  background: linear-gradient(145deg, #2a1a3a, #1a0a2a);
  border: 2px solid #c9a84c;
  color: #c9a84c;
  font-family: 'Georgia', serif;
  font-size: 1.2rem;
  padding: 1rem 3rem;
  cursor: pointer;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  transition: all 0.3s;
  position: relative;
  z-index: 1;
}
.btn-start:hover {
  background: linear-gradient(145deg, #3a2a4a, #2a1a3a);
  box-shadow: 0 0 30px rgba(201, 168, 76, 0.4);
  transform: scale(1.05);
}
/* ── css: hud ── */
#hud {
  position: absolute;
  top: 0; left: 0; right: 0;
  display: none;
  z-index: 20;
  pointer-events: none;
}
#hud.active { display: block; }

/* Top Bar */
.top-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.8rem 1.5rem;
  background: linear-gradient(180deg, rgba(10, 10, 15, 0.95), rgba(10, 10, 15, 0.7));
  border-bottom: 1px solid #2a2a4a;
  pointer-events: auto;
}
.hud-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.hud-label {
  color: #6b5b8a;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.hud-value {
  color: #c9a84c;
  font-size: 1.1rem;
  font-weight: bold;
  font-family: 'Georgia', serif;
}
.hud-value.hp { color: #c44a4a; }
.hud-value.energy { color: #4a8fa8; }
.hud-value.gold { color: #c9a84c; }
.hud-value.floor { color: #8a4ac4; }

/* Player Stats */
.player-stats {
  position: absolute;
  top: 60px;
  left: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  pointer-events: none;
}
.stat-bar {
  width: 200px;
  height: 8px;
  background: #1a1a2e;
  border: 1px solid #2a2a4a;
  border-radius: 4px;
  overflow: hidden;
}
.stat-bar-fill {
  height: 100%;
  transition: width 0.3s ease;
  border-radius: 3px;
}
.stat-bar-fill.hp-fill { background: linear-gradient(90deg, #8a2a2a, #c44a4a); }
.stat-bar-fill.defense-fill { background: linear-gradient(90deg, #2a5a6a, #4a8fa8); }
.stat-bar-fill.energy-fill { background: linear-gradient(90deg, #2a4a6a, #4a8fa8); }
.stat-bar-label {
  color: #6b5b8a;
  font-size: 0.65rem;
  margin-bottom: 2px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Enemy Display */
.enemy-display {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -60%);
  text-align: center;
  pointer-events: none;
}
.enemy-name {
  color: #8a7a9a;
  font-size: 0.9rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  margin-bottom: 0.5rem;
}
.enemy-hp-bar {
  width: 250px;
  height: 10px;
  background: #1a1a2e;
  border: 1px solid #2a2a4a;
  border-radius: 5px;
  overflow: hidden;
  margin: 0 auto;
}
.enemy-hp-fill {
  height: 100%;
  background: linear-gradient(90deg, #6a1a1a, #c44a4a);
  transition: width 0.3s ease;
  border-radius: 4px;
}
.enemy-hp-text {
  color: #c44a4a;
  font-size: 0.75rem;
  margin-top: 0.3rem;
  font-family: 'Georgia', serif;
}

/* Hand Area */
.hand-area {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  padding: 0 2rem 1rem;
  gap: 0;
  pointer-events: none;
}
.hand-card {
  width: 140px;
  height: 200px;
  cursor: pointer;
  transition: transform 0.2s, margin 0.2s;
  pointer-events: auto;
  position: relative;
  margin: 0 -10px;
}
.hand-card:hover {
  transform: translateY(-20px) scale(1.05);
  z-index: 30;
}
.hand-card.played {
  transform: translateY(-60px) scale(0.9);
  opacity: 0.7;
}
.hand-card.cannot-play {
  filter: brightness(0.5) saturate(0.3);
  cursor: not-allowed;
}

/* Energy Display */
.energy-display {
  position: absolute;
  top: 60px;
  right: 1.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  pointer-events: none;
}
.energy-orb {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #6ab4e8, #2a6a8a);
  box-shadow: 0 0 15px rgba(74, 143, 168, 0.4), inset 0 0 10px rgba(106, 180, 232, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 1.2rem;
  font-weight: bold;
  font-family: 'Georgia', serif;
}

/* Action Buttons */
.action-bar {
  position: absolute;
  bottom: 220px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 1rem;
  pointer-events: auto;
}
.action-btn {
  background: linear-gradient(145deg, #2a1a3a, #1a0a2a);
  border: 1px solid #3a2a5a;
  color: #c9a84c;
  font-family: 'Georgia', serif;
  font-size: 0.85rem;
  padding: 0.6rem 1.5rem;
  cursor: pointer;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  transition: all 0.2s;
  border-radius: 4px;
}
.action-btn:hover {
  border-color: #c9a84c;
  box-shadow: 0 0 15px rgba(201, 168, 76, 0.3);
}
.action-btn.end-turn-btn {
  background: linear-gradient(145deg, #3a1a1a, #2a0a0a);
  border-color: #6a2a2a;
  color: #c44a4a;
}
.action-btn.end-turn-btn:hover {
  border-color: #c44a4a;
  box-shadow: 0 0 15px rgba(196, 74, 74, 0.3);
}

/* Card Type Colors */
.card-attack { border-color: #8a2a2a !important; }
.card-attack .card-cost { background: #6a1a1a; }
.card-defense { border-color: #2a5a6a !important; }
.card-defense .card-cost { background: #1a4a5a; }
.card-other { border-color: #5a2a6a !important; }
.card-other .card-cost { background: #3a1a4a; }
/* ── css: cards ── */
.card-container {
  width: 140px;
  height: 200px;
  position: relative;
  border-radius: 8px;
  overflow: hidden;
  background: linear-gradient(160deg, #1a1a2e, #0f0f1a);
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(201, 168, 76, 0.1);
  transition: box-shadow 0.3s, transform 0.2s;
  cursor: pointer;
}
.card-container::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  border-radius: 8px;
  border: 2px solid;
  pointer-events: none;
  z-index: 2;
}
.card-container.card-attack::before { border-color: #8a2a2a; box-shadow: inset 0 0 20px rgba(138, 42, 42, 0.2); }
.card-container.card-defense::before { border-color: #2a5a6a; box-shadow: inset 0 0 20px rgba(42, 90, 106, 0.2); }
.card-container.card-other::before { border-color: #5a2a6a; box-shadow: inset 0 0 20px rgba(90, 42, 106, 0.2); }

.card-cost {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 0.85rem;
  font-weight: bold;
  font-family: 'Georgia', serif;
  z-index: 3;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
}
.card-attack .card-cost { background: radial-gradient(circle at 30% 30%, #c44a4a, #6a1a1a); }
.card-defense .card-cost { background: radial-gradient(circle at 30% 30%, #4a8fa8, #1a4a5a); }
.card-other .card-cost { background: radial-gradient(circle at 30% 30%, #8a4ac4, #3a1a4a); }

.card-image {
  width: 100%;
  height: 100%;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.card-image svg {
  width: 100%;
  height: 100%;
}

.card-name {
  position: absolute;
  top: 36px;
  left: 0;
  right: 0;
  text-align: center;
  color: #c9a84c;
  font-size: 0.7rem;
  font-weight: bold;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  z-index: 3;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}

.card-stats {
  position: absolute;
  bottom: 30px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  gap: 1rem;
  z-index: 3;
}
.card-stat {
  display: flex;
  align-items: center;
  gap: 3px;
}
.card-stat-value {
  color: white;
  font-size: 0.85rem;
  font-weight: bold;
  font-family: 'Georgia', serif;
}
.card-stat.atk .card-stat-value { color: #e86a4a; }
.card-stat.def .card-stat-value { color: #6ab4e8; }

.card-description {
  position: absolute;
  bottom: 12px;
  left: 8px;
  right: 8px;
  color: #8a7a9a;
  font-size: 0.55rem;
  text-align: center;
  line-height: 1.3;
  z-index: 3;
}

/* Glow effects */
.card-container.card-attack { box-shadow: 0 4px 15px rgba(138, 42, 42, 0.3); }
.card-container.card-defense { box-shadow: 0 4px 15px rgba(42, 90, 106, 0.3); }
.card-container.card-other { box-shadow: 0 4px 15px rgba(90, 42, 106, 0.3); }

/* Hand card hover */
.hand-card:hover {
  transform: translateY(-25px) scale(1.08) !important;
  z-index: 30 !important;
}
.hand-card:hover .card-container {
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.6);
}
.hand-card.cannot-play {
  filter: brightness(0.5) saturate(0.3);
  cursor: not-allowed;
}

/* Shop styling */
#shop-overlay {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(5, 5, 10, 0.92);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 50;
  pointer-events: auto;
}
#shop-overlay.active { display: flex; }
.shop-container {
  background: linear-gradient(145deg, #1a1a2e, #0f0f1a);
  border: 2px solid #2a2a4a;
  border-radius: 12px;
  padding: 2rem;
  max-width: 700px;
  width: 90%;
  box-shadow: 0 0 40px rgba(138, 74, 196, 0.2);
}
.shop-title {
  color: #c9a84c;
  font-size: 1.5rem;
  text-align: center;
  margin-bottom: 0.5rem;
  letter-spacing: 0.15em;
  font-family: 'Georgia', serif;
}
.shop-subtitle {
  color: #6b5b8a;
  font-size: 0.8rem;
  text-align: center;
  margin-bottom: 1.5rem;
  font-style: italic;
}
.shop-gold {
  color: #c9a84c;
  font-size: 1.1rem;
  text-align: center;
  margin-bottom: 1.5rem;
  font-family: 'Georgia', serif;
}
.shop-gold span { color: #c9a84c; font-weight: bold; font-size: 1.3rem; }
.shop-cards {
  display: flex;
  justify-content: center;
  gap: 1rem;
  margin-bottom: 1.5rem;
}
.shop-card {
  width: 130px;
  height: 185px;
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;
  border: 2px solid #2a2a4a;
  border-radius: 8px;
  overflow: hidden;
  background: linear-gradient(160deg, #1a1a2e, #0f0f1a);
  position: relative;
}
.shop-card:hover { transform: translateY(-10px); box-shadow: 0 8px 20px rgba(0,0,0,0.5); }
.shop-card-cost {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #4a8fa8, #1a4a5a);
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 0.75rem;
  font-weight: bold;
  font-family: 'Georgia', serif;
  z-index: 3;
}
.shop-card-name {
  position: absolute;
  top: 34px;
  left: 0;
  right: 0;
  text-align: center;
  color: #c9a84c;
  font-size: 0.65rem;
  font-weight: bold;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  z-index: 3;
}
.shop-card-desc {
  position: absolute;
  bottom: 8px;
  left: 6px;
  right: 6px;
  color: #8a7a9a;
  font-size: 0.5rem;
  text-align: center;
  line-height: 1.3;
  z-index: 3;
}
.shop-close {
  display: block;
  margin: 0 auto;
  background: linear-gradient(145deg, #2a1a3a, #1a0a2a);
  border: 1px solid #3a2a5a;
  color: #c9a84c;
  font-family: 'Georgia', serif;
  font-size: 0.85rem;
  padding: 0.5rem 2rem;
  cursor: pointer;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border-radius: 4px;
  transition: all 0.2s;
}
.shop-close:hover {
  border-color: #c9a84c;
  box-shadow: 0 0 15px rgba(201, 168, 76, 0.3);
}

/* Victory / Game Over screens */
#victory-screen {
  background: radial-gradient(ellipse at center, #1a2a1a 0%, #0a0a0f 70%);
}
.victory-title {
  font-size: 3rem;
  color: #4a8a4a;
  text-shadow: 0 0 30px rgba(74, 138, 74, 0.5);
  letter-spacing: 0.15em;
  margin-bottom: 0.5rem;
  font-family: 'Georgia', serif;
}
.victory-subtitle {
  color: #6b8a6b;
  font-size: 1.1rem;
  margin-bottom: 2rem;
  font-style: italic;
}
#gameover-screen {
  background: radial-gradient(ellipse at center, #2a1a1a 0%, #0a0a0f 70%);
}
.gameover-title {
  font-size: 3rem;
  color: #c44a4a;
  text-shadow: 0 0 30px rgba(196, 74, 74, 0.5);
  letter-spacing: 0.15em;
  margin-bottom: 0.5rem;
  font-family: 'Georgia', serif;
}
.gameover-subtitle {
  color: #8a6b6b;
  font-size: 1.1rem;
  margin-bottom: 2rem;
  font-style: italic;
}
/* ── css: atmosphere ── */
/* Atmospheric background */
#game-canvas {
  background: radial-gradient(ellipse at 50% 30%, #1a1025 0%, #0a0a0f 60%, #050508 100%);
}

/* Card back styles */
.card-back {
  width: 140px;
  height: 200px;
  border-radius: 8px;
  overflow: hidden;
  position: relative;
}
.card-back-bastion {
  background: linear-gradient(160deg, #1a2a3a, #0a1a2a);
  border: 2px solid #2a5a6a;
  box-shadow: inset 0 0 20px rgba(42, 90, 106, 0.3);
}
.card-back-wither {
  background: linear-gradient(160deg, #2a1a1a, #1a0a0a);
  border: 2px solid #6a2a2a;
  box-shadow: inset 0 0 20px rgba(106, 42, 42, 0.3);
}
.card-back-shroud {
  background: linear-gradient(160deg, #1a1a2a, #0a0a1a);
  border: 2px solid #3a2a5a;
  box-shadow: inset 0 0 20px rgba(58, 42, 90, 0.3);
}

/* Floating particles */
.particle {
  position: absolute;
  pointer-events: none;
  border-radius: 50%;
  animation: float 3s ease-in-out infinite;
}
@keyframes float {
  0%, 100% { transform: translateY(0); opacity: 0.3; }
  50% { transform: translateY(-10px); opacity: 0.7; }
}

/* Damage flash */
@keyframes damage-flash {
  0% { filter: brightness(1); }
  25% { filter: brightness(2) hue-rotate(20deg); }
  50% { filter: brightness(0.5); }
  100% { filter: brightness(1); }
}
.damage-flash { animation: damage-flash 0.4s ease-out; }

/* Card play animation */
@keyframes card-play {
  0% { transform: translateY(0) scale(1); }
  50% { transform: translateY(-80px) scale(1.1); }
  100% { transform: translateY(-120px) scale(0.8); opacity: 0; }
}
.card-playing { animation: card-play 0.5s ease-in forwards; }

/* Energy regen */
@keyframes energy-glow {
  0%, 100% { box-shadow: 0 0 15px rgba(74, 143, 168, 0.4); }
  50% { box-shadow: 0 0 25px rgba(74, 143, 168, 0.7); }
}
.energy-glow { animation: energy-glow 1s ease-in-out; }

/* Status effect indicators */
.status-indicator {
  position: absolute;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6rem;
  font-weight: bold;
  color: white;
  z-index: 10;
}
.status-burn {
  background: radial-gradient(circle, #e8a82a, #8a5a1a);
  box-shadow: 0 0 8px rgba(232, 168, 42, 0.5);
}
.status-poison {
  background: radial-gradient(circle, #4ae84a, #1a8a1a);
  box-shadow: 0 0 8px rgba(74, 232, 74, 0.5);
}
.status-stun {
  background: radial-gradient(circle, #e8e84a, #8a8a1a);
  box-shadow: 0 0 8px rgba(232, 232, 74, 0.5);
}
.status-silence {
  background: radial-gradient(circle, #8a8a8a, #4a4a4a);
  box-shadow: 0 0 8px rgba(138, 138, 138, 0.5);
}

/* HUD improvements */
#ui-overlay {
  position: absolute;
  top: 0; left: 0; width: 100%; height: 100%;
  pointer-events: none;
  z-index: 10;
}

/* Room progress indicator */
.room-progress {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 8px;
  pointer-events: none;
}
.room-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #2a2a4a;
  border: 1px solid #3a3a5a;
}
.room-dot.completed { background: #4a8a4a; border-color: #6aaa6a; }
.room-dot.current { background: #8a4ac4; border-color: #aa6aea; box-shadow: 0 0 8px rgba(138, 74, 196, 0.5); }
.room-dot.boss { background: #c44a4a; border-color: #e46a6a; }

/* Damage number animation */
.damage-number {
  position: absolute;
  color: #e84a4a;
  font-size: 24px;
  font-weight: bold;
  font-family: 'Georgia', serif;
  pointer-events: none;
  z-index: 100;
  animation: damage-number-fly 1s ease-out forwards;
  text-shadow: 0 0 10px rgba(232, 74, 74, 0.5);
}
@keyframes damage-number-fly {
  0% { transform: translateY(0) scale(1); opacity: 1; }
  100% { transform: translateY(-60px) scale(1.2); opacity: 0; }
}
.heal-number {
  color: #4ae84a;
  text-shadow: 0 0 10px rgba(74, 232, 74, 0.5);
}
.gold-number {
  color: #c9a84c;
  text-shadow: 0 0 10px rgba(201, 168, 76, 0.5);
}
</style>
</head>
<body>
<div id="game-container">
  <canvas id="game-canvas"></canvas>
  <div id="ui-overlay"></div>
  <div id="card-backdrop"></div>
</div>
<div id="start-screen" class="screen">
  <h1 class="game-title">Shadows of Aethelgard</h1>
  <p class="subtitle">A Roguelite Deckbuilder</p>
  <div class="class-select">
    <div class="class-option bastion-option" data-class="bastion">
      <div class="class-icon bastion-icon"></div>
      <h3>The Bastion</h3>
      <p class="class-desc">Immovable Object. High Defense, High Block. Focuses on durability and AoE.</p>
      <div class="class-stats">HP: 35 | Energy: 3</div>
    </div>
    <div class="class-option wither-option" data-class="wither">
      <div class="class-icon wither-icon"></div>
      <h3>The Wither</h3>
      <p class="class-desc">Rot and Burn. Status Effects, Debuff Control. Low HP, High AoE.</p>
      <div class="class-stats">HP: 25 | Energy: 5</div>
    </div>
    <div class="class-option shroud-option" data-class="shroud">
      <div class="class-icon shroud-icon"></div>
      <h3>The Shroud</h3>
      <p class="class-desc">Shadow and Speed. High Speed, Evasion. High Crit, Low Defense.</p>
      <div class="class-stats">HP: 28 | Energy: 5</div>
    </div>
  </div>
  <button id="start-btn" class="btn-start">Begin Descent</button>
</div>
<div id="victory-screen" class="screen hidden">
  <h1 class="victory-title">Floor Cleared</h1>
  <p class="victory-subtitle">The Void recedes... for now.</p>
  <button id="menu-btn" class="btn-start">Return to Menu</button>
</div>
<div id="gameover-screen" class="screen hidden">
  <h1 class="gameover-title">Consumed by the Void</h1>
  <p class="gameover-subtitle">Aethelgard falls to darkness.</p>
  <button id="retry-btn" class="btn-start">Try Again</button>
</div>
<script>
'use strict';

// ── const: CARD_DATA ──
// ============================================================
// CARD DATA - All 36 Core Cards
// ============================================================
const CARD_DATA = {
  // --- THE BASTION ---
  "BA-A01": { id: "BA-A01", name: "Slam", class: "bastion", type: "attack", cost: 2, atk: 3, def: 0, desc: "Deals 3 damage. If you have 30+ HP, draw 1 card." },
  "BA-A02": { id: "BA-A02", name: "Earthquake", class: "bastion", type: "attack", cost: 3, atk: 4, def: 0, desc: "Deals 4 damage to all enemies." },
  "BA-A03": { id: "BA-A03", name: "Stone Fist", class: "bastion", type: "attack", cost: 1, atk: 2, def: 0, desc: "Deals 2 damage." },
  "BA-A04": { id: "BA-A04", name: "Boulder", class: "bastion", type: "attack", cost: 4, atk: 6, def: 0, desc: "Deals 6 damage. Costs 4 Energy." },
  "BA-D01": { id: "BA-D01", name: "Shield Wall", class: "bastion", type: "defense", cost: 0, atk: 0, def: 4, desc: "Gain 4 Defense this turn." },
  "BA-D02": { id: "BA-D02", name: "Iron Skin", class: "bastion", type: "defense", cost: 2, atk: 0, def: 5, desc: "Gain 5 Defense permanently. Discard 1 card." },
  "BA-D03": { id: "BA-D03", name: "Stand Firm", class: "bastion", type: "defense", cost: 1, atk: 0, def: 2, desc: "Gain 2 Defense. Blocks 1 incoming damage." },
  "BA-D04": { id: "BA-D04", name: "Taunt", class: "bastion", type: "defense", cost: 2, atk: 0, def: 3, desc: "Gain 3 Defense. Stun 1 enemy for 1 turn." },
  "BA-U01": { id: "BA-U01", name: "Search", class: "bastion", type: "other", cost: 2, atk: 0, def: 0, desc: "Draw 1 card. Discard hand if > 5 cards." },
  "BA-U02": { id: "BA-U02", name: "Repair", class: "bastion", type: "other", cost: 3, atk: 0, def: 0, desc: "Heal 6 HP." },
  "BA-U03": { id: "BA-U03", name: "Rally", class: "bastion", type: "other", cost: 1, atk: 0, def: 0, desc: "Draw 1 card. Gain 1 Energy." },
  "BA-U04": { id: "BA-U04", name: "Loot", class: "bastion", type: "other", cost: 2, atk: 0, def: 0, desc: "Gain 5 Gold." },

  // --- THE WITHER ---
  "WI-A01": { id: "WI-A01", name: "Fireball", class: "wither", type: "attack", cost: 2, atk: 3, def: 0, desc: "Deals 3 damage. Apply 1 Burn." },
  "WI-A02": { id: "WI-A02", name: "Inferno", class: "wither", type: "attack", cost: 3, atk: 4, def: 0, desc: "Deals 4 damage. Applies 3 Burn." },
  "WI-A03": { id: "WI-A03", name: "Ember", class: "wither", type: "attack", cost: 1, atk: 2, def: 0, desc: "Deals 2 damage. 1 damage/turn to target." },
  "WI-A04": { id: "WI-A04", name: "Pyre", class: "wither", type: "attack", cost: 4, atk: 5, def: 0, desc: "Deals 5 damage. All enemies take 1 dmg next turn." },
  "WI-D01": { id: "WI-D01", name: "Scorch Shield", class: "wither", type: "defense", cost: 2, atk: 0, def: 3, desc: "Gain 3 Defense. Block 1 Burn damage." },
  "WI-D02": { id: "WI-D02", name: "Heat Resist", class: "wither", type: "defense", cost: 1, atk: 0, def: 1, desc: "Gain 1 Defense. Gain 1 Energy." },
  "WI-D03": { id: "WI-D03", name: "Smoke", class: "wither", type: "defense", cost: 2, atk: 0, def: 4, desc: "Gain 4 Defense. Block 1 Burn damage." },
  "WI-D04": { id: "WI-D04", name: "Endure", class: "wither", type: "defense", cost: 3, atk: 0, def: 2, desc: "Gain 2 Defense. Heal 2 HP." },
  "WI-U01": { id: "WI-U01", name: "Poison", class: "wither", type: "other", cost: 2, atk: 0, def: 0, desc: "Apply 1 Poison to target." },
  "WI-U02": { id: "WI-U02", name: "Burn", class: "wither", type: "other", cost: 3, atk: 0, def: 0, desc: "Deal 3 damage + 2 Burn to all enemies." },
  "WI-U03": { id: "WI-U03", name: "Slow", class: "wither", type: "other", cost: 1, atk: 0, def: 0, desc: "Draw 1 card. Stun target." },
  "WI-U04": { id: "WI-U04", name: "Wither", class: "wither", type: "other", cost: 2, atk: 0, def: 0, desc: "Heal 5 HP. Reduce enemy Attack by 1." },

  // --- THE SHROUD ---
  "SH-A01": { id: "SH-A01", name: "Backstab", class: "shroud", type: "attack", cost: 2, atk: 4, def: 0, desc: "Deals 4 damage. +1 if target has no Armor." },
  "SH-A02": { id: "SH-A02", name: "Shadow Bolt", class: "shroud", type: "attack", cost: 3, atk: 5, def: 0, desc: "Deals 5 damage. Target cannot block." },
  "SH-A03": { id: "SH-A03", name: "Silence", class: "shroud", type: "attack", cost: 1, atk: 3, def: 0, desc: "Deals 3 damage. Stun target." },
  "SH-A04": { id: "SH-A04", name: "Vengeance", class: "shroud", type: "attack", cost: 3, atk: 6, def: 0, desc: "Deals 6 damage. Gain 2 Energy." },
  "SH-D01": { id: "SH-D01", name: "Blur", class: "shroud", type: "defense", cost: 3, atk: 0, def: 2, desc: "Gain 2 Defense. Ignore next attack." },
  "SH-D02": { id: "SH-D02", name: "Step", class: "shroud", type: "defense", cost: 2, atk: 0, def: 3, desc: "Gain 3 Defense. Gain 1 Energy." },
  "SH-D03": { id: "SH-D03", name: "Evasion", class: "shroud", type: "defense", cost: 1, atk: 0, def: 1, desc: "Gain 1 Defense. Skip enemy action." },
  "SH-D04": { id: "SH-D04", name: "Phantom", class: "shroud", type: "defense", cost: 2, atk: 0, def: 4, desc: "Gain 4 Defense. Ghost form 1 turn." },
  "SH-U01": { id: "SH-U01", name: "Dash", class: "shroud", type: "other", cost: 1, atk: 0, def: 0, desc: "Gain 1 Energy. Skip enemy attack." },
  "SH-U02": { id: "SH-U02", name: "Teleport", class: "shroud", type: "other", cost: 2, atk: 0, def: 0, desc: "Deal 2 damage. Move to back row." },
  "SH-U03": { id: "SH-U03", name: "Hide", class: "shroud", type: "other", cost: 2, atk: 0, def: 0, desc: "Gain 1 Defense. Discard 1. Draw 2." },
  "SH-U04": { id: "SH-U04", name: "Assassinate", class: "shroud", type: "other", cost: 3, atk: 0, def: 0, desc: "Deal 8 damage. Cannot be blocked." },
};

// ── const: CLASS_DECKS ──
// Class starter decks
const CLASS_DECKS = {
  bastion: ["BA-A01","BA-A02","BA-A03","BA-A04","BA-D01","BA-D02","BA-D03","BA-D04","BA-U01","BA-U02","BA-U03","BA-U04"],
  wither: ["WI-A01","WI-A02","WI-A03","WI-A04","WI-D01","WI-D02","WI-D03","WI-D04","WI-U01","WI-U02","WI-U03","WI-U04"],
  shroud: ["SH-A01","SH-A02","SH-A03","SH-A04","SH-D01","SH-D02","SH-D03","SH-D04","SH-U01","SH-U02","SH-U03","SH-U04"],
};

// ── const: CLASS_CONFIG ──
const CLASS_CONFIG = {
  bastion: { startHP: 35, startEnergy: 3, maxEnergy: 5, color: "#4a8fa8" },
  wither: { startHP: 25, startEnergy: 5, maxEnergy: 5, color: "#c44a4a" },
  shroud: { startHP: 28, startEnergy: 5, maxEnergy: 5, color: "#8a4ac4" },
};

// ── const: ENEMY_TYPES ──
// Enemy types per floor
const ENEMY_TYPES = [
  { name: "Goblin", floor: 1, hp: 20, damage: 3, color: "#4a8a4a" },
  { name: "Rat", floor: 1, hp: 15, damage: 2, color: "#6a6a4a" },
  { name: "Void Warrior", floor: 3, hp: 40, damage: 8, color: "#4a2a6a" },
  { name: "Cultist", floor: 3, hp: 35, damage: 7, color: "#6a4a2a" },
];

// ── const: FLOOR_ENEMIES ──
const FLOOR_ENEMIES = {
  1: [{ name: "Goblin", hp: 20, damage: 3, color: "#4a8a4a" }, { name: "Rat", hp: 15, damage: 2, color: "#6a6a4a" }, { name: "Void Rat", hp: 18, damage: 3, color: "#5a5a3a" }],
  2: [{ name: "Vampire Knight", hp: 30, damage: 5, color: "#8a2a2a" }, { name: "Ogre", hp: 35, damage: 6, color: "#4a6a4a" }, { name: "Shadow Stalker", hp: 28, damage: 5, color: "#3a3a6a" }],
  3: [{ name: "Void Warrior", hp: 40, damage: 8, color: "#4a2a6a" }, { name: "Cultist", hp: 35, damage: 7, color: "#6a4a2a" }, { name: "Void Fiend", hp: 45, damage: 9, color: "#2a1a4a" }],
};

// ── const: BOSSES ──
const BOSSES = {
  1: { name: "The Gatekeeper", hp: 80, damage: 15, color: "#6a6a8a", desc: "Blocks 5 damage per turn until HP < 20" },
  2: { name: "The Corrupted", hp: 80, damage: 15, color: "#8a2a6a", desc: "Deals 20 damage if you have < 3 Energy" },
  3: { name: "The Void Lord", hp: 80, damage: 15, color: "#2a0a4a", desc: "Regenerates 10 HP per turn. Cannot block." },
};

// ── function: getCardFrameSVG ──
// ============================================================
// SVG CARD FRAMES - Unique per class
// ============================================================
function getCardFrameSVG(cardData) {
  const cls = cardData.class;
  const type = cardData.type;
  
  // Base frame colors per class
  const frameColors = {
    bastion: { outer: "#2a5a6a", inner: "#1a3a4a", accent: "#4a8fa8", glow: "rgba(74,143,168,0.3)" },
    wither: { outer: "#6a2a2a", inner: "#3a1a1a", accent: "#c44a4a", glow: "rgba(196,74,74,0.3)" },
    shroud: { outer: "#3a2a5a", inner: "#1a1a3a", accent: "#8a4ac4", glow: "rgba(138,74,196,0.3)" },
  };
  const colors = frameColors[cls];
  
  // Type-specific accent
  let typeAccent = colors.accent;
  if (type === "attack") typeAccent = "#c44a4a";
  else if (type === "defense") typeAccent = "#4a8fa8";
  else typeAccent = "#8a4ac4";

  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 200" width="140" height="200">
    <defs>
      <linearGradient id="frame-bg-${cls}-${cardData.id}" x1="0%" y1="0%" x2="100%" y2="100%">
        <stop offset="0%" style="stop-color:${colors.inner}"/>
        <stop offset="100%" style="stop-color:#0a0a1a"/>
      </linearGradient>
      <linearGradient id="frame-border-${cls}-${cardData.id}" x1="0%" y1="0%" x2="100%" y2="100%">
        <stop offset="0%" style="stop-color:${typeAccent}"/>
        <stop offset="50%" style="stop-color:${colors.accent}"/>
        <stop offset="100%" style="stop-color:${typeAccent}"/>
      </linearGradient>
      <radialGradient id="card-glow-${cls}-${cardData.id}" cx="50%" cy="30%" r="60%">
        <stop offset="0%" style="stop-color:${colors.glow}"/>
        <stop offset="100%" style="stop-color:transparent"/>
      </radialGradient>
      <filter id="inner-shadow-${cls}-${cardData.id}">
        <feGaussianBlur in="SourceAlpha" stdDeviation="3" result="blur"/>
        <feOffset dx="0" dy="2" result="offsetBlur"/>
        <feComposite in="SourceGraphic" in2="offsetBlur" operator="over"/>
      </filter>
    </defs>
    <!-- Outer glow -->
    <rect x="0" y="0" width="140" height="200" rx="8" fill="url(#card-glow-${cls}-${cardData.id})"/>
    <!-- Card background -->
    <rect x="4" y="4" width="132" height="192" rx="6" fill="url(#frame-bg-${cls}-${cardData.id})" stroke="url(#frame-border-${cls}-${cardData.id})" stroke-width="2"/>
    <!-- Decorative corner flourishes -->
    <path d="M12 12 L24 12 L24 16 L16 16 L16 20 L12 20 Z" fill="${colors.accent}" opacity="0.4"/>
    <path d="M128 12 L120 12 L120 16 L128 16 L128 20 L132 20 Z" fill="${colors.accent}" opacity="0.4"/>
    <path d="M12 188 L24 188 L24 184 L16 184 L16 180 L12 180 Z" fill="${colors.accent}" opacity="0.4"/>
    <path d="M128 188 L120 188 L120 184 L128 184 L128 180 L132 180 Z" fill="${colors.accent}" opacity="0.4"/>
    <!-- Image area background -->
    <rect x="14" y="38" width="112" height="90" rx="4" fill="#0a0a15" stroke="${colors.accent}" stroke-width="0.5" opacity="0.8"/>
    <!-- Image area decorative border -->
    <rect x="18" y="42" width="104" height="82" rx="3" fill="none" stroke="${colors.accent}" stroke-width="0.3" opacity="0.3"/>
    <!-- Center emblem per class -->
    ${getCardEmblemSVG(cls, type)}
    <!-- Bottom stat area -->
    <rect x="14" y="136" width="112" height="56" rx="4" fill="rgba(10,10,20,0.6)"/>
    <line x1="14" y1="136" x2="126" y2="136" stroke="${colors.accent}" stroke-width="0.5" opacity="0.5"/>
    <!-- Attack/Defense icons at bottom -->
    ${type === "attack" ? `<circle cx="30" cy="168" r="10" fill="none" stroke="#c44a4a" stroke-width="1.5" opacity="0.6"/><text x="30" y="172" text-anchor="middle" fill="#c44a4a" font-size="12" font-weight="bold" font-family="Georgia">${cardData.atk}</text>` : `<circle cx="30" cy="168" r="10" fill="none" stroke="#4a8fa8" stroke-width="1.5" opacity="0.6"/><text x="30" y="172" text-anchor="middle" fill="#4a8fa8" font-size="12" font-weight="bold" font-family="Georgia">${cardData.def}</text>`}
    ${type !== "other" && type !== "attack" ? `<circle cx="100" cy="168" r="10" fill="none" stroke="#4a8fa8" stroke-width="1.5" opacity="0.6"/><text x="100" y="172" text-anchor="middle" fill="#4a8fa8" font-size="12" font-weight="bold" font-family="Georgia">${cardData.def}</text>` : ''}
    ${type === "other" ? `<text x="70" y="172" text-anchor="middle" fill="#8a4ac4" font-size="10" font-family="Georgia">✦ EFFECT ✦</text>` : ''}
  </svg>`;
}

// ── function: getCardEmblemSVG ──
function getCardEmblemSVG(cls, type) {
  if (cls === "bastion") {
    if (type === "attack") {
      return `<svg viewBox="0 0 104 82" width="104" height="82">
        <polygon points="52,10 80,60 24,60" fill="none" stroke="#4a8fa8" stroke-width="1.5" opacity="0.5"/>
        <line x1="52" y1="10" x2="52" y2="60" stroke="#4a8fa8" stroke-width="1" opacity="0.3"/>
        <circle cx="52" cy="38" r="12" fill="none" stroke="#c44a4a" stroke-width="1" opacity="0.4"/>
        <line x1="44" y1="38" x2="60" y2="38" stroke="#c44a4a" stroke-width="1.5" opacity="0.5"/>
        <line x1="52" y1="30" x2="52" y2="46" stroke="#c44a4a" stroke-width="1.5" opacity="0.5"/>
      </svg>`;
    } else if (type === "defense") {
      return `<svg viewBox="0 0 104 82" width="104" height="82">
        <path d="M52,15 L75,30 L75,55 Q75,70 52,78 Q29,70 29,55 L29,30 Z" fill="none" stroke="#4a8fa8" stroke-width="1.5" opacity="0.5"/>
        <path d="M52,25 L65,35 L65,52 Q65,62 52,68 Q39,62 39,52 L39,35 Z" fill="none" stroke="#4a8fa8" stroke-width="1" opacity="0.3"/>
        <line x1="52" y1="25" x2="52" y2="68" stroke="#4a8fa8" stroke-width="0.5" opacity="0.2"/>
      </svg>`;
    } else {
      return `<svg viewBox="0 0 104 82" width="104" height="82">
        <circle cx="52" cy="41" r="20" fill="none" stroke="#8a4ac4" stroke-width="1" opacity="0.4"/>
        <circle cx="52" cy="41" r="12" fill="none" stroke="#8a4ac4" stroke-width="0.5" opacity="0.3"/>
        <circle cx="52" cy="41" r="4" fill="#8a4ac4" opacity="0.3"/>
        <line x1="52" y1="15" x2="52" y2="21" stroke="#8a4ac4" stroke-width="0.5" opacity="0.3"/>
        <line x1="52" y1="61" x2="52" y2="67" stroke="#8a4ac4" stroke-width="0.5" opacity="0.3"/>
        <line x1="26" y1="41" x2="32" y2="41" stroke="#8a4ac4" stroke-width="0.5" opacity="0.3"/>
        <line x1="72" y1="41" x2="78" y2="41" stroke="#8a4ac4" stroke-width="0.5" opacity="0.3"/>
      </svg>`;
    }
  } else if (cls === "wither") {
    if (type === "attack") {
      return `<svg viewBox="0 0 104 82" width="104" height="82">
        <path d="M52,10 Q65,30 70,45 Q75,60 52,75 Q29,60 34,45 Q39,30 52,10Z" fill="none" stroke="#c44a4a" stroke-width="1.5" opacity="0.5"/>
        <path d="M52,20 Q60,35 63,45 Q66,55 52,65 Q38,55 41,45 Q44,35 52,20Z" fill="none" stroke="#e8a84a" stroke-width="1" opacity="0.4"/>
        <circle cx="52" cy="45" r="6" fill="#e8a84a" opacity="0.3"/>
      </svg>`;
    } else if (type === "defense") {
      return `<svg viewBox="0 0 104 82" width="104" height="82">
        <path d="M30,20 Q52,10 74,20 L74,50 Q74,65 52,75 Q30,65 30,50Z" fill="none" stroke="#c44a4a" stroke-width="1.5" opacity="0.4"/>
        <path d="M38,28 Q52,22 66,28 L66,48 Q66,58 52,66 Q38,58 38,48Z" fill="none" stroke="#e8a84a" stroke-width="0.8" opacity="0.3"/>
      </svg>`;
    } else {
      return `<svg viewBox="0 0 104 82" width="104" height="82">
        <circle cx="52" cy="41" r="18" fill="none" stroke="#c44a4a" stroke-width="1" opacity="0.3"/>
        <circle cx="52" cy="41" r="10" fill="none" stroke="#e8a84a" stroke-width="0.8" opacity="0.3"/>
        <circle cx="52" cy="41" r="4" fill="#e8a84a" opacity="0.2"/>
        <path d="M35,25 Q52,15 69,25" fill="none" stroke="#c44a4a" stroke-width="0.5" opacity="0.3"/>
        <path d="M35,57 Q52,67 69,57" fill="none" stroke="#c44a4a" stroke-width="0.5" opacity="0.3"/>
      </svg>`;
    }
  } else if (cls === "shroud") {
    if (type === "attack") {
      return `<svg viewBox="0 0 104 82" width="104" height="82">
        <path d="M52,10 L58,30 L78,35 L62,48 L66,68 L52,58 L38,68 L42,48 L26,35 L46,30Z" fill="none" stroke="#8a4ac4" stroke-width="1.5" opacity="0.5"/>
        <circle cx="52" cy="41" r="8" fill="none" stroke="#c44a4a" stroke-width="1" opacity="0.4"/>
        <circle cx="52" cy="41" r="3" fill="#c44a4a" opacity="0.3"/>
      </svg>`;
    } else if (type === "defense") {
      return `<svg viewBox="0 0 104 82" width="104" height="82">
        <ellipse cx="52" cy="41" rx="22" ry="28" fill="none" stroke="#8a4ac4" stroke-width="1.5" opacity="0.4"/>
        <ellipse cx="52" cy="41" rx="14" ry="18" fill="none" stroke="#8a4ac4" stroke-width="0.8" opacity="0.3"/>
        <circle cx="52" cy="41" r="5" fill="none" stroke="#6ab4e8" stroke-width="0.8" opacity="0.3"/>
      </svg>`;
    } else {
      return `<svg viewBox="0 0 104 82" width="104" height="82">
        <circle cx="52" cy="41" r="18" fill="none" stroke="#8a4ac4" stroke-width="1" opacity="0.3"/>
        <path d="M52,23 L56,35 L68,35 L58,42 L62,54 L52,47 L42,54 L46,42 L36,35 L48,35Z" fill="none" stroke="#6ab4e8" stroke-width="0.8" opacity="0.3"/>
      </svg>`;
    }
  }
  return `<circle cx="52" cy="41" r="15" fill="none" stroke="#c9a84c" stroke-width="1" opacity="0.3"/>`;
}

// ── function: getCardBackSVG ──
// ============================================================
// SVG CARD BACKS - Unique per class
// ============================================================
function getCardBackSVG(cls) {
  const colors = {
    bastion: { bg: "#1a2a3a", border: "#2a5a6a", accent: "#4a8fa8", inner: "#0a1a2a" },
    wither: { bg: "#2a1a1a", border: "#6a2a2a", accent: "#c44a4a", inner: "#1a0a0a" },
    shroud: { bg: "#1a1a2a", border: "#3a2a5a", accent: "#8a4ac4", inner: "#0a0a1a" },
  };
  const c = colors[cls];
  
  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 200" width="140" height="200">
    <defs>
      <linearGradient id="back-bg-${cls}" x1="0%" y1="0%" x2="100%" y2="100%">
        <stop offset="0%" style="stop-color:${c.bg}"/>
        <stop offset="100%" style="stop-color:${c.inner}"/>
      </linearGradient>
      <radialGradient id="back-glow-${cls}" cx="50%" cy="50%" r="50%">
        <stop offset="0%" style="stop-color:${c.accent};stop-opacity:0.15"/>
        <stop offset="100%" style="stop-color:transparent"/>
      </radialGradient>
    </defs>
    <rect x="0" y="0" width="140" height="200" rx="8" fill="url(#back-bg-${cls})" stroke="${c.border}" stroke-width="2"/>
    <rect x="0" y="0" width="140" height="200" rx="8" fill="url(#back-glow-${cls})"/>
    <!-- Decorative pattern -->
    <rect x="8" y="8" width="124" height="184" rx="4" fill="none" stroke="${c.accent}" stroke-width="0.5" opacity="0.3"/>
    <rect x="14" y="14" width="112" height="172" rx="3" fill="none" stroke="${c.accent}" stroke-width="0.3" opacity="0.2"/>
    <!-- Center emblem -->
    ${cls === "bastion" ? `<circle cx="70" cy="100" r="25" fill="none" stroke="${c.accent}" stroke-width="1.5" opacity="0.4"/>
      <circle cx="70" cy="100" r="15" fill="none" stroke="${c.accent}" stroke-width="1" opacity="0.3"/>
      <path d="M70,75 L85,100 L70,125 L55,100Z" fill="none" stroke="${c.accent}" stroke-width="1" opacity="0.4"/>
      <line x1="70" y1="75" x2="70" y2="125" stroke="${c.accent}" stroke-width="0.5" opacity="0.3"/>` : ''}
    ${cls === "wither" ? `<circle cx="70" cy="100" r="25" fill="none" stroke="${c.accent}" stroke-width="1.5" opacity="0.4"/>
      <path d="M70,78 Q82,95 82,105 Q82,120 70,125 Q58,120 58,105 Q58,95 70,78Z" fill="none" stroke="${c.accent}" stroke-width="1" opacity="0.4"/>
      <circle cx="70" cy="100" r="8" fill="none" stroke="#e8a84a" stroke-width="0.8" opacity="0.3"/>` : ''}
    ${cls === "shroud" ? `<circle cx="70" cy="100" r="25" fill="none" stroke="${c.accent}" stroke-width="1.5" opacity="0.4"/>
      <path d="M70,75 L78,90 L93,90 L81,98 L85,113 L70,105 L55,113 L59,98 L47,90 L62,90Z" fill="none" stroke="${c.accent}" stroke-width="1" opacity="0.4"/>
      <circle cx="70" cy="100" r="5" fill="${c.accent}" opacity="0.2"/>` : ''}
    <!-- Corner ornaments -->
    <path d="M20,20 L30,20 L30,24 L24,24 L24,30 L20,30Z" fill="${c.accent}" opacity="0.2"/>
    <path d="M120,20 L110,20 L110,24 L116,24 L116,30 L120,30Z" fill="${c.accent}" opacity="0.2"/>
    <path d="M20,180 L30,180 L30,176 L24,176 L24,170 L20,170Z" fill="${c.accent}" opacity="0.2"/>
    <path d="M120,180 L110,180 L110,176 L116,176 L116,170 L120,170Z" fill="${c.accent}" opacity="0.2"/>
  </svg>`;
}

// ── let: gameState ──
// ============================================================
// GAME STATE
// ============================================================
let gameState = null;

// ── function: createGameState ──
function createGameState(className) {
  const config = CLASS_CONFIG[className];
  const deckIds = CLASS_DECKS[className];
  
  // Build deck from starter cards
  let deck = [];
  for (let i = 0; i < 2; i++) {
    for (let id of deckIds) {
      deck.push({ ...CARD_DATA[id], uid: `${id}-${i}` });
    }
  }
  
  gameState = {
    className: className,
    hero: {
      hp: config.startHP,
      maxHP: config.startHP,
      energy: config.startEnergy,
      maxEnergy: config.maxEnergy,
      gold: 0,
      defense: 0,
      permanentDefense: 0,
      deck: deck,
      discardPile: [],
      hand: [],
      drawnThisTurn: 0,
    },
    inventory: {}, // cardId -> count
    currentFloor: 1,
    currentRoom: 1,
    totalRoomsPerFloor: 3,
    isBossRoom: false,
    enemy: null,
    enemyDefense: 0,
    enemyStatusEffects: { burn: 0, poison: 0, stun: 0 },
    enemyAttackReduction: 0,
    playerDefenseThisTurn: 0,
    playerBlockedThisTurn: 0,
    cardsPlayed: [],
    turnPhase: "player", // player, enemy, end
    shopOpen: false,
    gameOver: false,
    victory: false,
    statusEffects: { burn: 0, poison: 0, stun: 0, silence: 0 },
    damageFlash: null,
    cardAnimations: [],
  };
  
  return gameState;
}

// ── function: drawCards ──
// ============================================================
// COMBAT SYSTEM
// ============================================================
function drawCards(count) {
  const gs = gameState;
  const hero = gs.hero;
  
  for (let i = 0; i < count; i++) {
    if (hero.deck.length === 0) {
      // Reshuffle discard into deck
      hero.deck = [...hero.discardPile];
      hero.discardPile = [];
      // Shuffle
      for (let j = hero.deck.length - 1; j > 0; j--) {
        const k = Math.floor(Math.random() * (j + 1));
        [hero.deck[j], hero.deck[k]] = [hero.deck[k], hero.deck[j]];
      }
    }
    if (hero.deck.length > 0) {
      const card = hero.deck.pop();
      if (!hero.hand.find(h => h.uid === card.uid)) {
        hero.hand.push(card);
      }
    }
  }
  hero.drawnThisTurn += count;
}

// ── function: endTurn ──
function endTurn() {
  const gs = gameState;
  const hero = gs.hero;
  
  // Process status effects on enemy
  if (gs.enemyStatusEffects.burn > 0) {
    let burnDmg = gs.enemyStatusEffects.burn;
    if (gs.burnBlock) {
      burnDmg = Math.max(0, burnDmg - gs.burnBlock);
    }
    if (gs.enemy) {
      gs.enemy.hp = Math.max(0, gs.enemy.hp - burnDmg);
    }
    gs.burnBlock = 0;
  }
  if (gs.enemyStatusEffects.poison > 0) {
    if (gs.enemy) {
      gs.enemy.hp = Math.max(0, gs.enemy.hp - gs.enemyStatusEffects.poison);
    }
  }
  
  // Process next turn burn
  if (gs._nextTurnBurn > 0 && gs.enemy) {
    gs.enemy.hp = Math.max(0, gs.enemy.hp - gs._nextTurnBurn);
    gs._nextTurnBurn = 0;
  }
  
  // Check enemy death
  if (gs.enemy && gs.enemy.hp <= 0) {
    onEnemyDefeated();
    return;
  }
  
  // Enemy turn
  if (gs.enemy && gs.enemyStatusEffects.stun <= 0 && !gs.skipEnemyAttack) {
    performEnemyAttack();
  } else {
    gs.skipEnemyAttack = false;
  }
  
  // Check player death
  if (hero.hp <= 0) {
    gs.gameOver = true;
    showGameOver();
    return;
  }
  
  // Check boss regen (Boss 3)
  if (gs.isBossRoom && gs.currentFloor === 3 && gs.enemy) {
    gs.enemy.hp = Math.min(gs.enemy.hp + 10, 80);
  }
  
  // Discard excess hand
  if (hero.hand.length > 5) {
    const excess = hero.hand.splice(5);
    hero.discardPile.push(...excess);
  }
  
  // Reset for next turn
  hero.energy = Math.min(hero.maxEnergy, hero.energy + 1);
  hero.defense = hero.permanentDefense; // Reset to permanent only
  
  // Reduce stun
  if (gs.enemyStatusEffects.stun > 0) gs.enemyStatusEffects.stun--;
  if (gs.enemyStatusEffects.burn > 0) gs.enemyStatusEffects.burn--;
  if (gs.enemyStatusEffects.poison > 0) gs.enemyStatusEffects.poison--;
  
  // Boss mechanics
  if (gs.isBossRoom && gs.currentFloor === 1 && gs.enemy) {
    // Boss 1: Block 5 damage per turn until HP < 20
    if (gs.enemy.hp >= 20) {
      gs.enemyDefense = Math.max(gs.enemyDefense, 5);
    }
  }
  if (gs.isBossRoom && gs.currentFloor === 2 && gs.enemy) {
    // Boss 2: Deal 20 damage if player has < 3 energy
    if (hero.energy < 3) {
      let dmg = 20 - hero.defense;
      hero.hp = Math.max(0, hero.hp - Math.max(0, dmg));
    }
  }
  
  // Reset temporary values
  gs.playerBlockedThisTurn = 0;
  gs.burnBlock = 0;
  gs.skipEnemyAttack = false;
  
  // Draw cards for next turn
  const drawCount = Math.min(5 - hero.hand.length, hero.deck.length);
  if (drawCount > 0) {
    drawCards(drawCount);
  }
  
  gs.turnPhase = "player";
  gs.cardsPlayed = [];
  hero.drawnThisTurn = 0;
  
  renderGame();
}

// ── function: performEnemyAttack ──
function performEnemyAttack() {
  const gs = gameState;
  const hero = gs.hero;
  const enemy = gs.enemy;
  
  if (!enemy) return;
  
  let damage = enemy.damage - (gs.enemyAttackReduction || 0);
  damage = Math.max(1, damage);
  
  // Boss 1 mechanic: extra block
  if (gs.isBossRoom && gs.currentFloor === 1 && enemy.hp >= 20) {
    gs.enemyDefense = Math.max(gs.enemyDefense, 5);
  }
  
  // Apply player defense
  let finalDamage = Math.max(0, damage - hero.defense);
  
  // Apply blocked damage
  if (gs.playerBlockedThisTurn > 0) {
    finalDamage = Math.max(0, finalDamage - gs.playerBlockedThisTurn);
  }
  
  hero.hp = Math.max(0, hero.hp - finalDamage);
  gs.damageFlash = "player";
  
  // Check player death
  if (hero.hp <= 0) {
    gs.gameOver = true;
    showGameOver();
  }
}

// ── function: onEnemyDefeated ──
function onEnemyDefeated() {
  const gs = gameState;
  const hero = gs.hero;
  
  // Gold reward
  const goldReward = gs.isBossRoom ? 15 : 5;
  hero.gold += goldReward;
  
  // Track inventory
  if (!gs.inventory) gs.inventory = {};
  
  // Move to next room
  gs.currentRoom++;
  
  if (gs.currentRoom > gs.totalRoomsPerFloor) {
    // Floor complete
    gs.currentFloor++;
    gs.currentRoom = 1;
    
    if (gs.currentFloor > 3) {
      // Victory!
      gs.victory = true;
      showVictory();
      return;
    }
  }
  
  // Check if next room is boss
  gs.isBossRoom = (gs.currentRoom === gs.totalRoomsPerFloor);
  
  // Generate new enemy
  generateEnemy();
  
  // Reset turn state
  gs.turnPhase = "player";
  gs.cardsPlayed = [];
  gs.playerBlockedThisTurn = 0;
  gs.burnBlock = 0;
  gs.skipEnemyAttack = false;
  gs.enemyDefense = 0;
  gs.enemyStatusEffects = { burn: 0, poison: 0, stun: 0 };
  gs.enemyAttackReduction = 0;
  gs._nextTurnBurn = 0;
  
  // Reset player defense to permanent
  hero.defense = hero.permanentDefense;
  
  // Draw cards
  hero.energy = Math.min(hero.maxEnergy, hero.energy + 1);
  const drawCount = Math.min(5 - hero.hand.length, hero.deck.length);
  if (drawCount > 0) drawCards(drawCount);
  
  renderGame();
}

// ── function: generateEnemy ──
function generateEnemy() {
  const gs = gameState;
  
  if (gs.isBossRoom) {
    const boss = BOSSES[gs.currentFloor];
    gs.enemy = {
      name: boss.name,
      hp: boss.hp,
      maxHP: boss.hp,
      damage: boss.damage,
      color: boss.color,
      desc: boss.desc,
    };
    return;
  }
  
  const floorEnemies = FLOOR_ENEMIES[gs.currentFloor];
  const template = floorEnemies[Math.floor(Math.random() * floorEnemies.length)];
  
  // Scale slightly within floor
  const hpScale = 0.9 + Math.random() * 0.2;
  const dmgScale = 0.9 + Math.random() * 0.2;
  
  gs.enemy = {
    name: template.name,
    hp: Math.round(template.hp * hpScale),
    maxHP: Math.round(template.hp * hpScale),
    damage: Math.round(template.damage * dmgScale),
    color: template.color,
  };
}

// ── const: canvas ──
// ============================================================
// CANVAS RENDERING
// ============================================================
const canvas = document.getElementById('game-canvas');

// ── const: ctx ──
const ctx = canvas.getContext('2d');

// ── function: resizeCanvas ──
function resizeCanvas() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
}

// ── let: particles ──
// Background particles
let particles = [];

// ── function: initParticles ──
function initParticles() {
  particles = [];
  for (let i = 0; i < 50; i++) {
    particles.push({
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
      size: Math.random() * 2 + 0.5,
      speed: Math.random() * 0.3 + 0.1,
      opacity: Math.random() * 0.3 + 0.1,
      drift: (Math.random() - 0.5) * 0.2,
    });
  }
}

// ── function: drawBackground ──
function drawBackground() {
  const cls = gameState.className;
  const classColors = {
    bastion: { bg1: '#0a0a15', bg2: '#1a2a3a', accent: '#4a8fa8' },
    wither: { bg1: '#0a0a15', bg2: '#2a1a1a', accent: '#c44a4a' },
    shroud: { bg1: '#0a0a15', bg2: '#1a1a2a', accent: '#8a4ac4' },
  };
  const colors = classColors[cls];
  
  // Dark gradient background
  const grad = ctx.createRadialGradient(
    canvas.width / 2, canvas.height * 0.3, 50,
    canvas.width / 2, canvas.height * 0.3, canvas.width * 0.7
  );
  grad.addColorStop(0, colors.bg2);
  grad.addColorStop(1, colors.bg1);
  ctx.fillStyle = grad;
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  
  // Atmospheric fog
  ctx.fillStyle = 'rgba(10, 10, 20, 0.3)';
  ctx.fillRect(0, canvas.height * 0.6, canvas.width, canvas.height * 0.4);
  
  // Floating particles
  for (let p of particles) {
    p.y -= p.speed;
    p.x += p.drift;
    if (p.y < -10) { p.y = canvas.height + 10; p.x = Math.random() * canvas.width; }
    if (p.x < -10) p.x = canvas.width + 10;
    if (p.x > canvas.width + 10) p.x = -10;
    
    ctx.beginPath();
    ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
    ctx.fillStyle = `rgba(${parseInt(colors.accent.slice(1,3),16)}, ${parseInt(colors.accent.slice(3,5),16)}, ${parseInt(colors.accent.slice(5,7),16)}, ${p.opacity})`;
    ctx.fill();
  }
}

// ── function: drawPlayerStats ──
function drawPlayerStats() {
  const gs = gameState;
  const hero = gs.hero;
  
  const x = 30;
  let y = 70;
  
  // HP bar
  ctx.fillStyle = '#6b5b8a';
  ctx.font = '11px Georgia';
  ctx.textAlign = 'left';
  ctx.fillText('HEALTH', x, y);
  y += 5;
  
  const hpBarW = 200;
  const hpBarH = 10;
  ctx.fillStyle = '#1a1a2e';
  ctx.fillRect(x, y, hpBarW, hpBarH);
  ctx.strokeStyle = '#2a2a4a';
  ctx.strokeRect(x, y, hpBarW, hpBarH);
  
  const hpRatio = Math.max(0, hero.hp / hero.maxHP);
  const hpColor = hpRatio > 0.5 ? '#4a8a4a' : hpRatio > 0.25 ? '#e8a82a' : '#c44a4a';
  ctx.fillStyle = hpColor;
  ctx.fillRect(x, y, hpBarW * hpRatio, hpBarH);
  
  ctx.fillStyle = '#c44a4a';
  ctx.font = '10px Georgia';
  ctx.fillText(`${hero.hp} / ${hero.maxHP}`, x + hpBarW + 8, y + 9);
  y += 20;
  
  // Defense bar
  ctx.fillStyle = '#6b5b8a';
  ctx.font = '11px Georgia';
  ctx.fillText('DEFENSE', x, y);
  y += 5;
  
  const defBarW = 200;
  const defBarH = 6;
  ctx.fillStyle = '#1a1a2e';
  ctx.fillRect(x, y, defBarW, defBarH);
  ctx.strokeStyle = '#2a2a4a';
  ctx.strokeRect(x, y, defBarW, defBarH);
  
  const defRatio = Math.min(1, hero.defense / 30);
  ctx.fillStyle = '#4a8fa8';
  ctx.fillRect(x, y, defBarW * defRatio, defBarH);
  
  ctx.fillStyle = '#4a8fa8';
  ctx.font = '10px Georgia';
  ctx.fillText(`${hero.defense} + ${hero.permanentDefense}`, x + defBarW + 8, y + 6);
  y += 20;
  
  // Damage flash on player
  if (gs.damageFlash === 'player') {
    ctx.fillStyle = 'rgba(196, 74, 74, 0.2)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    gs.damageFlash = null;
  }
}

// ── function: drawEnergyDisplay ──
function drawEnergyDisplay() {
  const gs = gameState;
  const hero = gs.hero;
  
  const cx = canvas.width - 50;
  const cy = 70;
  
  // Energy orb
  const orbGrad = ctx.createRadialGradient(cx - 5, cy - 5, 2, cx, cy, 30);
  orbGrad.addColorStop(0, '#6ab4e8');
  orbGrad.addColorStop(0.7, '#2a6a8a');
  orbGrad.addColorStop(1, '#1a4a5a');
  ctx.fillStyle = orbGrad;
  ctx.beginPath();
  ctx.arc(cx, cy, 28, 0, Math.PI * 2);
  ctx.fill();
  
  ctx.strokeStyle = '#4a8fa8';
  ctx.lineWidth = 2;
  ctx.stroke();
  
  // Energy text
  ctx.fillStyle = 'white';
  ctx.font = 'bold 18px Georgia';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText(`${hero.energy}`, cx, cy);
  
  // Max energy
  ctx.fillStyle = '#6b5b8a';
  ctx.font = '10px Georgia';
  ctx.fillText(`/${hero.maxEnergy}`, cx, cy + 38);
}

// ── function: drawGoldDisplay ──
function drawGoldDisplay() {
  const gs = gameState;
  const hero = gs.hero;
  
  const x = canvas.width / 2;
  const y = 35;
  
  ctx.fillStyle = '#c9a84c';
  ctx.font = 'bold 16px Georgia';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText(`⛃ ${hero.gold}`, x, y);
}

// ── function: drawFloorDisplay ──
function drawFloorDisplay() {
  const gs = gameState;
  
  const x = canvas.width / 2;
  const y = canvas.height - 230;
  
  ctx.fillStyle = '#8a4ac4';
  ctx.font = '12px Georgia';
  ctx.textAlign = 'center';
  ctx.fillText(`Floor ${gs.currentFloor}`, x, y);
  
  // Room indicators
  for (let i = 0; i < gs.totalRoomsPerFloor; i++) {
    const dotX = x - 20 + i * 20;
    const isCompleted = i < gs.currentRoom - 1;
    const isCurrent = i === gs.currentRoom - 1;
    const isBoss = i === gs.totalRoomsPerFloor - 1;
    
    ctx.beginPath();
    ctx.arc(dotX, y + 15, 5, 0, Math.PI * 2);
    if (isCompleted) {
      ctx.fillStyle = '#4a8a4a';
    } else if (isCurrent) {
      ctx.fillStyle = isBoss ? '#c44a4a' : '#8a4ac4';
    } else {
      ctx.fillStyle = '#2a2a4a';
    }
    ctx.fill();
    
    if (isBoss && i === gs.currentRoom - 1) {
      ctx.fillStyle = '#c44a4a';
      ctx.font = '8px Georgia';
      ctx.fillText('☠', dotX, y + 30);
    }
  }
}

// ── function: renderHand ──
function renderHand() {
  const gs = gameState;
  const hero = gs.hero;
  const overlay = document.getElementById('ui-overlay');
  
  // Remove old hand
  const oldHand = overlay.querySelector('.hand-area');
  if (oldHand) oldHand.remove();
  
  const handArea = document.createElement('div');
  handArea.className = 'hand-area';
  
  for (let i = 0; i < hero.hand.length; i++) {
    const card = hero.hand[i];
    const cardEl = createCardElement(card, i, hero);
    handArea.appendChild(cardEl);
  }
  
  // Action buttons
  const actionBar = document.createElement('div');
  actionBar.className = 'action-bar';
  
  const endTurnBtn = document.createElement('button');
  endTurnBtn.className = 'action-btn end-turn-btn';
  endTurnBtn.textContent = 'End Turn';
  endTurnBtn.addEventListener('click', () => {
    if (gs.turnPhase === 'player') endTurn();
  });
  
  const shopBtn = document.createElement('button');
  shopBtn.className = 'action-btn';
  shopBtn.textContent = `Shop (${hero.gold}g)`;
  shopBtn.addEventListener('click', () => {
    gs.shopOpen = true;
    renderGame();
  });
  
  actionBar.appendChild(shopBtn);
  actionBar.appendChild(endTurnBtn);
  handArea.appendChild(actionBar);
  
  overlay.appendChild(handArea);
}

// ── function: createCardElement ──
function createCardElement(card, index, hero) {
  const cardEl = document.createElement('div');
  cardEl.className = `hand-card card-${card.type}`;
  
  const canPlay = card.cost <= hero.energy;
  if (!canPlay) cardEl.classList.add('cannot-play');
  
  const cardContainer = document.createElement('div');
  cardContainer.className = `card-container card-${card.type}`;
  
  // Card cost
  const costEl = document.createElement('div');
  costEl.className = 'card-cost';
  costEl.textContent = card.cost;
  cardContainer.appendChild(costEl);
  
  // Card image (SVG frame)
  const imageEl = document.createElement('div');
  imageEl.className = 'card-image';
  imageEl.innerHTML = getCardFrameSVG(card);
  cardContainer.appendChild(imageEl);
  
  // Card name
  const nameEl = document.createElement('div');
  nameEl.className = 'card-name';
  nameEl.textContent = card.name;
  cardContainer.appendChild(nameEl);
  
  // Card description
  const descEl = document.createElement('div');
  descEl.className = 'card-description';
  descEl.textContent = card.desc;
  cardContainer.appendChild(descEl);
  
  cardEl.appendChild(cardContainer);
  
  // Click handler
  cardEl.addEventListener('click', () => {
    if (gs.turnPhase === 'player' && canPlay) {
      playCard(card.uid);
      renderGame();
    }
  });
  
  return cardEl;
}

// ── function: renderShop ──
function renderShop() {
  const gs = gameState;
  const overlay = document.getElementById('ui-overlay');
  
  // Remove old shop
  const oldShop = overlay.querySelector('#shop-overlay');
  if (oldShop) oldShop.remove();
  
  if (!gs.shopOpen) return;
  
  const shop = document.createElement('div');
  shop.id = 'shop-overlay';
  shop.className = 'active';
  
  const container = document.createElement('div');
  container.className = 'shop-container';
  
  const title = document.createElement('h2');
  title.className = 'shop-title';
  title.textContent = 'Void Merchant';
  container.appendChild(title);
  
  const subtitle = document.createElement('p');
  subtitle.className = 'shop-subtitle';
  subtitle.textContent = 'Choose wisely - the Void hungers.';
  container.appendChild(subtitle);
  
  const goldEl = document.createElement('p');
  goldEl.className = 'shop-gold';
  goldEl.innerHTML = `Gold: <span>${gs.hero.gold}</span> ⛃`;
  container.appendChild(goldEl);
  
  const cardsContainer = document.createElement('div');
  cardsContainer.className = 'shop-cards';
  
  // Generate 3 random cards from any class
  const allCardIds = Object.keys(CARD_DATA);
  const shopCards = [];
  for (let i = 0; i < 3; i++) {
    const id = allCardIds[Math.floor(Math.random() * allCardIds.length)];
    const card = CARD_DATA[id];
    const count = gs.inventory[id] || 0;
    const cost = Math.min(50, card.cost + count);
    shopCards.push({ card, cost, count });
  }
  
  for (let sc of shopCards) {
    const cardEl = document.createElement('div');
    cardEl.className = `shop-card card-${sc.card.type}`;
    
    const cardInner = document.createElement('div');
    cardInner.style.cssText = 'width:100%;height:100%;position:relative;';
    
    // Mini SVG
    const svgEl = document.createElement('div');
    svgEl.style.cssText = 'width:100%;height:100%;';
    svgEl.innerHTML = getCardFrameSVG(sc.card);
    cardInner.appendChild(svgEl);
    
    // Cost badge
    const costBadge = document.createElement('div');
    costBadge.className = 'shop-card-cost';
    costBadge.textContent = sc.cost;
    cardInner.appendChild(costBadge);
    
    // Name
    const nameEl = document.createElement('div');
    nameEl.className = 'shop-card-name';
    nameEl.textContent = sc.card.name;
    cardInner.appendChild(nameEl);
    
    // Desc
    const descEl = document.createElement('div');
    descEl.className = 'shop-card-desc';
    descEl.textContent = sc.card.desc;
    cardInner.appendChild(descEl);
    
    cardEl.appendChild(cardInner);
    
    // Buy handler
    cardEl.addEventListener('click', () => {
      if (gs.hero.gold >= sc.cost && sc.count < 5) {
        gs.hero.gold -= sc.cost;
        if (!gs.inventory[sc.card.id]) gs.inventory[sc.card.id] = 0;
        gs.inventory[sc.card.id]++;
        
        // Add to deck
        gs.hero.deck.push({ ...CARD_DATA[sc.card.id], uid: `${sc.card.id}-${Date.now()}-${Math.random()}` });
        
        renderGame();
      }
    });
    
    cardsContainer.appendChild(cardEl);
  }
  
  container.appendChild(cardsContainer);
  
  const closeBtn = document.createElement('button');
  closeBtn.className = 'shop-close';
  closeBtn.textContent = 'Close';
  closeBtn.addEventListener('click', () => {
    gs.shopOpen = false;
    renderGame();
  });
  container.appendChild(closeBtn);
  
  shop.appendChild(container);
  overlay.appendChild(shop);
}

// ── function: showVictory ──
function showVictory() {
  document.getElementById('victory-screen').classList.remove('hidden');
}

// ── function: showGameOver ──
function showGameOver() {
  document.getElementById('gameover-screen').classList.remove('hidden');
}

// ── function: hideAllScreens ──
function hideAllScreens() {
  document.querySelectorAll('.screen').forEach(s => s.classList.add('hidden'));
}

// ── function: startGame ──
function startGame(className) {
  hideAllScreens();
  createGameState(className);
  generateEnemy();
  gameState.shopOpen = false;
  
  // Draw initial hand
  const hero = gameState.hero;
  const drawCount = Math.min(4, hero.deck.length);
  drawCards(drawCount);
  
  resizeCanvas();
  initParticles();
  renderGame();
}

// ── function: resolveCardEffect ──
function resolveCardEffect(card) {
  const gs = gameState;
  const hero = gs.hero;
  const enemy = gs.enemy;
  
  if (!enemy) return;
  
  // Debug: log the card being resolved
  gs._lastResolvedCard = card.id;
  gs._lastResolvedType = card.type;
  
  switch (card.type) {
    case "attack": {
      let damage = card.atk;
      if (card.id === "BA-A01" && hero.hp >= 30) drawCards(1);
      if (card.id === "SH-A01" && gs.enemyDefense <= 0) damage += 1;
      if (card.id === "SH-A02") gs.enemyDefense = 0;
      if (card.id === "SH-U04") { gs.enemyDefense = 0; damage = 8; }
      enemy.hp = Math.max(0, enemy.hp - damage);
      if (card.id === "WI-A01") gs.enemyStatusEffects.burn = Math.max(gs.enemyStatusEffects.burn, 1);
      if (card.id === "WI-A02") gs.enemyStatusEffects.burn = Math.max(gs.enemyStatusEffects.burn, 3);
      if (card.id === "WI-A03") gs.enemyStatusEffects.burn = Math.max(gs.enemyStatusEffects.burn, 1);
      if (card.id === "WI-A04") gs._nextTurnBurn = (gs._nextTurnBurn || 0) + 1;
      if (card.id === "BA-D04") gs.enemyStatusEffects.stun = Math.max(gs.enemyStatusEffects.stun, 1);
      if (card.id === "SH-A03") gs.enemyStatusEffects.stun = Math.max(gs.enemyStatusEffects.stun, 1);
      break;
    }
    case "defense": {
      let defGain = card.def;
      if (card.id === "BA-D02") {
        hero.permanentDefense += defGain;
        if (hero.hand.length > 0) { const discard = hero.hand.pop(); hero.discardPile.push(discard); }
      } else {
        hero.defense += defGain;
      }
      if (card.id === "BA-D03") gs.playerBlockedThisTurn += 1;
      if (card.id === "WI-D01" || card.id === "WI-D03") gs.burnBlock = (gs.burnBlock || 0) + 1;
      if (card.id === "WI-D02") hero.energy = Math.min(hero.maxEnergy, hero.energy + 1);
      if (card.id === "SH-D02") hero.energy = Math.min(hero.maxEnergy, hero.energy + 1);
      break;
    }
    case "other": {
      if (card.id === "BA-U01") { drawCards(1); if (hero.hand.length > 5) { const excess = hero.hand.splice(5); hero.discardPile.push(...excess); } }
      if (card.id === "BA-U02") hero.hp = Math.min(hero.maxHP, hero.hp + 6);
      if (card.id === "BA-U03") { drawCards(1); hero.energy = Math.min(hero.maxEnergy, hero.energy + 1); }
      if (card.id === "BA-U04") { hero.gold += 5; gs._lootApplied = true; }
      if (card.id === "WI-U01") gs.enemyStatusEffects.poison = Math.max(gs.enemyStatusEffects.poison, 1);
      if (card.id === "WI-U02") { enemy.hp = Math.max(0, enemy.hp - 3); gs.enemyStatusEffects.burn = Math.max(gs.enemyStatusEffects.burn, 2); }
      if (card.id === "WI-U03") { drawCards(1); gs.enemyStatusEffects.stun = Math.max(gs.enemyStatusEffects.stun, 1); }
      if (card.id === "WI-U04") { hero.hp = Math.min(hero.maxHP, hero.hp + 5); gs.enemyAttackReduction = (gs.enemyAttackReduction || 0) + 1; }
      if (card.id === "SH-U01") { hero.energy = Math.min(hero.maxEnergy, hero.energy + 1); gs.skipEnemyAttack = true; }
      if (card.id === "SH-U02") enemy.hp = Math.max(0, enemy.hp - 2);
      if (card.id === "SH-U03") { hero.defense += 1; if (hero.hand.length > 0) hero.discardPile.push(hero.hand.pop()); drawCards(2); }
      if (card.id === "SH-D03") gs.skipEnemyAttack = true;
      if (card.id === "SH-A04") hero.energy = Math.min(hero.maxEnergy, hero.energy + 2);
      break;
    }
  }
}

// ── function: playCard ──
function playCard(cardUid) {
  // Debug: check what gameState we see
  const gs = gameState;
  const hero = gs.hero;
  
  const cardIndex = hero.hand.findIndex(h => h.uid === cardUid);
  if (cardIndex === -1) return false;
  
  const card = hero.hand[cardIndex];
  if (card.cost > hero.energy) return false;
  
  hero.energy -= card.cost;
  hero.hand.splice(cardIndex, 1);
  hero.discardPile.push(card);
  
  gs._lastPlayedCard = card.id;
  
  if (card.type === "attack") {
    let damage = card.atk;
    if (card.id === "BA-A01" && hero.hp >= 30) drawCards(1);
    if (card.id === "SH-A01" && gs.enemyDefense <= 0) damage += 1;
    if (card.id === "SH-A02") gs.enemyDefense = 0;
    if (card.id === "SH-U04") { gs.enemyDefense = 0; damage = 8; }
    gs.enemy.hp = Math.max(0, gs.enemy.hp - damage);
    if (card.id === "WI-A01") gs.enemyStatusEffects.burn = Math.max(gs.enemyStatusEffects.burn, 1);
    if (card.id === "WI-A02") gs.enemyStatusEffects.burn = Math.max(gs.enemyStatusEffects.burn, 3);
    if (card.id === "WI-A03") gs.enemyStatusEffects.burn = Math.max(gs.enemyStatusEffects.burn, 1);
    if (card.id === "WI-A04") gs._nextTurnBurn = (gs._nextTurnBurn || 0) + 1;
    if (card.id === "BA-D04") gs.enemyStatusEffects.stun = Math.max(gs.enemyStatusEffects.stun, 1);
    if (card.id === "SH-A03") gs.enemyStatusEffects.stun = Math.max(gs.enemyStatusEffects.stun, 1);
  } else if (card.type === "defense") {
    let defGain = card.def;
    if (card.id === "BA-D02") {
      hero.permanentDefense += defGain;
      if (hero.hand.length > 0) { const discard = hero.hand.pop(); hero.discardPile.push(discard); }
    } else {
      hero.defense += defGain;
    }
    if (card.id === "BA-D03") gs.playerBlockedThisTurn += 1;
    if (card.id === "WI-D01" || card.id === "WI-D03") gs.burnBlock = (gs.burnBlock || 0) + 1;
    if (card.id === "WI-D02") hero.energy = Math.min(hero.maxEnergy, hero.energy + 1);
    if (card.id === "SH-D02") hero.energy = Math.min(hero.maxEnergy, hero.energy + 1);
  } else if (card.type === "other") {
    if (card.id === "BA-U01") { drawCards(1); if (hero.hand.length > 5) { const excess = hero.hand.splice(5); hero.discardPile.push(...excess); } }
    if (card.id === "BA-U02") hero.hp = Math.min(hero.maxHP, hero.hp + 6);
    if (card.id === "BA-U03") { drawCards(1); hero.energy = Math.min(hero.maxEnergy, hero.energy + 1); }
    if (card.id === "BA-U04") hero.gold += 5;
    if (card.id === "WI-U01") gs.enemyStatusEffects.poison = Math.max(gs.enemyStatusEffects.poison, 1);
    if (card.id === "WI-U02") { gs.enemy.hp = Math.max(0, gs.enemy.hp - 3); gs.enemyStatusEffects.burn = Math.max(gs.enemyStatusEffects.burn, 2); }
    if (card.id === "WI-U03") { drawCards(1); gs.enemyStatusEffects.stun = Math.max(gs.enemyStatusEffects.stun, 1); }
    if (card.id === "WI-U04") { hero.hp = Math.min(hero.maxHP, hero.hp + 5); gs.enemyAttackReduction = (gs.enemyAttackReduction || 0) + 1; }
    if (card.id === "SH-U01") { hero.energy = Math.min(hero.maxEnergy, hero.energy + 1); gs.skipEnemyAttack = true; }
    if (card.id === "SH-U02") gs.enemy.hp = Math.max(0, gs.enemy.hp - 2);
    if (card.id === "SH-U03") { hero.defense += 1; if (hero.hand.length > 0) hero.discardPile.push(hero.hand.pop()); drawCards(2); }
    if (card.id === "SH-D03") gs.skipEnemyAttack = true;
    if (card.id === "SH-A04") hero.energy = Math.min(hero.maxEnergy, hero.energy + 2);
  }
  
  gs.cardsPlayed.push(card);
  return true;
}

// ── function: drawEnemy ──
function drawEnemy() {
  const gs = gameState;
  if (!gs.enemy) return;
  
  const enemy = gs.enemy;
  const cx = canvas.width / 2;
  const cy = canvas.height * 0.42;
  const size = gs.isBossRoom ? 110 : 75;
  
  // Parse enemy color for glow
  const r = parseInt(enemy.color.slice(1, 3), 16);
  const g = parseInt(enemy.color.slice(3, 5), 16);
  const b = parseInt(enemy.color.slice(5, 7), 16);
  
  // Ambient glow
  const glowGrad = ctx.createRadialGradient(cx, cy, size * 0.2, cx, cy, size * 2);
  glowGrad.addColorStop(0, `rgba(${r},${g},${b},0.12)`);
  glowGrad.addColorStop(0.5, `rgba(${r},${g},${b},0.04)`);
  glowGrad.addColorStop(1, 'transparent');
  ctx.fillStyle = glowGrad;
  ctx.fillRect(cx - size * 2.5, cy - size * 2.5, size * 5, size * 5);
  
  // Draw enemy body
  ctx.save();
  
  // Shadow
  ctx.fillStyle = 'rgba(0,0,0,0.3)';
  ctx.beginPath();
  ctx.ellipse(cx, cy + size * 1.1, size * 0.6, size * 0.15, 0, 0, Math.PI * 2);
  ctx.fill();
  
  // Body
  const bodyGrad = ctx.createLinearGradient(cx - size * 0.5, cy - size * 0.5, cx + size * 0.5, cy + size);
  bodyGrad.addColorStop(0, `rgba(${Math.min(255, r + 80)}, ${Math.min(255, g + 80)}, ${Math.min(255, b + 80)}, 0.9)`);
  bodyGrad.addColorStop(0.5, `rgba(${Math.min(255, r + 40)}, ${Math.min(255, g + 40)}, ${Math.min(255, b + 40)}, 0.8)`);
  bodyGrad.addColorStop(1, `rgba(${Math.max(0, r - 20)}, ${Math.max(0, g - 20)}, ${Math.max(0, b - 20)}, 0.7)`);
  
  ctx.fillStyle = bodyGrad;
  ctx.beginPath();
  ctx.ellipse(cx, cy + size * 0.15, size * 0.45, size * 0.75, 0, 0, Math.PI * 2);
  ctx.fill();
  
  // Head
  const headGrad = ctx.createRadialGradient(cx - 5, cy - size * 0.7, 5, cx, cy - size * 0.6, size * 0.35);
  headGrad.addColorStop(0, `rgba(${Math.min(255, r + 100)}, ${Math.min(255, g + 100)}, ${Math.min(255, b + 100)}, 0.95)`);
  headGrad.addColorStop(1, `rgba(${Math.min(255, r + 40)}, ${Math.min(255, g + 40)}, ${Math.min(255, b + 40)}, 0.8)`);
  
  ctx.fillStyle = headGrad;
  ctx.beginPath();
  ctx.arc(cx, cy - size * 0.6, size * 0.32, 0, Math.PI * 2);
  ctx.fill();
  
  // Eyes
  const eyeColor = gs.isBossRoom ? '#ff4a4a' : '#e8a82a';
  ctx.shadowColor = eyeColor;
  ctx.shadowBlur = 12;
  ctx.fillStyle = eyeColor;
  ctx.beginPath();
  ctx.arc(cx - size * 0.1, cy - size * 0.65, size * 0.045, 0, Math.PI * 2);
  ctx.fill();
  ctx.beginPath();
  ctx.arc(cx + size * 0.1, cy - size * 0.65, size * 0.045, 0, Math.PI * 2);
  ctx.fill();
  ctx.shadowBlur = 0;
  
  // Mouth
  ctx.strokeStyle = `rgba(${Math.max(0, r - 40)}, ${Math.max(0, g - 40)}, ${Math.max(0, b - 40)}, 0.6)`;
  ctx.lineWidth = 1.5;
  ctx.beginPath();
  ctx.arc(cx, cy - size * 0.48, size * 0.1, 0.1 * Math.PI, 0.9 * Math.PI);
  ctx.stroke();
  
  // Damage flash
  if (gs.damageFlash === 'enemy') {
    ctx.fillStyle = 'rgba(255, 255, 255, 0.25)';
    ctx.beginPath();
    ctx.ellipse(cx, cy, size * 0.55, size * 0.85, 0, 0, Math.PI * 2);
    ctx.fill();
    gs.damageFlash = null;
  }
  
  // Status effect indicators
  let statusY = cy - size - 25;
  if (gs.enemyStatusEffects.burn > 0) {
    ctx.fillStyle = '#e8a82a';
    ctx.font = 'bold 16px Georgia';
    ctx.textAlign = 'center';
    ctx.fillText('🔥', cx - 40, statusY);
    ctx.font = '12px Georgia';
    ctx.fillStyle = '#e8a82a';
    ctx.fillText(gs.enemyStatusEffects.burn, cx - 40, statusY + 16);
    statusY += 20;
  }
  if (gs.enemyStatusEffects.poison > 0) {
    ctx.fillStyle = '#4ae84a';
    ctx.font = 'bold 16px Georgia';
    ctx.textAlign = 'center';
    ctx.fillText('☠', cx + 40, statusY);
    ctx.font = '12px Georgia';
    ctx.fillStyle = '#4ae84a';
    ctx.fillText(gs.enemyStatusEffects.poison, cx + 40, statusY + 16);
  }
  
  ctx.restore();
  
  // Enemy HP bar
  const barWidth = Math.min(250, canvas.width * 0.25);
  const barHeight = 10;
  const barX = cx - barWidth / 2;
  const barY = cy + size * 1.0 + 15;
  
  // Bar background
  ctx.fillStyle = '#1a1a2e';
  ctx.beginPath();
  ctx.roundRect(barX, barY, barWidth, barHeight, 5);
  ctx.fill();
  ctx.strokeStyle = '#2a2a4a';
  ctx.lineWidth = 1;
  ctx.beginPath();
  ctx.roundRect(barX, barY, barWidth, barHeight, 5);
  ctx.stroke();
  
  // HP fill
  const hpRatio = Math.max(0, enemy.hp / enemy.maxHP);
  const hpColor = hpRatio > 0.5 ? '#c44a4a' : hpRatio > 0.25 ? '#e8a82a' : '#e84a4a';
  ctx.fillStyle = hpColor;
  ctx.beginPath();
  ctx.roundRect(barX, barY, barWidth * hpRatio, barHeight, 5);
  ctx.fill();
  
  // HP text
  ctx.fillStyle = '#c44a4a';
  ctx.font = 'bold 11px Georgia';
  ctx.textAlign = 'center';
  ctx.fillText(`${enemy.hp} / ${enemy.maxHP}`, cx, barY + barHeight + 14);
  
  // Enemy name
  ctx.fillStyle = '#8a7a9a';
  ctx.font = '13px Georgia';
  ctx.textAlign = 'center';
  ctx.fillText(enemy.name, cx, barY - 8);
  
  // Boss description
  if (gs.isBossRoom && enemy.desc) {
    ctx.fillStyle = '#6b5b8a';
    ctx.font = 'italic 10px Georgia';
    ctx.textAlign = 'center';
    ctx.fillText(enemy.desc, cx, barY + barHeight + 30);
  }
}

// ── function: drawHandArea ──
function drawHandArea() {
  const gs = gameState;
  const hero = gs.hero;
  
  const handY = canvas.height - 225;
  const handWidth = hero.hand.length * 150;
  const startX = (canvas.width - handWidth) / 2;
  
  for (let i = 0; i < hero.hand.length; i++) {
    const card = hero.hand[i];
    const x = startX + i * 150 + 5;
    
    // Card shadow
    ctx.fillStyle = 'rgba(0,0,0,0.3)';
    ctx.beginPath();
    ctx.roundRect(x + 3, handY + 3, 140, 200, 8);
    ctx.fill();
    
    // Card border glow
    const borderColor = card.type === 'attack' ? '#8a2a2a' : card.type === 'defense' ? '#2a5a6a' : '#5a2a6a';
    ctx.strokeStyle = borderColor;
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.roundRect(x, handY, 140, 200, 8);
    ctx.stroke();
    
    // Card background
    const cardGrad = ctx.createLinearGradient(x, handY, x + 140, handY + 200);
    cardGrad.addColorStop(0, '#1a1a2e');
    cardGrad.addColorStop(1, '#0f0f1a');
    ctx.fillStyle = cardGrad;
    ctx.beginPath();
    ctx.roundRect(x, handY, 140, 200, 8);
    ctx.fill();
    
    // Cost circle
    const costColor = card.type === 'attack' ? '#c44a4a' : card.type === 'defense' ? '#4a8fa8' : '#8a4ac4';
    const costGrad = ctx.createRadialGradient(x + 14, handY + 14, 2, x + 14, handY + 14, 14);
    costGrad.addColorStop(0, costColor);
    costGrad.addColorStop(1, card.type === 'attack' ? '#6a1a1a' : card.type === 'defense' ? '#1a4a5a' : '#3a1a4a');
    ctx.fillStyle = costGrad;
    ctx.beginPath();
    ctx.arc(x + 14, handY + 14, 14, 0, Math.PI * 2);
    ctx.fill();
    
    ctx.fillStyle = 'white';
    ctx.font = 'bold 12px Georgia';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(card.cost, x + 14, handY + 14);
    
    // Card name
    ctx.fillStyle = '#c9a84c';
    ctx.font = 'bold 10px Georgia';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'alphabetic';
    ctx.fillText(card.name, x + 70, handY + 42);
    
    // Card description
    ctx.fillStyle = '#8a7a9a';
    ctx.font = '8px Georgia';
    const words = card.desc.split(' ');
    let line = '';
    let ly = handY + 175;
    for (let w = 0; w < words.length; w++) {
      const testLine = line + words[w] + ' ';
      if (ctx.measureText(testLine).width > 120 && line !== '') {
        ctx.fillText(line.trim(), x + 70, ly);
        line = words[w] + ' ';
        ly += 10;
      } else {
        line = testLine;
      }
    }
    ctx.fillText(line.trim(), x + 70, ly);
    
    // Type indicator
    const typeColor = card.type === 'attack' ? '#e86a4a' : card.type === 'defense' ? '#6ab4e8' : '#a86ae8';
    ctx.fillStyle = typeColor;
    ctx.font = 'bold 9px Georgia';
    ctx.fillText(card.type.toUpperCase(), x + 70, handY + 58);
  }
}

// ── function: renderGame ──
function renderGame() {
  const gs = gameState;
  const hero = gs.hero;
  
  // Clear canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  // Draw background
  drawBackground();
  
  // Draw game elements
  drawEnemy();
  drawPlayerStats();
  drawEnergyDisplay();
  drawGoldDisplay();
  drawFloorDisplay();
  drawDeckPile();
  drawHandArea();
  
  // Render HTML UI overlay
  renderHand();
  renderShop();
}

// ── function: drawCardBack ──
function drawCardBack(cx, cy, size, cls) {
  const colors = {
    bastion: { bg1: '#1a2a3a', bg2: '#0a1a2a', border: '#2a5a6a', accent: '#4a8fa8' },
    wither: { bg1: '#2a1a1a', bg2: '#1a0a0a', border: '#6a2a2a', accent: '#c44a4a' },
    shroud: { bg1: '#1a1a2a', bg2: '#0a0a1a', border: '#3a2a5a', accent: '#8a4ac4' },
  };
  const c = colors[cls] || colors.bastion;
  
  // Background
  const grad = ctx.createLinearGradient(cx - size/2, cy - size, cx + size/2, cy + size);
  grad.addColorStop(0, c.bg1);
  grad.addColorStop(1, c.bg2);
  ctx.fillStyle = grad;
  ctx.beginPath();
  ctx.roundRect(cx - size/2, cy - size, size, size * 1.43, 6);
  ctx.fill();
  
  // Border
  ctx.strokeStyle = c.border;
  ctx.lineWidth = 2;
  ctx.beginPath();
  ctx.roundRect(cx - size/2, cy - size, size, size * 1.43, 6);
  ctx.stroke();
  
  // Inner border
  ctx.strokeStyle = c.accent;
  ctx.lineWidth = 0.5;
  ctx.globalAlpha = 0.3;
  ctx.beginPath();
  ctx.roundRect(cx - size/2 + 4, cy - size + 4, size - 8, size * 1.43 - 8, 4);
  ctx.stroke();
  ctx.globalAlpha = 1;
  
  // Center emblem
  ctx.fillStyle = c.accent;
  ctx.globalAlpha = 0.4;
  ctx.beginPath();
  ctx.arc(cx, cy, size * 0.25, 0, Math.PI * 2);
  ctx.fill();
  ctx.globalAlpha = 0.6;
  ctx.beginPath();
  ctx.arc(cx, cy, size * 0.15, 0, Math.PI * 2);
  ctx.fill();
  ctx.globalAlpha = 1;
}

// ── function: drawDeckPile ──
function drawDeckPile() {
  const gs = gameState;
  const hero = gs.hero;
  
  const x = 30;
  const y = canvas.height * 0.55;
  
  // Draw card back
  drawCardBack(x + 30, y + 40, 25, gs.className);
  
  // Deck count
  ctx.fillStyle = '#6b5b8a';
  ctx.font = '11px Georgia';
  ctx.textAlign = 'left';
  ctx.fillText(`Deck: ${hero.deck.length}`, x + 65, y + 30);
  
  // Discard pile count
  ctx.fillStyle = '#6b5b8a';
  ctx.fillText(`Discard: ${hero.discardPile.length}`, x + 65, y + 50);
  
  // Permanent defense
  ctx.fillStyle = '#4a8fa8';
  ctx.fillText(`Perma Def: ${hero.permanentDefense}`, x + 65, y + 70);
  
  // Room progress
  const roomX = canvas.width / 2;
  const roomY = canvas.height * 0.75;
  
  ctx.fillStyle = '#6b5b8a';
  ctx.font = '11px Georgia';
  ctx.textAlign = 'center';
  ctx.fillText(`Room ${gs.currentRoom} of ${gs.totalRoomsPerFloor}`, roomX, roomY);
  
  for (let i = 0; i < gs.totalRoomsPerFloor; i++) {
    const dotX = roomX - 24 + i * 24;
    const isCompleted = i < gs.currentRoom - 1;
    const isCurrent = i === gs.currentRoom - 1;
    const isBoss = i === gs.totalRoomsPerFloor - 1;
    
    ctx.beginPath();
    ctx.arc(dotX, roomY + 15, 6, 0, Math.PI * 2);
    if (isCompleted) {
      ctx.fillStyle = '#4a8a4a';
    } else if (isCurrent) {
      ctx.fillStyle = isBoss ? '#c44a4a' : '#8a4ac4';
    } else {
      ctx.fillStyle = '#2a2a4a';
    }
    ctx.fill();
    ctx.strokeStyle = '#3a3a5a';
    ctx.lineWidth = 1;
    ctx.stroke();
  }
}

// ── main ──
// ============================================================
// MAIN - Game Initialization
// ============================================================
(function init() {
  resizeCanvas();
  window.addEventListener('resize', resizeCanvas);
  
  // Class selection
  let selectedClass = null;
  document.querySelectorAll('.class-option').forEach(opt => {
    opt.addEventListener('click', () => {
      document.querySelectorAll('.class-option').forEach(o => o.classList.remove('selected'));
      opt.classList.add('selected');
      selectedClass = opt.dataset.class;
    });
  });
  
  // Start button
  document.getElementById('start-btn').addEventListener('click', () => {
    if (selectedClass) {
      startGame(selectedClass);
    }
  });
  
  // Menu button (victory screen)
  document.getElementById('menu-btn').addEventListener('click', () => {
    hideAllScreens();
    gameState = null;
    document.getElementById('start-screen').classList.remove('hidden');
  });
  
  // Retry button (game over screen)
  document.getElementById('retry-btn').addEventListener('click', () => {
    hideAllScreens();
    gameState = null;
    document.getElementById('start-screen').classList.remove('hidden');
  });
  
  // Keyboard controls
  document.addEventListener('keydown', (e) => {
    if (!gameState || gameState.gameOver || gameState.victory) return;
    if (gameState.shopOpen) return;
    
    if (e.key === 'e' || e.key === 'E') {
      endTurn();
    }
    if (e.key === 's' || e.key === 'S') {
      gameState.shopOpen = !gameState.shopOpen;
      renderGame();
    }
  });
  
  // Initial render of empty canvas
  ctx.fillStyle = '#0a0a0f';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"qwen/qwen3.6-35b-a3b","provider":"lmstudio","persona":"composer","sessionId":"ea21a1ea-b4b9-4901-b188-bf926910ad2b","tokensIn":4552814,"tokensOut":72126,"tokensTotal":4624940,"turns":79,"toolCalls":78,"failedToolCalls":1,"timestamp":"2026-07-20T22:57:47.025Z"} -->