← Shadows results

Shadows

Tiel Coder 35B A3B GGUF MTP · full

Initial view of Shadows

Browser check: Artifact runtime error. This is a technical check, not a quality review.

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 88,472 bytes · SHA-256 fb18b0745c1e
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shadows of the Abyss</title>
<style>
  * { margin:0; padding:0; box-sizing:border-box; }
  :root{
    --bg:#0a0710; --bg2:#140c1c; --panel:#1c1226; --panel2:#261833;
    --gold:#d4af37; --blood:#a11d2a; --blood2:#c62828; --purple:#7b2ff7;
    --teal:#2ec4b6; --text:#e8e0f0; --dim:#9a8bb0; --danger:#e53935;
    --card-common:#3a3a4a; --card-uncommon:#2d5a3d; --card-rare:#2a3f7a;
  }
  html,body{ height:100%; }
  body{
    font-family:'Trebuchet MS','Segoe UI',sans-serif;
    background:radial-gradient(circle at 50% 0%,#1a0f24 0%,var(--bg) 60%);
    color:var(--text); overflow:hidden; user-select:none;
  }
  #app{ position:fixed; inset:0; display:flex; flex-direction:column; }
  .screen{ position:absolute; inset:0; display:none; flex-direction:column; }
  .screen.active{ display:flex; }
  h1{ font-size:3rem; text-align:center; letter-spacing:4px;
      background:linear-gradient(90deg,var(--blood2),var(--gold),var(--purple));
      -webkit-background-clip:text; background-clip:text; color:transparent;
      text-shadow:0 0 30px rgba(161,29,42,.3); margin:auto; }
  h2{ font-size:1.5rem; color:var(--gold); letter-spacing:2px; margin-bottom:12px; }
  .sub{ text-align:center; color:var(--dim); margin:10px 0 30px; font-style:italic; }

  /* Class select */
  #classSelect{ justify-content:center; align-items:center; gap:30px; padding:20px; }
  .classGrid{ display:flex; gap:24px; flex-wrap:wrap; justify-content:center; }
  .classCard{
    width:260px; background:var(--panel); border:2px solid #3a2b4d; border-radius:14px;
    padding:22px; cursor:pointer; transition:.2s; text-align:center; position:relative;
  }
  .classCard:hover{ transform:translateY(-8px); border-color:var(--gold);
    box-shadow:0 10px 40px rgba(212,175,55,.25); }
  .classIcon{ font-size:4rem; margin-bottom:10px; }
  .classCard h3{ color:var(--gold); font-size:1.4rem; margin-bottom:8px; }
  .classCard .desc{ color:var(--dim); font-size:.85rem; line-height:1.4; margin-bottom:12px; }
  .classCard .start{ font-size:.8rem; color:var(--text); }
  .classCard .sig{ margin-top:10px; font-size:.8rem; color:var(--teal); }

  .btn{
    background:linear-gradient(180deg,var(--panel2),var(--panel));
    border:1px solid var(--gold); color:var(--gold); padding:10px 22px;
    border-radius:8px; cursor:pointer; font-size:1rem; letter-spacing:1px;
    transition:.15s; font-family:inherit;
  }
  .btn:hover{ background:var(--gold); color:var(--bg); }
  .btn:disabled{ opacity:.4; cursor:not-allowed; }
  .btn.danger{ border-color:var(--danger); color:var(--danger); }
  .btn.danger:hover{ background:var(--danger); color:#fff; }
  .btn.small{ padding:6px 14px; font-size:.85rem; }

  /* Top HUD */
  #hud{ display:flex; align-items:center; justify-content:space-between;
    padding:8px 16px; background:rgba(0,0,0,.4); border-bottom:1px solid #2a1c38; }
  #hud .info{ display:flex; gap:18px; align-items:center; font-size:.95rem; }
  #hud .gold{ color:var(--gold); font-weight:bold; }
  #hud .act{ color:var(--purple); font-weight:bold; }

  /* Map */
  #mapScreen{ justify-content:flex-start; }
  #mapScroll{ flex:1; overflow:auto; padding:30px; position:relative; }
  #mapCanvas{ position:absolute; inset:0; pointer-events:none; }
  .mapNodes{ position:relative; z-index:2; display:flex; flex-direction:column;
    align-items:center; gap:26px; max-width:700px; margin:0 auto; }
  .mapRow{ display:flex; gap:40px; justify-content:center; align-items:center; position:relative; }
  .mapNode{
    width:64px; height:64px; border-radius:50%; display:flex; align-items:center;
    justify-content:center; font-size:1.6rem; cursor:pointer; position:relative;
    border:2px solid #444; background:var(--panel); transition:.15s;
  }
  .mapNode .floorNum{ position:absolute; bottom:-18px; font-size:.65rem; color:var(--dim); }
  .mapNode.enemy{ border-color:var(--danger); }
  .mapNode.elite{ border-color:#ff7f00; box-shadow:0 0 12px rgba(255,127,0,.5); }
  .mapNode.rest{ border-color:var(--teal); }
  .mapNode.shop{ border-color:var(--gold); }
  .mapNode.event{ border-color:var(--purple); }
  .mapNode.boss{ border-color:var(--blood2); box-shadow:0 0 20px rgba(198,40,40,.7); font-size:2rem; }
  .mapNode.available{ animation:pulse 1.2s infinite; }
  .mapNode.cleared{ opacity:.5; }
  .mapNode.current{ box-shadow:0 0 0 3px var(--gold),0 0 20px var(--gold); transform:scale(1.15); }
  .mapNode.locked{ opacity:.35; cursor:not-allowed; }
  @keyframes pulse{ 0%,100%{filter:brightness(1);} 50%{filter:brightness(1.5)); }
  .nodeLabel{ position:absolute; top:70px; width:120px; text-align:center;
    font-size:.7rem; color:var(--dim); z-index:5; }

  /* Combat */
  #combatScreen{ justify-content:flex-start; }
  #battlefield{ flex:1; display:flex; flex-direction:column; position:relative; overflow:hidden; }
  .enemyRow{ display:flex; justify-content:center; gap:40px; flex:1; align-items:center; }
  .enemy{
    width:130px; text-align:center; cursor:crosshair; transition:.15s; position:relative;
  }
  .enemy.targetable{ outline:2px solid var(--danger); border-radius:12px; }
  .enemy.dead{ opacity:0; transform:scale(.3) rotate(90deg); }
  .enemyArt{ font-size:3.5rem; filter:drop-shadow(0 4px 8px rgba(0,0,0,.6)); transition:.2s; }
  .enemy.hit .enemyArt{ animation:shake .3s; filter:brightness(2) hue-rotate(-30deg); }
  @keyframes shake{ 0%,100%{transform:translateX(0);} 25%{transform:translateX(-8px);} 75%{transform:translateX(8px);} }
  .enemyName{ font-size:.8rem; color:var(--text); margin-top:4px; }
  .hpBar{ width:110px; height:12px; background:#300; border-radius:6px; margin:4px auto; overflow:hidden; border:1px solid #500; }
  .hpFill{ height:100%; background:linear-gradient(90deg,var(--blood),var(--blood2)); transition:width .3s; }
  .enemyStats{ font-size:.7rem; color:var(--dim); }
  .intentBox{ position:absolute; top:-6px; left:50%; transform:translateX(-50%);
    background:rgba(0,0,0,.7); border:1px solid var(--gold); border-radius:6px;
    padding:2px 6px; font-size:.7rem; white-space:nowrap; color:var(--gold); }
  .statusPips{ display:flex; gap:3px; justify-content:center; margin-top:3px; min-height:16px; }
  .pip{ font-size:.6rem; background:#2a1c38; border-radius:4px; padding:1px 4px; color:var(--text); }
  .blockBadge{ position:absolute; top:-6px; right:0; background:#3a5a8a; border-radius:6px;
    padding:1px 6px; font-size:.75rem; border:1px solid #6a8aba; }

  /* Player combat */
  #playerPanel{ position:relative; height:150px; display:flex; align-items:center; padding:0 20px; gap:16px; }
  #playerArt{ font-size:3.5rem; position:relative; }
  #playerArt .playerBlock{ position:absolute; top:0; right:-6px; background:#3a5a8a;
    border-radius:8px; padding:2px 8px; font-size:1rem; border:2px solid #6a8aba; }
  #playerHUD{ display:flex; flex-direction:column; gap:6px; min-width:140px; }
  #hpBar{ width:180px; height:18px; background:#300; border-radius:9px; overflow:hidden; border:2px solid #700; }
  #hpFill{ height:100%; background:linear-gradient(90deg,#8b0000,var(--blood2)); transition:width .3s; }
  .playerStat{ font-size:.8rem; color:var(--dim); display:flex; justify-content:space-between; }
  #energyOrb{ font-size:1.4rem; color:var(--teal); font-weight:bold; }
  .buffsRow{ display:flex; gap:4px; }
  .buff{ font-size:.65rem; background:#2a1c38; border:1px solid #4a3a5d; border-radius:4px; padding:1px 5px; }
  .buff.weak{ color:#8ec5ff; } .buff.vulnerable{ color:#ff9800; }
  .buff.bleed{ color:#e53935; } .buff.burn{ color:#ff5722; } .buff.regen{ color:var(--teal); }
  .buff.intangible{ color:#b39dff; } .buff.strength{ color:var(--gold); } .buff.dex{ color:#4fc3f7; }

  /* Hand */
  #hand{ display:flex; justify-content:center; gap:10px; padding:10px; min-height:190px; align-items:flex-end; position:relative; }
  .card{
    width:130px; height:185px; background:var(--panel); border:2px solid #555; border-radius:10px;
    cursor:pointer; transition:.15s; position:relative; display:flex; flex-direction:column;
    padding:8px; flex-shrink:0;
  }
  .card:hover{ transform:translateY(-16px); z-index:10; box-shadow:0 12px 30px rgba(0,0,0,.6); }
  .card.common{ border-color:#777; }
  .card.uncommon{ border-color:#4caf50; }
  .card.rare{ border-color:#3f5efb; }
  .card.curse{ border-color:#6a1b3a; background:#2a0f1a; }
  .card.ethereal{ opacity:.85; }
  .card.unplayable{ filter:grayscale(.6); }
  .card .cost{ position:absolute; top:-8px; left:-8px; width:28px; height:28px; border-radius:50%;
    background:radial-gradient(circle at 30% 30%,#4dd0e1,#00838f); color:#003; font-weight:bold;
    display:flex; align-items:center; justify-content:center; font-size:1rem; border:2px solid #b2ebf2; }
  .card .cname{ text-align:center; font-size:.8rem; color:var(--gold); font-weight:bold; margin:6px 0 2px; }
  .card .ctype{ text-align:center; font-size:.6rem; color:var(--dim); margin-bottom:4px; }
  .card .cimg{ text-align:center; font-size:2rem; flex:1; display:flex; align-items:center; justify-content:center; }
  .card .cdesc{ text-align:center; font-size:.62rem; color:var(--text); line-height:1.3; }
  .card .cbar{ height:6px; border-radius:3px; margin-top:6px; }
  .card.common .cbar{ background:#555; } .card.uncommon .cbar{ background:#4caf50; }
  .card.rare .cbar{ background:#3f5efb; } .card.curse .cbar{ background:#6a1b3a; }
  .card.playing{ animation:playCard .3s forwards; }
  @keyframes playCard{ to{ transform:translateY(-260px) scale(.7); opacity:0; } }
  .card .exhaustTag{ position:absolute; bottom:2px; right:4px; font-size:.55rem; color:var(--blood2); }

  /* Controls */
  #combatControls{ position:absolute; right:16px; bottom:210px; display:flex; flex-direction:column; gap:8px; align-items:center; z-index:20; }
  #endTurnBtn{ font-size:1.1rem; padding:12px 20px; }
  #potionBar{ position:absolute; left:16px; bottom:210px; display:flex; gap:8px; z-index:20; }
  .potion{ width:44px; height:44px; border-radius:8px; background:var(--panel); border:2px solid #666;
    display:flex; align-items:center; justify-content:center; font-size:1.4rem; cursor:pointer; transition:.15s; }
  .potion:hover{ transform:translateY(-4px); border-color:var(--gold); }

  .floatText{ position:absolute; font-weight:bold; font-size:1.4rem; pointer-events:none;
    animation:floatUp 1s forwards; z-index:30; }
  @keyframes floatUp{ to{ transform:translateY(-50px); opacity:0; } }
  .floatText.dmg{ color:#ff5252; } .floatText.heal{ color:#66bb6a; } .floatText.block{ color:#90caf9; }
  .floatText.intangible{ color:#b39dff; }

  /* Overlay panels (reward, shop, rest, event, gameover) */
  .overlay{ position:absolute; inset:0; background:rgba(5,2,10,.92); z-index:50;
    display:none; flex-direction:column; align-items:center; justify-content:center; padding:30px; }
  .overlay.active{ display:flex; }
  .overlayBox{ max-width:900px; width:100%; text-align:center; }
  .rewardCards{ display:flex; gap:20px; justify-content:center; flex-wrap:wrap; margin:20px 0; }
  .rewardCards .card{ opacity:.95; }
  .rewardSkip{ margin-top:10px; }
  .rewardPotion, .rewardRelic, .rewardGold{ display:inline-block; margin:8px; padding:10px 16px;
    background:var(--panel); border:1px solid var(--gold); border-radius:8px; }
  .listChoices{ display:flex; flex-direction:column; gap:12px; align-items:center; margin:20px 0; }
  .listChoices .choice{ text-align:left; max-width:520px; }
  .msgText{ font-size:1.1rem; line-height:1.6; color:var(--text); margin:16px 0; }
  .bigText{ font-size:2.5rem; margin:20px 0; }
  .victory .bigText{ background:linear-gradient(90deg,var(--gold),var(--teal));
    -webkit-background-clip:text; background-clip:text; color:transparent; }
  .defeat .bigText{ color:var(--blood2); }

  .shopGrid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(160px,1fr)); gap:14px; margin:16px 0; }
  .shopItem{ background:var(--panel2); border:1px solid #444; border-radius:10px; padding:10px; text-align:center; }
  .shopItem .price{ color:var(--gold); margin-top:6px; }
  .shopItem.sold{ opacity:.4; }
  .itemIcon{ font-size:2rem; }
  .tooltip{ font-size:.7rem; color:var(--dim); }

  .center-wrap{ display:flex; flex-direction:column; align-items:center; gap:14px; }
  #log{ position:absolute; left:0; right:0; bottom:195px; max-height:90px; overflow:hidden;
    font-size:.72rem; color:var(--dim); text-align:center; pointer-events:none; z-index:15; }
  #log div{ padding:1px 0; }
  .banner{ position:absolute; top:40%; left:0; right:0; text-align:center; z-index:40;
    font-size:2.5rem; color:var(--gold); text-shadow:0 0 20px #000; pointer-events:none;
    animation:bannerPop 1.2s forwards; }
  @keyframes bannerPop{ 0%{transform:scale(.5); opacity:0;} 30%{transform:scale(1.2); opacity:1;} 100%{transform:scale(1); opacity:0;} }
</style>
</head>
<body>
<div id="app">
  <!-- Class Select -->
  <div id="classSelect" class="screen active">
    <div>
      <h1>SHADOWS OF THE ABYSS</h1>
      <p class="sub">Three cursed archetypes. One dying world. Climb. Battle. Survive.</p>
    </div>
    <div class="classGrid" id="classGrid"></div>
  </div>

  <!-- Map -->
  <div id="mapScreen" class="screen">
    <div id="hud">
      <div class="info"><span class="act" id="hudAct">Act 1</span> <span id="hudClass">Class</span></div>
      <div class="info"><span class="gold">◈ <span id="hudGold">0</span></span> <span>HP <span id="hudHp">0</span>/<span id="hudMaxHp">0</span></span></div>
    </div>
    <div id="mapScroll">
      <canvas id="mapCanvas"></canvas>
      <div class="mapNodes" id="mapNodes"></div>
    </div>
  </div>

  <!-- Combat -->
  <div id="combatScreen" class="screen">
    <div id="hud">
      <div class="info"><span class="act" id="combatAct">Act 1</span> <span id="combatClass">Class</span></div>
      <div class="info"><span class="gold">◈ <span id="combatGold">0</span></span> <span id="combatPotions"></span></div>
    </div>
    <div id="battlefield">
      <div class="enemyRow" id="enemyRow"></div>
      <div id="log"></div>
      <div id="playerPanel">
        <div id="playerArt">🧙<span class="playerBlock" id="playerBlock" style="display:none">0</span></div>
        <div id="playerHUD">
          <div id="hpBar"><div id="hpFill" style="width:100%"></div></div>
          <div class="playerStat"><span>HP</span><span id="playerHpText">70/70</span></div>
          <div class="playerStat"><span id="energyOrb">⚡3/3</span></div>
          <div class="playerStat"><span>Draw:<span id="drawCount">0</span> | Discard:<span id="discardCount">0</span></span></div>
          <div class="buffsRow" id="playerBuffs"></div>
        </div>
      </div>
    </div>
    <div id="potionBar"></div>
    <div id="hand"></div>
    <div id="combatControls">
      <button class="btn" id="endTurnBtn">End Turn</button>
    </div>
  </div>

  <!-- Overlays -->
  <div id="overlay" class="overlay"><div class="overlayBox" id="overlayBox"></div></div>
</div>

<script>
"use strict";
"use strict";

// ============================================================
// SHADOWS OF THE ABYSS — game engine
// ============================================================

// ---------- Utility ----------
const rand = (a,b)=>Math.floor(Math.random()*(b-a+1))+a;
const pick = arr => arr[Math.floor(Math.random()*arr.length)];
const shuffle = arr => { const a=[...arr]; for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]];} return a; };
const clamp = (v,a,b)=>Math.max(a,Math.min(b,v));

// ---------- Card definitions ----------
// base effect function returns an array of actions to perform on play.
// Each card: {id, name, type, cost, rarity, img, cls, desc, upg, func, exhaust, ethereal, playable}
// func(ctx) performs effects. ctx = combat state.

const CARDS = {};
function defCard(c){ CARDS[c.id]=c; return c; }

// Basic cards (shared)
defCard({id:'strike',name:'Strike',type:'Attack',cost:1,rarity:'Basic',img:'⚔️',desc:'Deal 6 damage.',upg:'Deal 8 damage.',func:ctx=>{ ctx.damage(ctx.target,6,ctx.player); }});
defCard({id:'defend',name:'Defend',type:'Skill',cost:1,rarity:'Basic',img:'🛡️',desc:'Gain 5 Block.',upg:'Gain 8 Block.',func:ctx=>{ ctx.player.block += ctx.player.dex? ctx.player.dex:0; ctx.player.block += 5; }});

// ---- Blood Reaper ----
defCard({id:'sanguine',name:'Sanguine Strike',type:'Attack',cost:1,rarity:'Common',img:'🩸',cls:'blood',desc:'Deal 8 damage. Heal 2 HP.',upg:'Deal 10 damage. Heal 3 HP.',func:ctx=>{ const d=ctx.damage(ctx.target,8,ctx.player); ctx.player.heal(2); if(ctx.relics.bloodstone) ctx.player.heal(1); }});
defCard({id:'hemorrhage',name:'Hemorrhage',type:'Attack',cost:1,rarity:'Common',img:'🔪',cls:'blood',desc:'Deal 5 damage. Apply 3 Bleed.',upg:'Deal 6 damage. Apply 4 Bleed.',func:ctx=>{ ctx.damage(ctx.target,5,ctx.player); ctx.applyToEnemy(ctx.target,'bleed',3); }});
defCard({id:'bloodletting',name:'Bloodletting',type:'Attack',cost:1,rarity:'Uncommon',img:'🩸',cls:'blood',desc:'Lose 3 HP. Deal 12 damage.',upg:'Lose 2 HP. Deal 15 damage.',func:ctx=>{ if(ctx.player.hp>3) ctx.player.takeDamage(3); ctx.damage(ctx.target,12,ctx.player); }});
defCard({id:'crimsonEruption',name:'Crimson Eruption',type:'Attack',cost:2,rarity:'Rare',img:'🌋',cls:'blood',desc:'Deal 8 damage to ALL enemies. Lose 2 HP.',upg:'Deal 10 damage to ALL. Lose 2 HP.',func:ctx=>{ ctx.player.takeDamage(2); ctx.damageAll(8,ctx.player); }});
defCard({id:'crimsonShield',name:'Crimson Shield',type:'Skill',cost:1,rarity:'Common',img:'🛡️',cls:'blood',desc:'Gain 6 Block. Lose 2 HP.',upg:'Gain 8 Block. Lose 2 HP.',func:ctx=>{ if(ctx.player.hp>2) ctx.player.takeDamage(2); ctx.player.block += (6+ctx.player.dex); }});
defCard({id:'bloodWard',name:'Blood Ward',type:'Skill',cost:1,rarity:'Common',img:'🛡️',cls:'blood',desc:'Gain 3 Block. Gain 1 Regeneration.',upg:'Gain 4 Block. Gain 2 Regeneration.',func:ctx=>{ ctx.player.block += (3+ctx.player.dex); ctx.player.addBuff('regen',1,1); }});
defCard({id:'sacrificialBarrier',name:'Sacrificial Barrier',type:'Skill',cost:2,rarity:'Uncommon',img:'🛡️',cls:'blood',desc:'Gain 10 Block. Lose 5 HP.',upg:'Gain 13 Block. Lose 5 HP.',func:ctx=>{ if(ctx.player.hp>5) ctx.player.takeDamage(5); ctx.player.block += (10+ctx.player.dex); }});
defCard({id:'vitalSurge',name:'Vital Surge',type:'Skill',cost:0,rarity:'Rare',img:'💚',cls:'blood',desc:'Gain Block equal to missing HP. Exhaust.',upg:'Gain Block = missing HP + 3. Exhaust.',exhaust:true,func:ctx=>{ const miss=ctx.player.maxHp-ctx.player.hp; ctx.player.block += (miss+3+ctx.player.dex); }});
defCard({id:'lifeTap',name:'Life Tap',type:'Skill',cost:0,rarity:'Common',img:'📖',cls:'blood',desc:'Draw 2 cards. Lose 3 HP.',upg:'Draw 3 cards. Lose 3 HP.',func:ctx=>{ if(ctx.player.hp>3) ctx.player.takeDamage(3); ctx.drawCards(ctx.player.upgrade?3:2); }});
defCard({id:'rendFlesh',name:'Rend Flesh',type:'Skill',cost:1,rarity:'Uncommon',img:'🔪',cls:'blood',desc:'Apply 5 Bleed to ALL enemies. Lose 2 HP.',upg:'Apply 7 Bleed to ALL. Lose 2 HP.',func:ctx=>{ ctx.player.takeDamage(2); ctx.damageAll(0,ctx.player,true,7); }});
defCard({id:'soulSiphon',name:'Soul Siphon',type:'Power',cost:2,rarity:'Uncommon',img:'💀',cls:'blood',desc:'Start of turn: gain 1 Energy and lose 1 HP.',upg:'Cost 1. Same effect.',func:()=>{}});
defCard({id:'bloodPact',name:'Blood Pact',type:'Power',cost:1,rarity:'Rare',img:'🩸',cls:'blood',desc:'Lose HP from a card: gain 1 Strength.',upg:'gain 2 Strength.',func:()=>{}});

// ---- Shadowmancer ----
defCard({id:'shadowBolt',name:'Shadow Bolt',type:'Attack',cost:1,rarity:'Common',img:'🔮',cls:'shadow',desc:'Deal 6 damage. Apply 1 Weak.',upg:'Deal 8 damage. Apply 2 Weak.',func:ctx=>{ ctx.damage(ctx.target,6,ctx.player); ctx.applyToEnemy(ctx.target,'weak',1); }});
defCard({id:'graspDespair',name:'Grasp of Despair',type:'Attack',cost:1,rarity:'Common',img:'🕷️',cls:'shadow',desc:'Deal 4 damage. Apply 1 Vulnerable.',upg:'Deal 5 damage. Apply 2 Vulnerable.',func:ctx=>{ ctx.damage(ctx.target,4,ctx.player); ctx.applyToEnemy(ctx.target,'vulnerable',1); }});
defCard({id:'nightmareLash',name:'Nightmare Lash',type:'Attack',cost:2,rarity:'Uncommon',img:'🌙',cls:'shadow',desc:'Deal 7 damage. If enemy Weak, +7 damage.',upg:'Deal 9 damage. If Weak, +9.',func:ctx=>{ let d=7; if(ctx.target.statuses.weak>0) d+=7; ctx.damage(ctx.target,d,ctx.player); }});
defCard({id:'eternalDarkness',name:'Eternal Darkness',type:'Attack',cost:2,rarity:'Rare',img:'🌑',cls:'shadow',desc:'Deal 3 damage to a random enemy 3 times.',upg:'Deal 4 damage, 3 times.',func:ctx=>{ const alive=ctx.enemies.filter(e=>e.hp>0); for(let i=0;i<3;i++){ const e=pick(alive); ctx.damage(e,3,ctx.player); if(e.hp<=0){ const a=ctx.enemies.filter(x=>x.hp>0); if(a.length) alive.splice(alive.indexOf(e),1); alive.push(e);} }});
defCard({id:'cloakShadows',name:'Cloak of Shadows',type:'Skill',cost:1,rarity:'Common',img:'🌫️',cls:'shadow',desc:'Gain 5 Block. Draw 1 card.',upg:'Gain 7 Block. Draw 1 card.',func:ctx=>{ ctx.player.block += (5+ctx.player.dex); ctx.drawCards(1); }});
defCard({id:'hauntingMist',name:'Haunting Mist',type:'Skill',cost:1,rarity:'Common',img:'🌫️',cls:'shadow',desc:'Gain 7 Block. Apply 1 Weak to ALL enemies.',upg:'Gain 9 Block. Apply 1 Weak to ALL.',func:ctx=>{ ctx.player.block += (7+ctx.player.dex); for(const e of ctx.enemies) if(e.hp>0) ctx.applyToEnemy(e,'weak',1); }});
defCard({id:'phantomArmor',name:'Phantom Armor',type:'Skill',cost:1,rarity:'Uncommon',img:'🛡️',cls:'shadow',desc:'Gain 8 Block. Next turn gain 2 Dexterity.',upg:'Gain 10 Block. Next turn gain 3 Dexterity.',func:ctx=>{ ctx.player.block += (8+ctx.player.dex); ctx.player.pendingDex = (ctx.player.upgrade?3:2); }});
defCard({id:'whisperingShroud',name:'Whispering Shroud',type:'Skill',cost:1,rarity:'Rare',img:'🛡️',cls:'shadow',desc:'Gain 4 Block. Next turn gain 4 Block.',upg:'Gain 5 Block. Next turn gain 5 Block.',func:ctx=>{ ctx.player.block += (4+ctx.player.dex); ctx.player.pendingBlock = (ctx.player.upgrade?5:4); }});
defCard({id:'shadowform',name:'Shadowform',type:'Skill',cost:2,rarity:'Uncommon',img:'👤',cls:'shadow',desc:'Gain 2 Intangible. Exhaust.',upg:'Gain 3 Intangible. Exhaust.',exhaust:true,func:ctx=>{ ctx.player.addBuff('intangible',2,1); }});
defCard({id:'curseMisfortune',name:'Curse of Misfortune',type:'Skill',cost:1,rarity:'Common',img:'☠️',cls:'shadow',desc:'Enemy loses 3 Strength this turn. Apply 1 Vulnerable.',upg:'Loses 4 Strength. Apply 2 Vulnerable.',func:ctx=>{ ctx.applyToEnemy(ctx.target,'vulnerable',1); ctx.pendingStrengthDmg = (ctx.pendingStrengthDmg||0); ctx.target._tempStrengthLoss = (ctx.target._tempStrengthLoss||0)+(ctx.player.upgrade?4:3); }});
defCard({id:'darkRitual',name:'Dark Ritual',type:'Skill',cost:0,rarity:'Uncommon',img:'📖',cls:'shadow',desc:'Gain 2 Energy. Shuffle a Dazed into draw pile.',upg:'Gain 2 Energy. Shuffle Dazed into discard.',func:ctx=>{ ctx.player.energy += 2; ctx.shuffleDazed(ctx.player.upgrade?'discard':'draw'); }});
defCard({id:'umbralMastery',name:'Umbral Mastery',type:'Power',cost:2,rarity:'Rare',img:'👁️',cls:'shadow',desc:'Start of turn: apply 1 Weak to ALL enemies.',upg:'Apply 2 Weak to ALL.',func:()=>{}});

// ---- Hellforged ----
defCard({id:'firebolt',name:'Firebolt',type:'Attack',cost:1,rarity:'Common',img:'🔥',cls:'hell',desc:'Deal 7 damage. Apply 2 Burn.',upg:'Deal 9 damage. Apply 3 Burn.',func:ctx=>{ ctx.damage(ctx.target,7,ctx.player); ctx.applyToEnemy(ctx.target,'burn',2); }});
defCard({id:'hellfireBlast',name:'Hellfire Blast',type:'Attack',cost:2,rarity:'Uncommon',img:'🔥',cls:'hell',desc:'Deal 9 damage to ALL enemies.',upg:'Deal 12 damage to ALL.',func:ctx=>{ ctx.damageAll(9,ctx.player); }});
defCard({id:'searingStrike',name:'Searing Strike',type:'Attack',cost:1,rarity:'Common',img:'🗡️',cls:'hell',desc:'Deal 6 damage. Gain 1 Energy next turn.',upg:'Deal 8 damage. Gain 1 Energy next turn.',func:ctx=>{ ctx.damage(ctx.target,6,ctx.player); ctx.player.pendingEnergy = (ctx.player.pendingEnergy||0)+1; }});
defCard({id:'immolate',name:'Immolate',type:'Attack',cost:2,rarity:'Rare',img:'🔥',cls:'hell',desc:'Deal 12 damage. Exhaust.',upg:'Deal 16 damage. Exhaust.',exhaust:true,func:ctx=>{ ctx.damage(ctx.target,12,ctx.player); }});
defCard({id:'obsidianSkin',name:'Obsidian Skin',type:'Skill',cost:1,rarity:'Common',img:'🛡️',cls:'hell',desc:'Gain 7 Block.',upg:'Gain 10 Block.',func:ctx=>{ ctx.player.block += (7+ctx.player.dex); }});
defCard({id:'flameWard',name:'Flame Ward',type:'Skill',cost:1,rarity:'Common',img:'🛡️',cls:'hell',desc:'Gain 5 Block. Apply 2 Burn to a random enemy.',upg:'Gain 6 Block. Apply 3 Burn.',func:ctx=>{ const e=pick(ctx.enemies.filter(x=>x.hp>0)); ctx.player.block += (5+ctx.player.dex); ctx.applyToEnemy(e,'burn',ctx.player.upgrade?3:2); }});
defCard({id:'demonicResilience',name:'Demonic Resilience',type:'Skill',cost:1,rarity:'Uncommon',img:'💚',cls:'hell',desc:'Gain 4 Block. Heal 2 HP.',upg:'Gain 5 Block. Heal 3 HP.',func:ctx=>{ ctx.player.block += (4+ctx.player.dex); ctx.player.heal(2); }});
defCard({id:'brimstoneShield',name:'Brimstone Shield',type:'Skill',cost:1,rarity:'Rare',img:'🛡️',cls:'hell',desc:'Gain 3 Block. Gain 2 Strength this turn.',upg:'Gain 4 Block. Gain 3 Strength this turn.',func:ctx=>{ ctx.player.block += (3+ctx.player.dex); ctx.player.addBuff('strength',2,1); }});
defCard({id:'summonImp',name:'Summon Imp',type:'Skill',cost:1,rarity:'Common',img:'👺',cls:'hell',desc:'Add an Imp Claw to hand. Exhaust.',upg:'Add Imp Claw+.',exhaust:true,func:ctx=>{ const c = makeImpClaw(ctx.player.upgrade); ctx.player.hand.push(c); }});
defCard({id:'infernalPact',name:'Infernal Pact',type:'Skill',cost:0,rarity:'Uncommon',img:'🔥',cls:'hell',desc:'Lose 2 HP. Gain 2 Energy. Add Imp Claw to hand.',upg:'Lose 1 HP. Gain 2 Energy. Add Imp Claw+.',func:ctx=>{ if(ctx.player.hp>1) ctx.player.takeDamage(ctx.player.upgrade?1:2); ctx.player.energy += 2; const c = makeImpClaw(ctx.player.upgrade); ctx.player.hand.push(c); }});
defCard({id:'hellfireAura',name:'Hellfire Aura',type:'Power',cost:2,rarity:'Rare',img:'🔥',cls:'hell',desc:'Start of turn: deal 3 damage to ALL enemies.',upg:'Deal 5 damage to ALL.',func:()=>{}});
defCard({id:'soulBurn',name:'Soul Burn',type:'Skill',cost:1,rarity:'Uncommon',img:'🔥',cls:'hell',desc:'Apply 8 Burn to an enemy. Draw 1 card.',upg:'Apply 10 Burn. Draw 1 card.',func:ctx=>{ ctx.applyToEnemy(ctx.target,'burn',ctx.player.upgrade?10:8); ctx.drawCards(1); }});

// Imp Claw token
function makeImpClaw(upgraded){ return {id:'impClaw',name:upgraded?'Imp Claw+':'Imp Claw',type:'Attack',cost:0,rarity:'Basic',img:'👺',desc:upgraded?'Deal 6 damage.':'Deal 4 damage.',upg:'',exhaust:true,token:true,func:ctx=>{ ctx.damage(ctx.target, upgraded?6:4, ctx.player); }}; }

// Colorless
defCard({id:'flashOfInsight',name:'Flash of Insight',type:'Skill',cost:1,rarity:'Common',img:'📖',desc:'Draw 2 cards.',upg:'Draw 3 cards.',func:ctx=>{ ctx.drawCards(ctx.player.upgrade?3:2); }});
defCard({id:'ironWill',name:'Iron Will',type:'Skill',cost:1,rarity:'Common',img:'🛡️',desc:'Gain 8 Block.',upg:'Gain 11 Block.',func:ctx=>{ ctx.player.block += 8+ctx.player.dex; }});
defCard({id:'quickSlash',name:'Quick Slash',type:'Attack',cost:1,rarity:'Common',img:'🗡️',desc:'Deal 7 damage.',upg:'Deal 9 damage.',func:ctx=>{ ctx.damage(ctx.target,7,ctx.player); }});
defCard({id:'potentBrew',name:'Potent Brew',type:'Skill',cost:0,rarity:'Uncommon',img:'⚗️',desc:'Gain 1 Energy. Exhaust.',upg:'Gain 2 Energy. Exhaust.',exhaust:true,func:ctx=>{ ctx.player.energy += ctx.player.upgrade?2:1; }});

// Curses / status
defCard({id:'wound',name:'Wound',type:'Curse',cost:0,rarity:'Basic',img:'☠️',curse:true,ethereal:true,exhaust:true,desc:'Unplayable. Ethereal.',func:()=>{}});
defCard({id:'dazed',name:'Dazed',type:'Status',cost:0,rarity:'Basic',img:'💫',curse:true,ethereal:true,exhaust:true,desc:'Unplayable. Ethereal.',func:()=>{}});
defCard({id:'doubt',name:'Doubt',type:'Curse',cost:0,rarity:'Basic',img:'❓',curse:true,desc:'Unplayable. End of turn, lose 1 HP.',func:()=>{}});
defCard({id:'impClawCard',name:'Imp Claw',type:'Attack',cost:0,rarity:'Basic',img:'👺',desc:'Deal 4 damage. Exhaust.',func:()=>{}});

// ---------- Card pools per class ----------
const CLASS_POOLS = {
  blood: ['strike','strike','strike','strike','strike','defend','defend','defend','defend','sanguine','hemorrhage','bloodletting','crimsonEruption','crimsonShield','bloodWard','sacrificialBarrier','vitalSurge','lifeTap','rendFlesh','soulSiphon','bloodPact'],
  shadow: ['strike','strike','strike','strike','strike','defend','defend','defend','defend','shadowBolt','graspDespair','nightmareLash','eternalDarkness','cloakShadows','hauntingMist','phantomArmor','whisperingShroud','shadowform','curseMisfortune','darkRitual','umbralMastery'],
  hell: ['strike','strike','strike','strike','strike','defend','defend','defend','defend','firebolt','hellfireBlast','searingStrike','immolate','obsidianSkin','flameWard','demonicResilience','brimstoneShield','summonImp','infernalPact','hellfireAura','soulBurn']
};

// ---------- Relics ----------
const RELICS = {
  bloodstone:{name:'Bloodstone',img:'🔴',rarity:'Common',desc:'Whenever you lose HP from a card, heal 1 HP.'},
  demonHorns:{name:'Demon Horns',img:'🐉',rarity:'Uncommon',desc:'Start of combat: add a random Imp Claw to hand (once per combat). Hellforged only.',cls:'hell'},
  shadowCloak:{name:'Shadow Cloak',img:'🟣',rarity:'Rare',desc:'Start of combat: gain 1 Intangible.'},
  cursedAmulet:{name:'Cursed Amulet',img:'📿',rarity:'Common',desc:'Start of combat: apply 1 Weak to ALL enemies.'},
  tomeDark:{name:'Tome of Dark Rituals',img:'📚',rarity:'Uncommon',desc:'Whenever you play a Power card, draw 1 card.'},
  bleedingEdge:{name:'Bleeding Edge',img:'🔪',rarity:'Common',desc:'Start of your turn: apply 1 Bleed to a random enemy.'},
  soulEmber:{name:'Soul Ember',img:'🔥',rarity:'Rare',desc:'Whenever you apply Burn to an enemy, deal 2 additional damage.'},
  eternalHourglass:{name:'Eternal Hourglass',img:'⏳',rarity:'Uncommon',desc:'Start of turn: if no cards in hand, gain 1 Energy.'},
  sacrificialDagger:{name:'Sacrificial Dagger',img:'🗡️',rarity:'Rare',desc:'Your first Attack that costs HP each combat costs 1 less HP. Blood Reaper only.',cls:'blood'},
  vialShadows:{name:'Vial of Shadows',img:'🧪',rarity:'Rare',desc:'Upon obtaining, remove a card from your deck (one-time).',oneTime:true},
  ashenArmor:{name:'Ashen Armor',img:'🛠️',rarity:'Common',desc:'At the end of combat, heal 6 HP.'},
  greedyImp:{name:'Greedy Imp',img:'🪙',rarity:'Common',desc:'Enemies drop 10 extra Gold.'},
  bossCursedCrown:{name:'Cursed Crown',img:'👑',rarity:'Boss',desc:'Gain 1 Energy at start of each turn. Curses can no longer be removed.'},
  bossEctoplasm:{name:'Ectoplasm',img:'👻',rarity:'Boss',desc:'Gain 1 Energy. Lose all Gold and gain no more Gold.'},
  bossPhilosopher:{name:'Philosopher\'s Stone',img:'💎',rarity:'Boss',desc:'Gain 1 Energy. All enemies start combat with 1 Strength.'},
  bossBlackBlood:{name:'Black Blood',img:'🩸',rarity:'Boss',desc:'Start of combat, lose 10% max HP. Playing an Attack heals 1 HP.'},
  bossShadowAmulet:{name:'Shadow Amulet',img:'🌑',rarity:'Boss',desc:'Start each turn with 1 Intangible. Reduce max HP by 15.'}
};
const BOSS_RELIC_IDS = ['bossCursedCrown','bossEctoplasm','bossPhilosopher','bossBlackBlood','bossShadowAmulet'];

// ---------- Potions ----------
const POTIONS = {
  bloodVial:{name:'Blood Vial',img:'❤️',effect:'heal15'},
  firebomb:{name:'Firebomb',img:'💥',effect:'firebomb'},
  dexTonic:{name:'Dex Tonic',img:'💪',effect:'dex'},
  energyElixir:{name:'Energy Elixir',img:'🔵',effect:'energy'},
  cursedBrew:{name:'Cursed Brew',img:'🟣',effect:'cursedBrew'},
  bleedingFlask:{name:'Bleeding Flask',img:'🩸',effect:'bleeding'},
  impInBottle:{name:'Imp-in-a-Bottle',img:'👺',effect:'impInBottle'},
  smokeBomb:{name:'Smoke Bomb',img:'💨',effect:'smokeBomb'}
};
const POTION_IDS = Object.keys(POTIONS);

// ---------- Enemies ----------
// intent: function(ctx, enemy) -> {type, ...} describing next action
function makeEnemy(def, idx){
  return {
    id:def.id, name:def.name, hp:def.hp, maxHp:def.hp, idx,
    img:def.img, alive:true,
    statuses:{}, block:0, strength:0, _turn:0, _tempStrengthLoss:0,
    pendingBlock:0, intent:null,
    isMinion:def.isMinion||false
  };
}

// Intent patterns
function enemyIntent(enemy, ctx){
  const t = enemy._turn % enemy.pattern.length;
  enemy._turn++;
  return enemy.pattern[t];
}

function P(atk, dmg){ return {type:'attack', atk, dmg}; }
function Pdef(x){ return {type:'defend', x}; }
function Pstr(x){ return {type:'buff_strength', x}; }
function Psum(monk){ return {type:'summon', monk}; }
function Pmulti(n, d){ return {type:'multi', n, d}; }
function Pweak(x){ return {type:'apply_weak', x}; }
function Pvuln(x){ return {type:'apply_vulnerable', x}; }
function Pbled(x){ return {type:'apply_bleed', x}; }
function Pburn(x){ return {type:'apply_burn', x}; }
function Pbuff(x){ return {type:'buff', x}; }
function Pdrain(atk, heal){ return {type:'drain', atk, heal}; }
function PvulnAll(x){ return {type:'apply_vulnerable', x}; }

const ENEMY_DEFS = {
  // Act 1
  skeleton:{id:'skeleton',name:'Skeleton Warrior',hp:22,img:'💀',pattern:[P(6),P(6),Pdef(8)]},
  zombie:{id:'zombie',name:'Zombie Brute',hp:28,img:'🧟',pattern:[P(8),Pweak(1),P(8)]},
  cultist:{id:'cultist',name:'Dark Cultist',hp:20,img:'🧟‍♂️',pattern:[Pstr(3),P(5),P(5)]},
  rat:{id:'rat',name:'Rat Swarm',hp:15,img:'🐀',pattern:[Pmulti(2,3),Pmulti(2,3)]},
  tombGuardian:{id:'tombGuardian',name:'Tomb Guardian',hp:42,img:'🗿',pattern:[{type:'attack',atk:6,dmg:6,block:8},{type:'multi',n:3,d:4},{type:'attack',atk:12}]},
  bloodWitch:{id:'bloodWitch',name:'Blood Witch',hp:36,img:'🧙‍♀️',pattern:[Pdrain(5,4),Pvuln(1),Pdrain(7,4)]},
  lichKing:{id:'lichKing',name:'Lich King',hp:65,img:'🧟‍♂️',boss:true,
    phase1:[Psum('skeletonMinion'),P(8),Pdef(12)],
    phase2:[P(10),P(10),Pdef(15)]},
  // Act 2
  wraith:{id:'wraith',name:'Wraith',hp:30,img:'👻',pattern:[Pweak(1),P(7),Pdef(10)]},
  beast:{id:'beast',name:'Corrupted Beast',hp:38,img:'🐻',pattern:[P(12),{type:'defend',x:5,type2:'vuln_self'},{type:'defend',x:5,type2:'vuln_self',atk:12}]},
  thorned:{id:'thorned',name:'Thorned Horror',hp:25,img:'🌿',pattern:[Pmulti(3,4),Pbled(3),P(8)]},
  witchDoc:{id:'witchDoc',name:'Witch Doctor',hp:22,img:'🧝‍♀️',pattern:[{type:'buff_self',block:4,regen:2},{type:'poison',atk:6,poison:2},{type:'poison',atk:6,poison:2}]},
  treant:{id:'treant',name:'Treant Protector',hp:50,img:'🌳',pattern:[{type:'attack',atk:9,block:6},{type:'apply_vulnerable',x:2},{type:'multi',n:2,d:10}]},
  plagueShaman:{id:'plagueShaman',name:'Plague Shaman',hp:42,img:'🧟‍♀️',pattern:[Pbled(4),Psum('plagueRat'),P(8)]},
  eldritchTreant:{id:'eldritchTreant',name:'Eldritch Treant',hp:75,img:'🌲',boss:true,
    phase1:[{type:'apply_weak',x:2,atk:10},{type:'defend',x:15,regen:2},{type:'multi',n:2,d:8}],
    phase2:[{type:'apply_vulnerable',x:1,atk:14},{type:'defend',x:20,heal:5},{type:'apply_vulnerable',x:1,atk:14}]},
  // Act 3
  hellhound:{id:'hellhound',name:'Hellhound',hp:26,img:'🐕',pattern:[Pburn(2),P(8),Pdef(9)]},
  shadowDemo:{id:'shadowDemo',name:'Shadow Demon',hp:30,img:'👹',pattern:[Pweak(1),P(8),Pdef(9)]},
  infernalImp:{id:'infernalImp',name:'Infernal Imp',hp:18,img:'👺',pattern:[P(5),P(5),Psum('infernalImp')]},
  abyssCultist:{id:'abyssCultist',name:'Abyssal Cultist',hp:24,img:'🕴️',pattern:[{type:'buff_all',str:2},P(7),P(7)]},
  flamespawn:{id:'flamespawn',name:'Flamespawn',hp:48,img:'🔥',pattern:[Pburn(3),{type:'defend',x:10,buff_str:2},{type:'multi',n:2,d:8}]},
  dreadKnight:{id:'dreadKnight',name:'Dread Knight',hp:55,img:'🛡️',pattern:[P(12),Pbled(3),{type:'defend',x:12,heal:4}]},
  archdemon:{id:'archdemon',name:'Archdemon',hp:85,img:'😈',boss:true,
    phase1:[Psum('hellhound'),Pburn(2),Pdef(15)],
    phase2:[P(16),Pmulti(3,7),P(16)]}
};

function minionDef(kind){
  if(kind==='skeletonMinion') return {id:'skeletonMinion',name:'Skeleton Minion',hp:15,img:'💀',isMinion:true,pattern:[P(5),P(5),Pdef(6)]};
  if(kind==='plagueRat') return {id:'plagueRat',name:'Plague Rat',hp:18,img:'🐀',isMinion:true,pattern:[P(5),P(5),P(5)]};
  if(kind==='infernalImp') return {id:'infernalImp',name:'Infernal Imp',hp:18,img:'👺',isMinion:true,pattern:[P(5),P(5),Psum('infernalImp')]};
  if(kind==='hellhound') return {id:'hellhound',name:'Hellhound',hp:26,img:'🐕',isMinion:true,pattern:[Pburn(2),P(8),Pdef(9)]};
  return {id:'skeletonMinion',name:'Skeleton Minion',hp:15,img:'💀',isMinion:true,pattern:[P(5),P(5)]};
}

// ---------- Events ----------
const EVENTS = {
  altar:{name:'The Forgotten Altar',text:'A blood-stained altar hums with forbidden power. It hungers for a sacrifice.',
    choices:[
      {label:'Offer 15 HP — obtain a random Rare relic',fx:g=>{ if(g.player.hp<=15) return {fail:'Not enough HP!'}; g.player.takeDamage(15,false); const rare=RELICS[Object.keys(RELICS).find(k=>RELICS[k].rarity==='Rare')]; const id=pick(['soulEmber','shadowCloak','hellfireAura','sacrificialDagger']); /* keep relic */ g.gainRelic('soulEmber'); return {msg:'You obtain the Soul Ember!';}; },
       simpleRelic:'Rare'},
      {label:'Pray — heal 10 HP, gain a Curse (Doubt)',fx:g=>{ g.player.heal(10); g.addCurse('doubt'); return {msg:'The altar heals you (-10 HP curse). You gain Doubt.'}; }},
      {label:'Leave',fx:g=>{ return {msg:'You leave the altar untouched.'}; }}
    ]},
  peddler:{name:'Shadowy Peddler',text:'A hooded figure whispers: "Trade, or buy... or take."',
    choices:[
      {label:'Trade a card — remove a card, gain 100 Gold',fx:g=>{ const removed=g.removeCardForEvent(); if(!removed) return {fail:'No cards to trade!'}; g.gold+=100; return {msg:`You sold ${removed.name} for 100 Gold.`}; }},
      {label:'Buy his wares — lose 50 Gold, get a Potion + Common relic',fx:g=>{ if(g.gold<50) return {fail:'Not enough Gold!'}; g.gold-=50; g.addPotion(pick(POTION_IDS)); g.gainRelic(pick(Object.keys(RELICS).filter(k=>RELICS[k].rarity==='Common'))); return {msg:'You acquire a Potion and a Common relic.'}; }},
      {label:'Attack — combat with Shadow Thief, 75 Gold on victory',fx:g=>{ g.startCombat('shadowThief'); return {msg:'You lunge at the peddler!'}; }}
    ]},
  spring:{name:'Cursed Spring',text:'A spring bubbles with dark, tempting water.',
    choices:[
      {label:'Drink — lose 10 HP, remove all Curses from deck',fx:g=>{ g.player.takeDamage(10,false); g.removeAllCurses(); return {msg:'The curses wash away. You feel weaker (-10 HP).'; }},
      {label:'Bottle it — obtain a Cursed Brew',fx:g=>{ g.addPotion('cursedBrew'); return {msg:'You bottle the dark water (Cursed Brew).'; }},
      {label:'Leave',fx:g=>{ return {msg:'You leave the spring alone.'}; }}
    ]},
  restlessDead:{name:'Restless Dead',text:'Ghostly apparitions offer a bargain: "Upgrade, or be denied."',
    choices:[
      {label:'Accept — lose 20 HP, upgrade 2 random cards',fx:g=>{ g.player.takeDamage(20,false); const up=g.upgradeRandomCards(2); if(!up||up.length===0) return {msg:'You lose 20 HP but have no cards to upgrade.'}; return {msg:`You upgrade ${up.length} card(s): ${up.join(', ')}. (-20 HP)`}; },
      {label:'Refuse',fx:g=>{ return {msg:'The ghosts fade, disappointed.'}; }}
    ]},
  gambler:{name:'Impish Gambler',text:'A grinning imp holds up gold. "Bet, or wager a card?"',
    choices:[
      {label:'Bet 30 Gold — 50% to double, else lose it',fx:g=>{ if(g.gold<30) return {fail:'Not enough Gold!'}; if(Math.random()<0.5){ g.gold+=30; return {msg:'The imp flips heads! You win 60 Gold.'}; } g.gold-=30; return {msg:'The imp flips tails. You lose 30 Gold.'}; },
      {label:'Wager a card — if Curse gain 100 Gold, else lose the card',fx:g=>{ const c=g.removeRandomCardForBet(); if(!c) return {fail:'No cards to wager!'}; if(c.curse){ g.gold+=100; return {msg:`You wagered ${c.name} (a Curse)! 100 Gold!`; } return {msg:`You wagered ${c.name} and lost it.`}; },
      {label:'Leave',fx:g=>{ return {msg:'You decline the imp\'s game.'}; }}
    ]},
  library:{name:'Ancient Library',text:'Dusty tomes whisper forgotten knowledge.',
    choices:[
      {label:'Study — gain 1 Energy next combat, lose 5 HP',fx:g=>{ g.player.nextCombatEnergy=1; g.player.takeDamage(5,false); return {msg:'You study ancient lore. Next combat: +1 Energy. (-5 HP)'; }},
      {label:'Read forbidden page — add a random Power card to deck, gain Doubt',fx:g=>{ const powers=g.classPool.filter(id=>CARDS[id]&&CARDS[id].type==='Power'); if(powers.length===0) return {msg:'No power cards available.'}; const chosen=pick(powers); g.player.deck.push(makeCard(chosen)); g.addCurse('doubt'); return {msg:`You add ${CARDS[chosen].name} to your deck and gain Doubt.`}; },
      {label:'Leave',fx:g=>{ return {msg:'You leave the library undisturbed.'}; }}
    ]}
};

// Special event enemy
ENEMY_DEFS.shadowThief={id:'shadowThief',name:'Shadow Thief',hp:30,img:'🥷',pattern:[P(6),Pweak(1),P(7),P(5)]};

// ============================================================
// GAME STATE
// ============================================================
const Game = {
  player: null,
  currentAct: 0,
  map: null,
  currentNode: null,
  combat: null,
  nextCombatEnergy: 0,
  goldGainBlocked: false,
  running: false
};

const CLASSES = {
  blood:{name:'Blood Reaper',img:'🩸',desc:'Sacrifice HP for power, life steal, and Bleed.',sig:'Sanguine Strike'},
  shadow:{name:'Shadowmancer',img:'🔮',desc:'Debuffs, Intangible, and card manipulation.',sig:'Shadow Bolt'},
  hell:{name:'Hellforged',img:'🔥',desc:'Fire damage, Burn, and summoning Imps.',sig:'Firebolt'}
};

function makeCard(id, upgraded){
  const base = CARDS[id];
  return {
    id: base.id, name: base.name, type: base.type, cost: base.cost,
    rarity: base.rarity, img: base.img, cls: base.cls,
    desc: base.desc, upg: base.upg, func: base.func,
    exhaust: base.exhaust, ethereal: base.ethereal, curse: base.curse,
    token: base.token, upgraded: !!upgraded, playable: true,
    upgradeCard: upgraded ? {id:id+'+'} : null
  };
}

function newPlayer(classId){
  const cls = CLASSES[classId];
  const deckIds = CLASS_POOLS[classId].slice(0,10); // 5 strike? handled below
  // Build starting deck: 5 Strike, 4 Defend, 1 signature
  const startDeck = [];
  const sig = {blood:'sanguine',shadow:'shadowBolt',hell:'firebolt'}[classId];
  for(let i=0;i<5;i++) startDeck.push(makeCard('strike'));
  for(let i=0;i<4;i++) startDeck.push(makeCard('defend'));
  startDeck.push(makeCard(sig));
  const p = {
    classId, className:cls.name, classImg:cls.img,
    maxHp:70, hp:70, gold:99,
    deck: startDeck,
    relics: [], potions: [],
    potionSlots: 3,
    nextCombatEnergy: 0
  };
  return p;
}

// ---------- Map generation ----------
// Fixed floor layout per act. Node types: enemy(elite branch optional), event, shop, rest, boss
// Each act shares structure but different enemy pools.
function buildMap(actNum){
  // Return array of floors (index by floor 2..15). Floor 1 = start.
  const actNames = ['The Crypts','Blighted Woods','Abyssal Spire'];
  const floors = {};
  // Define fixed layout
  const layout = {
    2: {type:'enemy', branch:true},
    3: {type:'enemy'},
    4: {type:'event'},
    5: {type:'shop'},
    6: {type:'enemy'},
    7: {type:'rest'},
    8: {type:'enemy', branch:true},
    9: {type:'elite', alt:'enemy'},
    10:{type:'event'},
    11:{type:'enemy'},
    12:{type:'elite', alt:'enemy'},
    13:{type:'enemy'},
    14:{type:'rest'},
    15:{type:'boss'}
  };
  for(const f in layout){ floors[+f] = layout[f]; }
  return { name: actNames[actNum-1], num: actNum, floors, actName: actNames[actNum-1] };
}

// ---------- Combat state ----------
function startCombat(enemyDefs, opts){
  opts = opts||{};
  const c = {
    enemies: [], deck: [], draw:[], discard:[], exhaust:[],
    hand: [], player: null, energy:3, turn:0, ended:false,
    gold:0, isBoss:false, rewardApplied:false, playerUpgraded:[],
    extraGold:0
  };
  c.player = JSON.parse(JSON.stringify(Game.player));
  c.player.deck = Game.player.deck.map(card=>makeCard(card.id, card.upgradeCard?true:card.upgraded));
  // Actually rebuild from id preserving upgrade:
  c.player.deck = Game.player.deck.map(card => {
    const upg = card.upgradeCard ? true : card.upgraded;
    return makeCard(card.id, upg);
  });
  c.isBoss = !!opts.boss;
  // Place enemies
  const list = Array.isArray(enemyDefs) ? enemyDefs : [enemyDefs];
  list.forEach((def,idx)=>{
    if(typeof def==='string') def = ENEMY_DEFS[def];
    const e = makeEnemy(def, idx);
    if(opts.boss){ e.phase1=def.phase1; e.phase2=def.phase2; }
    c.enemies.push(e);
  });
  // Relic start-of-combat effects
  applyRelicStartOfCombat(c);
  // Shuffle draw
  c.draw = shuffle(c.player.deck.slice());
  c.discard = []; c.exhaust = [];
  c.energy = 3;
  c.turn = 1;
  if(Game.nextCombatEnergy>0){ c.energy += Game.nextCombatEnergy; Game.nextCombatEnergy=0; }
  Game.combat = c;
  Game.player = c.player; // keep reference
  renderCombatInit();
}

function applyRelicStartOfCombat(c){
  const rel = c.player.relics;
  if(rel.includes('demonHorns') && Game.player.classId==='hell'){
    // add random Imp Claw to hand (done at player turn start instead since hand not drawn yet)
    c.demonHornsUsed = false;
  }
  if(rel.includes('shadowCloak')) c.player.addBuff('intangible',1,1);
  if(rel.includes('cursedAmulet')) c.enemies.forEach(e=>{ if(e.hp>0) e.statuses.weak = (e.statuses.weak||0)+1; });
  if(rel.includes('bossBlackBlood')){
    const loss = Math.ceil(c.player.maxHp*0.1);
    c.player.hp -= loss;
  }
  if(rel.includes('bossEctoplasm')){ c.player.noGold=true; c.player.gold=0; c.player.goldGain=false; }
  if(rel.includes('bossPhilosopher')) c.enemies.forEach(e=>{ e.strength = (e.strength||0)+1; });
  if(rel.includes('vialShadows')){ /* handled at pickup */ }
  if(rel.includes('demonHorns') && Game.player.classId==='hell'){
    c.pendingImpClaw = true;
  }
}

// ---------- Combat actions ----------
function combatCtx(){
  const c = Game.combat;
  return {
    enemies:c.enemies, player:c.player, energy:c.energy, turn:c.turn,
    target:c.selectedEnemy
  };
}

// ---------- Damage / healing ----------
function targetOrSelf(){ const c=Game.combat; return c.selectedEnemy && c.selectedEnemy.hp>0 ? c.selectedEnemy : c.enemies.find(e=>e.hp>0); }

function applyBurnDamage(c, enemy){
  // Soul Ember: +2 dmg when burn applied handled inline; here just apply burn tick
  const stacks = enemy.statuses.burn||0;
  if(stacks>0){
    const dmg = stacks + (c.player.relics.includes('soulEmber')?0:0);
    dealDamageToEnemy(enemy, dmg, c.player, true);
    if(enemy.hp>0){ log(`${enemy.name} takes ${dmg} Burn.`); }
  }
}

function dealDamageToEnemy(enemy, amount, attacker, noIntangible){
  // Intangible (enemy only from... actually only player has intangible per DDD)
  let dmg = amount;
  if(!noIntangible && enemy.statuses.intangible && enemy.statuses.intangible>0){
    dmg = 1;
    enemy.statuses.intangible--;
    if(enemy.statuses.intangible<=0) delete enemy.statuses.intangible;
    log(`${enemy.name} is Intangible — damage reduced to 1.`);
  }
  if(enemy.statuses.vulnerable && enemy.statuses.vulnerable>0){
    dmg = Math.floor(dmg*1.5);
  }
  // Soul Ember extra damage when burn applied (checked at apply time)
  enemy.hp -= dmg;
  if(enemy.hp<=0){ enemy.hp=0; enemy.alive=false; }
  return dmg;
}

function ctxDamageToEnemy(enemy, amount){
  const c = Game.combat;
  const dmg = dealDamageToEnemy(enemy, amount, c.player, false);
  animateHit(enemy, dmg);
  return dmg;
}

// Player takes damage
function playerTakeDamage(amount, fromCard){
  const c = Game.combat;
  let dmg = amount;
  // Intangible
  if(c.player.statuses.intangible && c.player.statuses.intangible>0){
    dmg = 1;
    c.player.statuses.intangible--;
    if(c.player.statuses.intangible<=0) delete c.player.statuses.intangible;
    log('Intangible reduces damage to 1!');
    floatText(document.getElementById('playerArt'), '1','intangible');
  }
  // Block
  if(c.player.block>0){
    const absorbed = Math.min(c.player.block, dmg);
    c.player.block -= absorbed;
    dmg -= absorbed;
  }
  if(dmg>0){
    c.player.hp -= dmg;
    floatText(document.getElementById('playerArt'), '-'+dmg,'dmg');
  }
  if(c.player.hp<=0){ c.player.hp=0; }
  // Bloodstone / Blood Pact on HP loss from card
  if(fromCard){
    if(c.player.relics.includes('bloodstone')){ c.player.hp = Math.min(c.player.maxHp, c.player.hp+1); }
    if(c.player.relics.includes('blackBlood')){ c.player.hp = Math.min(c.player.maxHp, c.player.hp+1); }
    if(c.player.relics.includes('bloodPact') || c.player.deck.some(cd=>cd.id==='bloodPact' && cd.upgradeCard)){
      // gain strength from blood pact power
    }
  }
  renderPlayer();
}

// Player heal with intangible bypass (heal not affected by intangible)
function playerHeal(amount){
  const c = Game.combat;
  c.player.hp = Math.min(c.player.maxHp, c.player.hp+amount);
  floatText(document.getElementById('playerArt'), '+'+amount,'heal');
  renderPlayer();
}

// Enemy takes damage helper (used by card funcs)
function ctx_damage(target, amount, player){
  const c = Game.combat;
  // Weak on target reduces damage
  if(target.statuses.weak && target.statuses.weak>0){
    amount = Math.floor(amount*0.75);
  }
  const dmg = dealDamageToEnemy(target, amount, player, false);
  animateHit(target, dmg);
  return dmg;
}

function ctx_damageAll(amount, player){
  const c = Game.combat;
  const alive = c.enemies.filter(e=>e.hp>0);
  alive.forEach(e=>{
    let d = amount;
    if(e.statuses.weak) d = Math.floor(d*0.75);
    const dmg = dealDamageToEnemy(e, d, player, false);
    animateHit(e, dmg);
  });
}

function ctx_applyToEnemy(enemy, status, amount){
  const c = Game.combat;
  if(enemy.hp<=0) return;
  enemy.statuses[status] = (enemy.statuses[status]||0) + amount;
  // Soul Ember: +2 when burn applied
  if(status==='burn' && c.player.relics.includes('soulEmber')){
    dealDamageToEnemy(enemy, 2, c.player, true);
    animateHit(enemy, 2);
  }
  // Bleed/Burn on enemy only; if applied to player it's fine too
  log(`Applied ${status} ${amount} to ${enemy.name}.`);
}

function ctx_drawCards(n){
  const c = Game.combat;
  for(let i=0;i<n;i++){
    if(c.draw.length===0){
      if(c.discard.length===0) break;
      c.draw = shuffle(c.discard);
      c.discard = [];
    }
    if(c.draw.length===0) break;
    const card = c.draw.pop();
    if(card.curse && card.id!=='doubt'){
      // Wound/Dazed: drawn, no effect, stays in hand (ethereal, exhaust when played/discarded)
      c.hand.push(card);
    } else {
      c.hand.push(card);
    }
  }
}

function ctx_shuffleDazed(place){
  const c = Game.combat;
  const dazed = makeCard('dazed');
  if(place==='draw') c.draw.push(dazed);
  else c.discard.push(dazed);
}

// ---------- Status tick helpers ----------
function startPlayerTurn(){
  const c = Game.combat;
  const p = c.player;
  // Intangible decrement
  if(p.statuses.intangible){ p.statuses.intangible--; if(p.statuses.intangible<=0) delete p.statuses.intangible; }
  // Energy from brimstone? no. pending energy
  // Regeneration
  if(p.statuses.regen){ playerHeal(p.statuses.regen); p.statuses.regen--; if(p.statuses.regen<=0) delete p.statuses.regen; }
  // Energy next turn
  if(p.pendingEnergy){ c.energy += p.pendingEnergy; p.pendingEnergy=0; }
  // Pending block (phantom armor / whispering shroud)
  if(p.pendingBlock){ p.block += p.pendingBlock; p.pendingBlock=0; }
  if(p.pendingDex){ p.addBuff('dex', p.pendingDex, 1); p.pendingDex=0; }
  // Relic start-of-turn
  if(p.relics.includes('eternalHourglass') && c.hand.length===0){ c.energy+=1; }
  if(p.relics.includes('bleedingEdge')){ const e=c.enemies.find(e=>e.hp>0); if(e) ctx_applyToEnemy(e,'bleed',1); }
  if(p.relics.includes('bossCursedCrown')){ c.energy+=1; }
  // Soul Siphon power
  if(p.deck.some(cd=>cd.id==='soulSiphon')){ c.energy+=1; p.hp=Math.max(1,p.hp-1); renderPlayer(); }
  // Hellfire Aura power
  if(p.deck.some(cd=>cd.id==='hellfireAura')){ const dmg = p.upgradeCard && p.deck.find(cd=>cd.id==='hellfireAura')&&p.deck.find(cd=>cd.id==='hellfireAura').upgradeCard ? 5:3; c.enemies.filter(e=>e.hp>0).forEach(e=>{ const dd=dealDamageToEnemy(e,dmg,p,false); animateHit(e,dd); }); }
  // Umbral Mastery power
  if(p.deck.some(cd=>cd.id==='umbralMastery')){ const n = p.deck.find(cd=>cd.id==='umbralMastery')&&p.deck.find(cd=>cd.id==='umbralMastery').upgradeCard?2:1; c.enemies.filter(e=>e.hp>0).forEach(e=>{ e.statuses.weak=(e.statuses.weak||0)+n; }); }
  // Blood Pact: strength already applied on HP loss
  // Dazed in hand -> unplayable
  // End of Energy Orb display
  renderEnergy();
}

function endPlayerTurn(){
  const c = Game.combat;
  const p = c.player;
  // Doubt curse in hand -> lose 1 HP
  c.hand.forEach(cd=>{ if(cd.id==='doubt'){ playerTakeDamage(1,false); } });
  // Burn tick for player
  if(p.statuses.burn){
    const s = p.statuses.burn;
    playerTakeDamage(s,false);
    p.statuses.burn = Math.floor(s/2);
    if(p.statuses.burn<=0) delete p.statuses.burn;
  }
  // Discard hand
  c.discard.push(...c.hand);
  c.hand = [];
  // Block resets handled next turn start (block=0)
  renderPlayer();
}

function startEnemyTurn(enemy){
  const c = Game.combat;
  // Bleed tick
  if(enemy.statuses.bleed){
    const s = enemy.statuses.bleed;
    const dmg = dealDamageToEnemy(enemy, s, c.player, true);
    animateHit(enemy, dmg);
    enemy.statuses.bleed = Math.floor(s/2);
    if(enemy.statuses.bleed<=0) delete enemy.statuses.bleed;
  }
  // Strength loss temp
  if(enemy._tempStrengthLoss){ enemy.strength = Math.max(0,(enemy.strength||0)-enemy._tempStrengthLoss); enemy._tempStrengthLoss=0; }
  // Weak decrement
  if(enemy.statuses.weak){ enemy.statuses.weak--; if(enemy.statuses.weak<=0) delete enemy.statuses.weak; }
  if(enemy.statuses.vulnerable){ enemy.statuses.vulnerable--; if(enemy.statuses.vulnerable<=0) delete enemy.statuses.vulnerable; }
}

function executeEnemyIntent(enemy, intent){
  const c = Game.combat;
  switch(intent.type){
    case 'attack':
      enemyApplyDamage(enemy, intent.dmg, enemy.atk||0);
      break;
    case 'multi':
      for(let i=0;i<intent.n;i++){ if(c.enemies.every(e=>e.hp<=0)) break; enemyApplyDamage(enemy, intent.dmg, 0); }
      break;
    case 'defend':
      enemy.block += (intent.x||0);
      log(`${enemy.name} gains ${intent.x} Block.`);
      break;
    case 'buff_strength':
      enemy.strength = (enemy.strength||0)+(intent.x||0);
      log(`${enemy.name} gains ${intent.x} Strength.`);
      break;
    case 'buff':
      if(intent.x && intent.x.strength) enemy.strength=(enemy.strength||0)+intent.x.strength;
      if(intent.x && intent.x.block) enemy.block+=(intent.x.block);
      if(intent.x && intent.x.regen){ enemy.statuses.regen=(enemy.statuses.regen||0)+intent.x.regen; }
      log(`${enemy.name} buffs.`);
      break;
    case 'apply_weak':
      if(c.player.hp>0){ c.player.statuses.weak=(c.player.statuses.weak||0)+(intent.x||1); }
      break;
    case 'apply_vulnerable':
      if(c.player.hp>0){ c.player.statuses.vulnerable=(c.player.statuses.vulnerable||0)+(intent.x||1); }
      break;
    case 'apply_bleed':
      if(c.player.hp>0){ c.player.statuses.bleed=(c.player.statuses.bleed||0)+(intent.x||1); }
      break;
    case 'apply_burn':
      if(c.player.hp>0){ c.player.statuses.burn=(c.player.statuses.burn||0)+(intent.x||1); }
      break;
    case 'poison':
      if(c.player.hp>0){ c.player.statuses.bleed=(c.player.statuses.bleed||0)+(intent.x||0); c.player.statuses.burn=(c.player.statuses.burn||0)+(intent.x||0); c.player.statuses.poison=(c.player.statuses.poison||0)+(intent.poison||0); enemyApplyDamage(enemy, intent.atk,0); }
      break;
    case 'drain':
      enemyApplyDamage(enemy, intent.atk, 0);
      enemy.hp = Math.min(enemy.maxHp, enemy.hp + intent.heal);
      log(`${enemy.name} drains ${intent.heal} HP.`);
      break;
    case 'summon':
      summonMinion(enemy, intent.monk);
      break;
    case 'buff_self':
      if(intent.block) enemy.block+=(intent.block);
      if(intent.regen) enemy.statuses.regen=(enemy.statuses.regen||0)+intent.regen;
      log(`${enemy.name} buffs itself.`);
      break;
    case 'buff_all':
      c.enemies.filter(e=>e.hp>0).forEach(e=>{ e.strength=(e.strength||0)+(intent.str||0); });
      log(`${enemy.name} buffs all enemies.`);
      break;
    case 'type2':
      break;
  }
  enemy._tempStrengthLoss = 0;
}

function enemyApplyDamage(enemy, dmg, strFromIntent){
  const c = Game.combat;
  let total = dmg + (enemy.strength||0);
  // Vulnerable on enemy
  if(enemy.statuses.vulnerable){ total = Math.floor(total*1.5); }
  // Block absorb
  if(enemy.block>0){ const ab=Math.min(enemy.block,total); enemy.block-=ab; total-=ab; }
  if(total>0){ enemy.hp -= total; if(enemy.hp<=0){ enemy.hp=0; enemy.alive=false; } }
  animateHit(enemy, dmg);
  log(`${enemy.name} attacks for ${dmg}.`);
}

function summonMinion(source, kind){
  const c = Game.combat;
  const minions = c.enemies.filter(e=>e.isMinion && e.hp>0).length;
  if(minions>=2){ log('Already 2 minions; summon skipped.'); return; }
  const def = minionDef(kind);
  const e = makeEnemy(def, c.enemies.length);
  c.enemies.push(e);
  log(`A ${def.name} is summoned!`);
  renderEnemies();
}

// ============================================================
// RENDERING & UI
// ============================================================
const el = id => document.getElementById(id);
function showScreen(id){ document.querySelectorAll('.screen').forEach(s=>s.classList.remove('active')); el(id).classList.add('active'); }

function log(msg){
  const l = el('log');
  const d = document.createElement('div');
  d.textContent = msg;
  l.appendChild(d);
  while(l.children.length>6) l.removeChild(l.firstChild);
}

function floatText(anchor, text, cls){
  const parent = anchor.parentElement;
  const f = document.createElement('div');
  f.className='floatText '+cls;
  f.textContent = text;
  f.style.left = (parent.offsetWidth/2 - 30 + rand(-30,30))+'px';
  f.style.top = (parent.offsetHeight/2)+'px';
  parent.appendChild(f);
  setTimeout(()=>f.remove(),1000);
}

function animateHit(enemy, dmg){
  const art = enemy.el && enemy.el.querySelector('.enemyArt');
  if(art){ art.parentElement.classList.remove('hit'); void art.offsetWidth; art.parentElement.classList.add('hit'); }
}

// ---------- Class select ----------
function renderClassSelect(){
  const grid = el('classGrid');
  grid.innerHTML='';
  Object.entries(CLASSES).forEach(([id,c])=>{
    const d = document.createElement('div');
    d.className='classCard';
    d.innerHTML = `<div class="classIcon">${c.img}</div><h3>${c.name}</h3><div class="desc">${c.desc}</div>
      <div class="start">Start HP 70 · 3 Energy · 5 Draws</div>
      <div class="sig">Signature: ${c.sig}</div>`;
    d.onclick = ()=>startRun(id);
    grid.appendChild(d);
  });
}

function startRun(classId){
  Game.player = newPlayer(classId);
  Game.currentAct = 1;
  Game.map = buildMap(1);
  Game.nodeIndex = 1;
  renderMap();
  showScreen('mapScreen');
}

// ---------- Map ----------
function nodeIcon(type){
  return {enemy:'⚔️',elite:'💀',rest:'🏕️',shop:'🏪',event:'❓',boss:'☠️'}[type];
}
function nodeLabel(type){
  return {enemy:'Enemy',elite:'Elite',rest:'Rest',shop:'Shop',event:'Event',boss:'Boss'}[type];
}

function renderMap(){
  el('hudAct').textContent = `Act ${Game.currentAct}: ${Game.map.actName}`;
  el('hudClass').textContent = Game.player.className + ' ' + Game.player.classImg;
  el('hudGold').textContent = Game.player.gold;
  el('hudHp').textContent = Game.player.hp;
  el('hudMaxHp').textContent = Game.player.maxHp;

  const container = el('mapNodes');
  container.innerHTML='';
  const floors = Game.map.floors;
  // Floor 1 start
  const startRow = document.createElement('div');
  startRow.className='mapRow';
  startRow.innerHTML = `<div class="mapNode current" style="cursor:default">🏕️<span class="floorNum">Start</span></div>`;
  container.appendChild(startRow);

  for(let f=2; f<=15; f++){
    const fl = floors[f];
    if(!fl) continue;
    const row = document.createElement('div');
    row.className='mapRow';
    const type = fl.branch ? 'enemy' : fl.type;
    // Determine node(s)
    const nodes = [];
    if(fl.branch){
      nodes.push({type:'enemy', label:nodeLabel('enemy')});
      nodes.push({type:'enemy', label:nodeLabel('enemy')});
    } else if(fl.alt){
      // elite branch + enemy alt
      nodes.push({type:fl.type, label:nodeLabel(fl.type)});
      nodes.push({type:fl.alt, label:nodeLabel(fl.alt)});
    } else {
      nodes.push({type:fl.type, label:nodeLabel(fl.type)});
    }
    // Special: floor 15 is boss single
    if(f===15){ nodes.length=0; nodes.push({type:'boss',label:'Boss'}); }

    nodes.forEach((n,i)=>{
      const node = document.createElement('div');
      const isCurrent = (f===Game.nodeIndex);
      node.className='mapNode '+n.type + (isCurrent?' current':'') + (f<Game.nodeIndex?' cleared':'') + (f>Game.nodeIndex?' locked':'');
      node.innerHTML = `${nodeIcon(n.type)}<span class="floorNum">${f}</span>`;
      if(isCurrent){
        node.classList.add('available');
        node.onclick = ()=>enterNode(f, n.type);
      }
      row.appendChild(node);
    });
    container.appendChild(row);
  }
  // Scroll current into view
  setTimeout(()=>{ const cur=document.querySelector('.mapNode.current'); if(cur) cur.scrollIntoView({behavior:'smooth',block:'center'}); },100);
}

function enterNode(floor, type){
  if(floor>Game.nodeIndex) return;
  const floors = Game.map.floors;
  const fl = floors[floor];
  if(type==='enemy'){
    const enemyGroup = getEnemyGroup(Game.currentAct, floor);
    startCombat(enemyGroup, {boss:false});
  } else if(type==='elite'){
    const elite = getElite(Game.currentAct);
    startCombat(elite, {boss:false});
  } else if(type==='boss'){
    const boss = getBoss(Game.currentAct);
    startCombat(boss, {boss:true});
  } else if(type==='rest'){
    showRest();
  } else if(type==='shop'){
    showShop();
  } else if(type==='event'){
    showEvent();
  }
}

function getEnemyGroup(act, floor){
  const pools = {
    1:{skeleton:'skeleton',zombie:'zombie',cultist:'cultist',rat:'rat'},
    2:{wraith:'wraith',beast:'beast',thorned:'thorned',witchDoc:'witchDoc'},
    3:{hellhound:'hellhound',shadowDemo:'shadowDemo',infernalImp:'infernalImp',abyssCultist:'abyssCultist'}
  };
  const p = pools[act];
  switch(floor){
    case 2: return Math.random()<0.5 ? [p.skeleton] : [p.zombie];
    case 3: return [p.cultist];
    case 6: return [p.rat];
    case 8: return Math.random()<0.5 ? [p.skeleton,p.skeleton] : [p.zombie];
    case 11: return [p.cultist, p.rat];
    case 13: return [p.cultist, p.cultist];
    default: return [pick(Object.values(p))];
  }
}
function getElite(act){
  return {1:'tombGuardian',2:'treant',3:'flamespawn'}[act];
}
function getBoss(act){
  return {1:'lichKing',2:'eldritchTreant',3:'archdemon'}[act];
}

// ---------- Combat rendering ----------
function renderCombatInit(){
  const c = Game.combat;
  el('combatAct').textContent = `Act ${Game.currentAct}: ${Game.map.actName}`;
  el('combatClass').textContent = Game.player.className+' '+Game.player.classImg;
  el('combatGold').textContent = Game.player.gold;
  showScreen('combatScreen');
  renderEnemies();
  renderPlayer();
  renderPotions();
  // Draw initial 5
  c.hand = [];
  ctx_drawCards(5);
  startPlayerTurn();
  renderHand();
  renderEnergy();
}

function renderEnemies(){
  const row = el('enemyRow');
  row.innerHTML='';
  const c = Game.combat;
  c.enemies.forEach(enemy=>{
    const d = document.createElement('div');
    d.className='enemy' + (enemy.hp<=0?' dead':'');
    if(enemy.hp>0) d.onclick = ()=>selectEnemy(enemy, d);
    d.el = d;
    enemy.el = d;
    const hpPct = Math.max(0,(enemy.hp/enemy.maxHp)*100);
    const statusHtml = Object.entries(enemy.statuses).map(([k,v])=>`<span class="pip">${k==='weak'?'🗡️':k==='vulnerable'?'🔴':k==='bleed'?'🩸':k==='burn'?'🔥':k==='regen'?'💚':k==='intangible'?'👤':k}</span>`).join('');
    let intentHtml='';
    if(enemy.hp>0 && c.player.hp>0 && c.playerTurn){
      intentHtml = renderIntent(enemy);
    }
    d.innerHTML = `
      ${enemy.block>0?`<div class="blockBadge">🛡️${enemy.block}</div>`:''}
      <div class="intentBox">${intentHtml}</div>
      <div class="enemyArt">${enemy.img}</div>
      <div class="enemyName">${enemy.name}</div>
      <div class="hpBar"><div class="hpFill" style="width:${hpPct}%"></div></div>
      <div class="enemyStats">${enemy.hp}/${enemy.maxHp} ${enemy.strength?('💪'+enemy.strength):''}</div>
      <div class="statusPips">${statusHtml}</div>
    `;
    row.appendChild(d);
  });
}

function renderIntent(enemy){
  const t = enemy._turn % enemy.pattern.length;
  const intent = enemy.pattern[t];
  let txt='';
  if(enemy.boss){
    const phase = enemy.hp<=enemy.maxHp*0.45 ? 1 : 0;
    const cyc = enemy.hp<=enemy.maxHp*0.45 ? enemy.phase2 : enemy.phase1;
    const ti = enemy._turn % cyc.length;
    return intentText(cyc[ti]);
  }
  return intentText(intent);
}

function intentText(intent){
  switch(intent.type){
    case 'attack': return `⚔️ ${intent.dmg+(intent.atk||0)}`;
    case 'multi': return `⚔️ ${intent.n}×${intent.dmg}`;
    case 'defend': return `🛡️ ${intent.x}`;
    case 'buff_strength': return `💪 ${intent.x}`;
    case 'apply_weak': return `🗡️${intent.x} Weak`;
    case 'apply_vulnerable': return `🔴${intent.x} Vuln`;
    case 'apply_bleed': return `🩸${intent.x} Bleed`;
    case 'apply_burn': return `🔥${intent.x} Burn`;
    case 'summon': return `✨ Summon`;
    case 'drain': return `🩸 ${intent.atk}+${intent.heal}`;
    case 'poison': return `☠️ ${intent.atk}`;
    default: return '❓';
  }
}

function selectEnemy(enemy, d){
  const c = Game.combat;
  if(!c.playerTurn || c.ended) return;
  c.selectedEnemy = enemy;
  document.querySelectorAll('.enemy').forEach(e=>e.classList.remove('targetable'));
  d.classList.add('targetable');
}

function renderPlayer(){
  const c = Game.combat;
  const p = c.player;
  const hpPct = Math.max(0,(p.hp/p.maxHp)*100);
  el('hpFill').style.width = hpPct+'%';
  el('playerHpText').textContent = `${p.hp}/${p.maxHp}`;
  const b = el('playerBlock');
  if(p.block>0){ b.style.display='block'; b.textContent='🛡️'+p.block; } else b.style.display='none';
  const buffs = Object.entries(p.statuses).map(([k,v])=>`<span class="buff ${k}">${k} ${v}</span>`).join('');
  el('playerBuffs').innerHTML = buffs;
  el('drawCount').textContent = c.draw.length;
  el('discardCount').textContent = c.discard.length;
  renderPotions();
}

function renderEnergy(){
  const c = Game.combat;
  el('energyOrb').textContent = `⚡${c.energy}/${c.maxEnergy||3}`;
  el('endTurnBtn').disabled = !c.playerTurn;
}

function renderHand(){
  const hand = el('hand');
  hand.innerHTML='';
  const c = Game.combat;
  c.hand.forEach((card, idx)=>{
    const d = document.createElement('div');
    const playable = card.playable && c.playerTurn && !c.ended && c.energy>=card.cost;
    d.className = 'card '+card.rarity.toLowerCase() + (card.ethereal?' ethereal':'') + (card.curse?' curse':'') + (playable?'':' unplayable');
    const desc = card.upgraded ? card.upg : card.desc;
    d.innerHTML = `
      <div class="cost">${card.cost}</div>
      <div class="cname">${card.name}${card.upgradeCard?'+':''}</div>
      <div class="ctype">${card.type}${card.rarity!=='Basic'?' · '+card.rarity:''}</div>
      <div class="cimg">${card.img}</div>
      <div class="cdesc">${desc}</div>
      <div class="cbar"></div>
      ${card.exhaust?'<div class="exhaustTag">Exhaust</div>':''}
    `;
    if(playable){
      d.onclick = ()=>playCard(idx);
    }
    d._idx = idx;
    hand.appendChild(d);
  });
}

function renderPotions(){
  const bar = el('potionBar');
  bar.innerHTML='';
  const c = Game.combat;
  c.player.potions.forEach((pid, idx)=>{
    const p = POTIONS[pid];
    const d = document.createElement('div');
    d.className='potion';
    d.textContent = p.img;
    d.title = p.name;
    d.onclick = ()=>usePotion(idx);
    bar.appendChild(d);
  });
  el('combatPotions').textContent = c.player.potions.length>0 ? c.player.potions.map(p=>POTIONS[p].img).join(' ') : '';
}

// ---------- Play card ----------
function playCard(idx){
  const c = Game.combat;
  const card = c.hand[idx];
  if(!card || !card.playable || c.ended) return;
  if(c.energy < card.cost) return;
  if(!c.selectedEnemy && !cardNoTarget(card)) return;

  c.energy -= card.cost;
  // Blood Reaper: sacrificial dagger reduces first HP-costing attack cost
  c.hand.splice(idx,1);
  const ctx = combatCtx();
  // Soul Siphon: start-of-turn energy handled separately; here Soul Siphon doesn't trigger on play

  // Play the card
  try{ card.func(ctx); }
  catch(e){ console.error('Card func error', e); }

  // Tome of Dark Rituals: play a Power card -> draw 1
  if(c.player.relics.includes('tomeDark') && card.type==='Power'){ c.hand.length < 10 && ctx_drawCards(1); }

  // Blood Pact: if HP lost from card, gain strength
  if(c.player.deck.some(cd=>cd.id==='bloodPact')){ /* strength applied via relic on HP loss */ }

  renderEnergy();
  renderPlayer();
  renderEnemies();
  renderHand();

  // Exhaust handling
  if(card.exhaust){ c.exhaust.push(card); }
  else c.discard.push(card);

  // Check enemy deaths
  checkCombatEnd();
  renderHand();
}

function cardNoTarget(card){
  // Cards that don't need a target
  return ['Skill','Power'].includes(card.type) || card.type==='Status';
}

// ---------- Potions ----------
function usePotion(idx){
  const c = Game.combat;
  if(!c.playerTurn || c.ended) return;
  const pid = c.player.potions[idx];
  const p = POTIONS[pid];
  c.player.potions.splice(idx,1);
  switch(p.effect){
    case 'heal15': playerHeal(15); break;
    case 'firebomb': c.enemies.filter(e=>e.hp>0).forEach(e=>{ const d=dealDamageToEnemy(e,10,c.player,false); animateHit(e,d); }); renderEnemies(); break;
    case 'dex': c.player.addBuff('dex',3,1); break;
    case 'energy': c.energy += 2; break;
    case 'cursedBrew': c.enemies.filter(e=>e.hp>0).forEach(e=>{ e.statuses.vulnerable=(e.statuses.vulnerable||0)+3; }); break;
    case 'bleeding': { const e=pick(c.enemies.filter(x=>x.hp>0)); if(e) e.statuses.bleed=(e.statuses.bleed||0)+6; } break;
    case 'impInBottle': c.hand.push(makeImpClaw(true)); break;
    case 'smokeBomb': { if(!c.isBoss){ endCombatEscape(); return; } else { log('Cannot escape a boss!'); c.player.potions.push(pid); return; } }
  }
  renderPlayer();
  renderPotions();
  renderEnergy();
  renderEnemies();
}

// ---------- Turn processing ----------
el('endTurnBtn').addEventListener('click', endPlayerTurnAction);
function endPlayerTurnAction(){
  const c = Game.combat;
  if(!c.playerTurn || c.ended) return;
  c.playerTurn = false;
  c.selectedEnemy = null;
  endPlayerTurn();
  renderHand();
  renderPlayer();
  el('endTurnBtn').disabled = true;
  setTimeout(processEnemyTurns, 600);
}

async function processEnemyTurns(){
  const c = Game.combat;
  for(const enemy of c.enemies){
    if(enemy.hp<=0) continue;
    startEnemyTurn(enemy);
    if(c.player.hp<=0){ return handlePlayerDeath(); }
    enemy.intent = enemy.boss ? bossIntent(enemy) : enemyIntent(enemy);
    executeEnemyIntent(enemy, enemy.intent);
    renderEnemies();
    renderPlayer();
    if(c.player.hp<=0){ return handlePlayerDeath(); }
    await sleep(500);
  }
  if(c.player.hp<=0){ return handlePlayerDeath(); }
  // Next player turn
  c.turn++;
  c.playerTurn = true;
  c.energy = 3;
  c.selectedEnemy = null;
  // Block reset
  c.player.block = 0;
  // Draw 5
  c.hand = [];
  startPlayerTurn();
  ctx_drawCards(5);
  renderHand();
  renderEnergy();
  renderPlayer();
  el('endTurnBtn').disabled = false;
}

function bossIntent(enemy){
  const phase = enemy.hp<=enemy.maxHp*0.45 ? enemy.phase2 : enemy.phase1;
  const t = enemy._turn % phase.length;
  enemy._turn++;
  return phase[t];
}

function sleep(ms){ return new Promise(r=>setTimeout(r,ms)); }

function handlePlayerDeath(){
  const c = Game.combat;
  c.ended = true;
  c.player.hp = 0;
  renderPlayer();
  showOverlay(`<div class="bigText">YOU DIED</div>
    <div class="msgText">The Abyss claims another soul.</div>
    <div class="center-wrap">
      <button class="btn" onclick="gameOver()">Accept Defeat</button>
    </div>`);
}

function checkCombatEnd(){
  const c = Game.combat;
  if(c.enemies.every(e=>e.hp<=0)){
    // Boss phase transitions handled mid-fight; if all dead, end combat
    setTimeout(()=>winCombat(), 400);
  }
}

function endCombatEscape(){
  const c = Game.combat;
  c.ended = true;
  Game.nodeIndex++;
  hideOverlay();
  renderMap();
  log('You escaped with the Smoke Bomb.');
}

// ---------- Win combat ----------
function winCombat(){
  const c = Game.combat;
  c.ended = true;
  const gold = computeGold();
  c.gold = gold;
  // Apply gold
  if(!c.player.goldGain===false){ /* default */ }
  if(!c.player.noGold){
    c.player.gold += gold;
    Game.player.gold = c.player.gold;
  }
  // Ashen Armor heal
  if(c.player.relics.includes('ashenArmor')){ c.player.hp = Math.min(c.player.maxHp, c.player.hp+6); }
  if(c.isBoss){
    // Full heal + boss rewards then advance
    showBossRewards();
  } else {
    showRewards();
  }
}

function computeGold(){
  const c = Game.combat;
  let g = 0;
  if(c.isBoss) g = 50;
  else if(c.enemies.some(e=>e.boss)) g = 50;
  else g = rand(10,20);
  if(c.player.relics.includes('greedyImp')) g += 10;
  return g;
}

// ---------- Rewards ----------
function showRewards(){
  const c = Game.combat;
  let html = `<h2>Victory!</h2>
    <div class="rewardGold">+${c.gold} Gold</div>
    <div class="rewardCards" id="rewardCards"></div>
    <div id="rewardExtras"></div>
    <div class="center-wrap">
      <button class="btn rewardSkip" onclick="skipReward()">Skip</button>
    </div>`;
  showOverlay(html);
  // Card reward: 3 cards weighted by rarity
  const choices = pickCardChoices();
  const rc = el('rewardCards');
  choices.forEach((cardId, i)=>{
    const card = makeCard(cardId, false);
    const d = createRewardCard(card);
    d.onclick = ()=>takeRewardCard(card);
    rc.appendChild(d);
  });
  // Potion reward
  let extras = `<div class="rewardGold">${c.gold} Gold</div>`;
  const normalDrop = c.enemies.some(e=>e.statuses && false) ? false : true;
  // Potion chance 40%
  if(Math.random()<0.4 && c.player.potions.length < c.player.potionSlots){
    const pot = pick(POTION_IDS);
    extras += `<div class="rewardPotion">${POTIONS[pot].img} ${POTIONS[pot].name}</div>`;
    c.player.potions.push(pot);
  }
  el('rewardExtras').innerHTML = extras;
}

function pickCardChoices(){
  const pool = Game.player.classPool || CLASS_POOLS[Game.player.classId];
  const rare = Game.map.floors[Game.nodeIndex] && Game.map.floors[Game.nodeIndex].type==='elite' ? 0.20 : 0.10;
  const choices = [];
  for(let i=0;i<3;i++){
    const r = Math.random();
    let rarity;
    if(r < rare) rarity='Rare';
    else if(r < rare+0.30) rarity='Uncommon';
    else rarity='Common';
    const candidates = pool.filter(id=>CARDS[id] && CARDS[id].rarity===rarity && CARDS[id].type!=='Basic');
    if(candidates.length===0){
      const fallback = pool.filter(id=>CARDS[id] && CARDS[id].type!=='Basic');
      choices.push(pick(fallback));
    } else choices.push(pick(candidates));
  }
  return choices;
}

function createRewardCard(card){
  const d = document.createElement('div');
  d.className='card '+card.rarity.toLowerCase();
  const desc = card.upgraded ? card.upg : card.desc;
  d.innerHTML = `<div class="cost">${card.cost}</div>
    <div class="cname">${card.name}</div>
    <div class="ctype">${card.type}${card.rarity!=='Basic'?' · '+card.rarity:''}</div>
    <div class="cimg">${card.img}</div>
    <div class="cdesc">${desc}</div>
    <div class="cbar"></div>`;
  return d;
}

function takeRewardCard(card){
  const c = Game.combat;
  c.player.deck.push(card);
  Game.player.deck = c.player.deck;
  hideOverlay();
  advanceNode();
}
function skipReward(){ hideOverlay(); advanceNode(); }

// ---------- Boss rewards ----------
function showBossRewards(){
  const c = Game.combat;
  // Choose 1 of 3 boss relics
  const relics = shuffle(BOSS_RELIC_IDS).slice(0,3);
  let html = `<h2>${Game.map.actName} Cleared!</h2>
    <div class="msgText">The ${c.enemies[0].name} is defeated. You are fully restored.</div>
    <div class="rewardGold">+50 Gold</div>
    <div class="rewardCards" id="bossRelicCards"></div>
    <div class="center-wrap"><button class="btn rewardSkip" onclick="skipBossReward()">Continue</button></div>`;
  showOverlay(html);
  const rc = el('bossRelicCards');
  relics.forEach(id=>{
    const rel = RELICS[id];
    const d = document.createElement('div');
    d.className='card rare';
    d.innerHTML = `<div class="cost">★</div><div class="cname">${rel.name}</div>
      <div class="cimg">${rel.img}</div><div class="cdesc">${rel.desc}</div><div class="cbar"></div>`;
    d.onclick = ()=>takeBossRelic(id);
    rc.appendChild(d);
  });
  // Store relics for take
  el('overlayBox')._bossRelics = relics;
}

function takeBossRelic(id){
  const c = Game.combat;
  c.player.relics.push(id);
  Game.player.relics = c.player.relics;
  // Full heal after boss
  Game.player.hp = Game.player.maxHp;
  // Advance to next act if boss was act 3, else continue
  hideOverlay();
  advanceNode();
}
function skipBossReward(){
  // still give full heal
  Game.player.hp = Game.player.maxHp;
  hideOverlay();
  advanceNode();
}

// ---------- Advance node ----------
function advanceNode(){
  Game.nodeIndex++;
  // Check for act completion (after boss floor 15)
  if(Game.nodeIndex > 15){
    if(Game.currentAct >= 3){
      victoryScreen();
    } else {
      Game.currentAct++;
      Game.map = buildMap(Game.currentAct);
      Game.nodeIndex = 1;
      // Show act banner
      showBanner(`Act ${Game.currentAct}: ${Game.map.actName}`);
      renderMap();
    }
    return;
  }
  renderMap();
}

function showBanner(text){
  const b = document.createElement('div');
  b.className='banner';
  b.textContent = text;
  document.getElementById('mapScroll').appendChild(b);
  setTimeout(()=>b.remove(),1200);
}

function gameOver(){
  showOverlay(`<div class="bigText">RUN OVER</div>
    <div class="msgText">You reached Act ${Game.currentAct}. The Abyss grows stronger.</div>
    <div class="center-wrap"><button class="btn" onclick="restart()">Choose Another Fate</button></div>`);
}
function victoryScreen(){
  Game.player.hp = Game.player.maxHp;
  showOverlay(`<div class="bigText victory">VICTORY</div>
    <div class="msgText">You have defeated the Archdemon and broken the curse of the Abyss. The dying world stirs with new light. <br><br>Final Gold: ${Game.player.gold} · Relics: ${Game.player.relics.length} · Potions: ${Game.player.potions.length}</div>
    <div class="center-wrap"><button class="btn" onclick="restart()">Play Again</button></div>`);
}
function restart(){ showScreen('classSelect'); }

// ---------- Overlay ----------
function showOverlay(html){
  el('overlayBox').innerHTML = `<div class="overlayBox">${html}</div>`;
  el('overlay').classList.add('active');
}
function hideOverlay(){ el('overlay').classList.remove('active'); }

// ---------- Rest site ----------
function showRest(){
  const p = Game.player;
  const healAmt = Math.floor(p.maxHp*0.25);
  const html = `<h2>Rest Site</h2>
    <div class="msgText">A moment of safety in the Abyss. Choose one:</div>
    <div class="listChoices">
      <button class="btn" onclick="restAction('heal')">🏕️ Rest — Heal ${healAmt} HP (25% max)</button>
      <button class="btn" onclick="restAction('forge')">🔨 Forge — Upgrade a card</button>
      <button class="btn" onclick="restAction('meditate')">🧘 Meditate — Remove a card</button>
    </div>`;
  showOverlay(html);
}
function restAction(choice){
  const p = Game.player;
  if(choice==='heal'){
    const heal = Math.floor(p.maxHp*0.25);
    p.hp = Math.min(p.maxHp, p.hp+heal);
    hideOverlay(); advanceNode();
  } else if(choice==='forge'){
    upgradeFromDeck();
  } else if(choice==='meditate'){
    removeFromDeck();
  }
}
function upgradeFromDeck(){
  const p = Game.player;
  const upgradable = p.deck.filter(c=>!c.upgradeCard);
  if(upgradable.length===0){ showOverlay(`<h2>Forge</h2><div class="msgText">No cards to upgrade.</div><div class="center-wrap"><button class="btn" onclick="hideOverlay();renderMap();">Back</button></div>`); return; }
  const html = `<h2>Forge</h2><div class="msgText">Choose a card to upgrade:</div><div class="rewardCards" id="forgeCards"></div>`;
  showOverlay(html);
  const rc = el('forgeCards');
  upgradable.forEach(card=>{
    const d = createRewardCard(card);
    d.onclick = ()=>{ card.upgradeCard = {id:card.id+'+'}; card.upgraded = true; hideOverlay(); advanceNode(); };
    rc.appendChild(d);
  });
}
function removeFromDeck(){
  const p = Game.player;
  const removable = p.deck.filter(c=>!c.curse && !c.token);
  if(removable.length===0){ showOverlay(`<h2>Meditate</h2><div class="msgText">No cards to remove.</div><div class="center-wrap"><button class="btn" onclick="hideOverlay();renderMap();">Back</button></div>`); return; }
  const html = `<h2>Meditate</h2><div class="msgText">Choose a card to remove:</div><div class="rewardCards" id="medCards"></div>`;
  showOverlay(html);
  const rc = el('medCards');
  removable.forEach(card=>{
    const d = createRewardCard(card);
    d.onclick = ()=>{ p.deck = p.deck.filter(c=>c!==card); hideOverlay(); advanceNode(); };
    rc.appendChild(d);
  });
}

// ---------- Shop ----------
function showShop(){
  const p = Game.player;
  const pool = CLASS_POOLS[p.classId];
  const wares = [];
  // 3 class cards
  for(let i=0;i<3;i++){
    const rarity = pick(['Common','Common','Uncommon','Rare']);
    const candidates = pool.filter(id=>CARDS[id] && CARDS[id].rarity===rarity && CARDS[id].type!=='Basic');
    if(candidates.length){ wares.push({kind:'card', card:makeCard(pick(candidates), false), price:{Common:50,Uncommon:75,Rare:150}[rarity]}); }
  }
  // 1 colorless
  const colorless = ['flashOfInsight','ironWill','quickSlash','potentBrew'];
  wares.push({kind:'card', card:makeCard(pick(colorless), false), price:60});
  // 2 relics
  const relicIds = shuffle(Object.keys(RELICS)).filter(k=>RELICS[k].rarity!=='Boss').slice(0,2);
  relicIds.forEach(id=> wares.push({kind:'relic', id, price:150}));
  // 3 potions
  for(let i=0;i<3;i++){ wares.push({kind:'potion', potion:pick(POTION_IDS), price:50}); }

  renderShop(wares);
}
function renderShop(wares){
  const p = Game.player;
  let html = `<h2>Shop</h2>
    <div class="msgText">Gold: <span style="color:var(--gold)">${p.gold}◈</span></div>
    <div class="shopGrid" id="shopGrid"></div>
    <div class="listChoices">
      <button class="btn small" id="removeCardBtn" onclick="shopRemoveCard()">Remove 1 card from deck — 75g</button>
      <button class="btn small" onclick="leaveShop()">Leave Shop</button>
    </div>`;
  showOverlay(html);
  const grid = el('shopGrid');
  const saved = el('overlayBox');
  saved._shopWares = wares;
  wares.forEach((w, i)=>{
    const d = document.createElement('div');
    d.className='shopItem';
    d._idx = i;
    let inner='';
    if(w.kind==='card'){
      inner = `<div class="itemIcon">${w.card.img}</div><div>${w.card.name}</div>
        <div class="tooltip">${w.card.type} · ${w.card.rarity}</div><div class="price">${w.price}◈</div>`;
    } else if(w.kind==='relic'){
      const rel = RELICS[w.id];
      inner = `<div class="itemIcon">${rel.img}</div><div>${rel.name}</div>
        <div class="tooltip">${rel.rarity}</div><div class="price">${w.price}◈</div>`;
    } else if(w.kind==='potion'){
      const pot = POTIONS[w.potion];
      inner = `<div class="itemIcon">${pot.img}</div><div>${pot.name}</div><div class="price">${w.price}◈</div>`;
    }
    d.innerHTML = inner;
    d.onclick = ()=>buyItem(i);
    grid.appendChild(d);
  });
  if(p.gold<75){ el('removeCardBtn').disabled = true; }
}
function buyItem(i){
  const p = Game.player;
  const w = el('overlayBox')._shopWares[i];
  if(!w || p.gold < w.price) return;
  p.gold -= w.price;
  if(w.kind==='card'){ p.deck.push(w.card); }
  else if(w.kind==='relic'){ p.relics.push(w.id); if(RELICS[w.id].oneTime){ /* one-time use handled */ } }
  else if(w.kind==='potion'){ if(p.potions.length<p.potionSlots) p.potions.push(w.potion); }
  el('overlayBox')._shopWares[i].sold = true;
  const node = el('shopGrid').children[i];
  if(node) node.classList.add('sold');
  renderShop(el('overlayBox')._shopWares);
}
function shopRemoveCard(){
  const p = Game.player;
  if(p.gold<75) return;
  const removable = p.deck.filter(c=>!c.curse && !c.token);
  if(removable.length===0) return;
  const html = `<h2>Remove Card</h2><div class="msgText">Choose a card to remove (75g):</div><div class="rewardCards" id="remCards"></div>`;
  showOverlay(html);
  const rc = el('remCards');
  removable.forEach(card=>{
    const d = createRewardCard(card);
    d.onclick = ()=>{ p.gold-=75; p.deck=p.deck.filter(c=>c!==card); hideOverlay(); renderShop(el('overlayBox')._shopWares); };
    rc.appendChild(d);
  });
}
function leaveShop(){ hideOverlay(); renderMap(); }

// ---------- Events ----------
function showEvent(){
  const eventId = pick(Object.keys(EVENTS));
  const ev = EVENTS[eventId];
  const html = `<h2>${ev.name}</h2><div class="msgText">${ev.text}</div><div class="listChoices" id="eventChoices"></div>`;
  showOverlay(html);
  const lc = el('eventChoices');
  el('overlayBox')._event = ev;
  ev.choices.forEach((ch, i)=>{
    const b = document.createElement('button');
    b.className='btn';
    b.textContent = ch.label;
    b.onclick = ()=>runEventChoice(i);
    lc.appendChild(b);
  });
}
function runEventChoice(i){
  const ev = el('overlayBox')._event;
  const ch = ev.choices[i];
  const g = Game;
  const res = ch.fx(g);
  hideOverlay();
  if(res && res.fail){
    showOverlay(`<div class="msgText">${res.fail}</div><div class="center-wrap"><button class="btn" onclick="hideOverlay();renderMap();">OK</button></div>`);
    return;
  }
  if(res && res.msg){ log(res.msg); }
  // Check death
  if(Game.player.hp<=0){ handlePlayerDeath(); return; }
  advanceNode();
}

// ---------- Relic / potion / card helpers ----------
function gainRelic(id){
  const p = Game.player;
  if(p.relics.includes(id)){ return; }
  p.relics.push(id);
}
function addPotion(pid){
  const p = Game.player;
  if(p.potions.length < p.potionSlots){ p.potions.push(pid); }
}
function addCurse(id){
  const p = Game.player;
  p.deck.push(makeCard(id));
}
function removeCardForEvent(){
  const p = Game.player;
  const removable = p.deck.filter(c=>!c.curse && !c.token);
  if(removable.length===0) return null;
  const card = removable[0];
  p.deck = p.deck.filter(c=>c!==card);
  return card;
}
function removeRandomCardForBet(){
  const p = Game.player;
  const removable = p.deck.filter(c=>!c.curse && !c.token);
  if(removable.length===0) return null;
  const card = pick(removable);
  p.deck = p.deck.filter(c=>c!==card);
  return card;
}
function removeAllCurses(){
  const p = Game.player;
  p.deck = p.deck.filter(c=>!c.curse);
}
function upgradeRandomCards(n){
  const p = Game.player;
  const upgradable = p.deck.filter(c=>!c.upgradeCard);
  const done = [];
  for(let i=0;i<n && upgradable.length;i++){
    const card = pick(upgradable);
    card.upgradeCard = {id:card.id+'+'}; card.upgraded = true;
    done.push(card.name);
    upgradable.splice(upgradable.indexOf(card),1);
  }
  return done;
}

// addBuff helper on player
function addBuff(status, amount, dur){
  this.statuses[status] = (this.statuses[status]||0) + amount;
}
Game.player && (Game.player.statuses = Game.player.statuses||{});

// ---------- Init ----------
renderClassSelect();
window.restart = restart;
window.gameOver = gameOver;
window.skipReward = skipReward;
window.skipBossReward = skipBossReward;
window.leaveShop = leaveShop;
</script>
</body>
</html>