Shadows
GPT 5.6 Luna · legacy

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 25,223 bytes · SHA-256 0e2d05706348
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Shadows of the Abyss</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@500;700&family=Inter:wght@400;600;700&display=swap');
:root{--bg:#090a12;--panel:#151525;--panel2:#1d1d32;--ink:#eeeafa;--muted:#9895ad;--red:#c94d63;--gold:#e5b85c;--violet:#9574dc;--blue:#67a8dc;--green:#6ec48b;--line:#383550}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 50% -20%,#29204b 0,#0b0a13 55%);color:var(--ink);font:14px Inter,system-ui;min-height:100vh}h1,h2,h3,.title,.brand{font-family:Cinzel,serif;letter-spacing:.04em}button{font:inherit;color:inherit;background:#24233a;border:1px solid #4c4769;border-radius:5px;padding:10px 15px;cursor:pointer;transition:.15s}button:hover:not(:disabled){border-color:var(--gold);background:#302d4c;transform:translateY(-1px)}button:disabled{opacity:.4;cursor:not-allowed}.screen{max-width:1200px;margin:auto;padding:24px}.brand{color:var(--gold);font-size:19px}.top{display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--line);padding:8px 0 15px;margin-bottom:18px}.stats{display:flex;gap:18px;color:var(--muted)}.stats b{color:var(--ink)}.heart{color:#e55b6f}.gold{color:var(--gold)}.center{text-align:center}.hero{max-width:760px;margin:8vh auto}.hero h1{font-size:clamp(34px,7vw,70px);margin:20px 0 6px;color:#eee}.subtitle{color:#a79bbb;font-size:16px}.choices{display:flex;gap:15px;justify-content:center;flex-wrap:wrap;margin-top:35px}.classcard{width:245px;min-height:200px;background:linear-gradient(145deg,#1b192e,#11111d);border:1px solid var(--line);padding:22px;text-align:left}.classcard:hover{border-color:var(--gold)}.classcard h2{margin:0 0 8px}.classcard p{color:var(--muted);line-height:1.55}.blood{color:#db6071}.shadow{color:#ad8ce9}.hell{color:#e99553}.panel{background:linear-gradient(150deg,#181829eF,#10101ceF);border:1px solid var(--line);border-radius:8px;padding:18px;box-shadow:0 14px 45px #0005}.map{display:flex;flex-direction:column-reverse;align-items:center;gap:7px;min-height:540px;padding:8px;background:#11111e;border:1px solid var(--line);border-radius:8px}.row{display:flex;gap:15px;justify-content:center;width:100%;min-height:47px;align-items:center}.node{width:64px;height:38px;padding:5px;font-size:11px;border-radius:20px;background:#29253e;border:1px solid #575170}.node.enemy{border-color:#984c60}.node.elite{border-color:#d08b4e}.node.rest{border-color:#69b28e}.node.shop{border-color:#d8b054}.node.event{border-color:#8c70d3}.node.boss{border-color:#e45a67;background:#442037}.node.done{opacity:.35}.node.current{box-shadow:0 0 0 2px var(--gold),0 0 18px #d4a84d88}.maplegend{display:flex;gap:15px;justify-content:center;color:var(--muted);font-size:12px;margin-top:12px}.layout{display:grid;grid-template-columns:1fr 290px;gap:18px}.combat{min-height:650px}.combathead{display:flex;justify-content:space-between;align-items:center}.intent{font-size:13px;color:#eabf72}.enemies{display:flex;gap:12px;justify-content:center;align-items:end;min-height:190px;margin:20px 0}.enemy{width:190px;text-align:center;padding:15px;background:#211725;border:1px solid #563849;border-radius:7px;position:relative}.enemy.dead{opacity:.25}.enemy .figure{font-size:42px}.bar{height:8px;background:#3c2632;border-radius:10px;overflow:hidden;margin:8px 0}.bar i{display:block;height:100%;background:var(--red)}.block{color:#75b9e8}.badges{min-height:23px;color:#d48ca1;font-size:12px}.hand{display:flex;flex-wrap:wrap;gap:9px;min-height:135px;align-items:flex-start}.card{width:132px;min-height:125px;background:linear-gradient(160deg,#282543,#171729);border:1px solid #61578b;border-radius:6px;padding:9px;text-align:left;position:relative;cursor:pointer}.card:hover{border-color:var(--gold);transform:translateY(-5px)}.card.attack{border-color:#a55768}.card.power{border-color:#c08b45}.card .cost{position:absolute;top:-7px;left:-7px;border-radius:50%;background:#5c77ae;width:25px;height:25px;text-align:center;padding-top:4px;font-weight:bold}.card h3{font-size:13px;margin:4px 0 8px}.card small{color:var(--muted);line-height:1.35}.unplay{cursor:not-allowed;opacity:.6}.combatfoot{display:flex;justify-content:space-between;align-items:center;margin-top:10px}.log{height:140px;overflow:auto;color:#aaa7ba;font-size:12px;line-height:1.55;border-top:1px solid var(--line);margin-top:12px;padding-top:8px}.side section{margin-bottom:14px}.side h3{margin:0 0 9px;color:var(--gold);font-size:14px}.list{display:flex;flex-wrap:wrap;gap:5px}.tag{background:#28263e;padding:5px 7px;border-radius:4px;font-size:11px}.modal{position:fixed;inset:0;background:#05050bd9;display:flex;align-items:center;justify-content:center;padding:20px;z-index:5}.modal>div{max-width:800px;width:100%;max-height:90vh;overflow:auto}.rewardcards{display:flex;gap:12px;flex-wrap:wrap}.rewardcards .card{flex:1;min-width:150px}.eventtext{font-size:17px;color:#cbc5da;line-height:1.7}.option{display:block;width:100%;margin:10px 0;text-align:left}.shopgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:10px}.shopitem{padding:12px;background:#24233a;border:1px solid var(--line);border-radius:5px}.toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:#29263d;padding:12px 20px;border:1px solid var(--gold);z-index:9}.tiny{font-size:12px;color:var(--muted)}
@media(max-width:800px){.layout{grid-template-columns:1fr}.side{display:grid;grid-template-columns:1fr 1fr;gap:12px}.enemy{width:145px}.card{width:115px}.screen{padding:12px}}
</style></head><body><main id="app"></main><script>
const $=s=>document.querySelector(s), rnd=a=>a[Math.floor(Math.random()*a.length)], shuffle=a=>a.sort(()=>Math.random()-.5), esc=s=>s.replace(/[&<>]/g,c=>({'&':'&','<':'<','>':'>'}[c]));
let G=null;
const basics=[{n:'Strike',t:'attack',c:1,r:'Basic',d:'Deal 6 damage.',u:'Deal 8 damage.',v:6,vu:8},{n:'Defend',t:'skill',c:1,r:'Basic',d:'Gain 5 Block.',u:'Gain 8 Block.',v:5,vu:8}];
const pools={Blood Reaper:[['Sanguine Strike','attack',1,'Common','Deal 8 damage. Heal 2 HP.',8],['Hemorrhage','attack',1,'Common','Deal 5 damage. Apply 3 Bleed.',5],['Bloodletting','attack',1,'Uncommon','Lose 3 HP. Deal 12 damage.',12],['Crimson Eruption','attack',2,'Rare','Deal 8 damage to ALL enemies. Lose 2 HP.',8],['Crimson Shield','skill',1,'Common','Gain 6 Block. Lose 2 HP.',6],['Blood Ward','skill',1,'Common','Gain 3 Block. Gain 1 Regeneration.',3],['Sacrificial Barrier','skill',2,'Uncommon','Gain 10 Block. Lose 5 HP.',10],['Vital Surge','skill',2,'Rare','Gain Block equal to missing HP. Exhaust.',0],['Life Tap','skill',0,'Common','Draw 2 cards. Lose 3 HP.',0],['Rend Flesh','skill',1,'Uncommon','Apply 5 Bleed to ALL enemies. Lose 2 HP.',5],['Soul Siphon','power',2,'Uncommon','Start of turn: gain 1 Energy and lose 1 HP.',0],['Blood Pact','power',1,'Rare','Whenever you lose HP from a card, gain 1 Strength.',0]],Shadowmancer:[['Shadow Bolt','attack',1,'Common','Deal 6 damage. Apply 1 Weak.',6],['Grasp of Despair','attack',1,'Common','Deal 4 damage. Apply 1 Vulnerable.',4],['Nightmare Lash','attack',2,'Uncommon','Deal 7 damage. If Weak, deal 7 more.',7],['Eternal Darkness','attack',2,'Rare','Deal 3 damage to a random enemy 3 times.',3],['Cloak of Shadows','skill',1,'Common','Gain 5 Block. Draw 1 card.',5],['Haunting Mist','skill',1,'Common','Gain 7 Block. Apply 1 Weak to ALL.',7],['Phantom Armor','skill',1,'Uncommon','Gain 8 Block. Next turn gain 2 Dexterity.',8],['Whispering Shroud','skill',1,'Rare','Gain 4 Block. Next turn gain 4 Block.',4],['Shadowform','skill',2,'Uncommon','Gain 2 Intangible. Exhaust.',0],['Curse of Misfortune','skill',1,'Common','Enemy loses 3 Strength. Apply 1 Vulnerable.',0],['Dark Ritual','skill',0,'Uncommon','Gain 2 Energy. Shuffle a Dazed.',0],['Umbral Mastery','power',2,'Rare','Start of turn: apply 1 Weak to ALL enemies.',0]],Hellforged:[['Firebolt','attack',1,'Common','Deal 7 damage. Apply 2 Burn.',7],['Hellfire Blast','attack',2,'Uncommon','Deal 9 damage to ALL enemies.',9],['Searing Strike','attack',1,'Common','Deal 6 damage. Gain 1 Energy next turn.',6],['Immolate','attack',2,'Rare','Deal 12 damage. Exhaust.',12],['Obsidian Skin','skill',1,'Common','Gain 7 Block.',7],['Flame Ward','skill',1,'Common','Gain 5 Block. Apply 2 Burn to a random enemy.',5],['Demonic Resilience','skill',1,'Uncommon','Gain 4 Block. Heal 2 HP.',4],['Brimstone Shield','skill',1,'Rare','Gain 3 Block. Gain 2 Strength this turn.',3],['Summon Imp','skill',1,'Common','Add an Imp Claw to hand. Exhaust.',0],['Infernal Pact','skill',0,'Uncommon','Lose 2 HP. Gain 2 Energy. Add Imp Claw.',0],['Hellfire Aura','power',2,'Rare','Start of turn: deal 3 damage to ALL.',0],['Soul Burn','skill',1,'Uncommon','Apply 8 Burn. Draw 1 card.',8]]};
const potions=[['Blood Vial','Heal 15 HP'],['Firebomb','Deal 10 damage to ALL'],['Dex Tonic','Gain 3 Dexterity'],['Energy Elixir','Gain 2 Energy'],['Cursed Brew','Apply 3 Vulnerable to ALL'],['Bleeding Flask','Apply 6 Bleed'],['Imp-in-a-Bottle','Add Imp Claw+']];
const relics=['Bloodstone','Shadow Cloak','Cursed Amulet','Tome of Dark Rituals','Bleeding Edge','Soul Ember','Eternal Hourglass','Ashen Armor','Greedy Imp'];
const acts=[{name:'The Crypts',normal:[['Skeleton Warrior',22,6],['Zombie Brute',28,8],['Dark Cultist',20,5],['Rat Swarm',15,3]],elite:[['Tomb Guardian',42,6],['Blood Witch',36,5]],boss:['Lich King',65,8]},{name:'Blighted Woods',normal:[['Wraith',30,7],['Corrupted Beast',38,12],['Thorned Horror',25,4],['Witch Doctor',22,6]],elite:[['Treant Protector',50,9],['Plague Shaman',42,8]],boss:['Eldritch Treant',75,10]},{name:'Abyssal Spire',normal:[['Hellhound',26,8],['Shadow Demon',30,7],['Infernal Imp',18,5],['Abyssal Cultist',24,7]],elite:[['Flamespawn',48,9],['Dread Knight',55,12]],boss:['Archdemon',85,12]}];
function card(x,up=false){let [n,t,c,r,d,v]=x;return {n,t,c,r,d,v,up};}function cardPool(){return pools[G.cls].map(x=>card(x))}function log(s){G.log.unshift(s);renderCombat()}
function start(){G={screen:'map',act:0,floor:1,cls:null,hp:70,max:70,energy:3,block:0,str:0,dex:0,weak:0,vuln:0,intangible:0,regen:0,gold:99,deck:[],draw:[],discard:[],exhaust:[],hand:[],relics:[],potions:[],map:[],log:[],enemy:[],turn:0};renderClass()}
function renderClass(){app.innerHTML=`<div class="screen hero center"><div class="brand">✦ THE ABYSSAL ARCHIVES ✦</div><h1>Shadows of the Abyss</h1><div class="subtitle">Every victory deepens the curse.</div><div class="choices">${Object.keys(pools).map((c,i)=>`<button class="classcard" onclick="choose('${c}')"><h2 class="${i==0?'blood':i==1?'shadow':'hell'}">${c}</h2><p>${i==0?'Sacrifice life for devastating blows and Bleed.':i==1?'Twist Weak, Vulnerable, and Intangible to your will.':'Harness Burn, fire, and forbidden infernal energy.'}</p><b>Choose archetype →</b></button>`).join('')}</div></div>`}
function choose(c){G.cls=c;G.deck=[...Array(5)].map(()=>card(basics[0])).concat([...Array(4)].map(()=>card(basics[1])),card(pools[c][0]));makeMap();renderMap()}
function makeMap(){let types=['enemy','enemy','event','shop','enemy','rest','enemy','elite','event','enemy','elite','enemy','rest','boss'];G.map=types.map((t,i)=>({t,f:i+2,side:t==='elite'?['elite','enemy']:t==='enemy'&&[0,6].includes(i)?['enemy','enemy']:[t]}))}
function header(){return `<div class="top"><div class="brand">SHADOWS <span class="tiny">OF THE ABYSS · ACT ${G.act+1}</span></div><div class="stats"><span class="heart">♥ <b>${G.hp}/${G.max}</b></span><span class="gold">◆ <b>${G.gold}</b></span><span>Deck <b>${G.deck.length}</b></span></div></div>`}
function renderMap(){G.screen='map';app.innerHTML=`<div class="screen">${header()}<div class="panel center"><h2>${acts[G.act].name}</h2><p class="tiny">Choose your path through the dying world. Floor ${Math.max(1,G.floor-1)} of 15</p><div class="map">${G.map.map((n,i)=>`<div class="row">${n.side.map((t,j)=>`<button class="node ${t} ${n.f<G.floor?'done':''} ${n.f===G.floor?'current':''}" ${n.f===G.floor?'onclick="visit('+i+')"':''}>${t==='boss'?'☠ BOSS':t==='elite'?'♜ ELITE':t==='rest'?'♨ REST':t==='shop'?'◆ SHOP':t==='event'?'? EVENT':'⚔ ENEMY'}</button>`).join('')}</div>`).join('')}</div><div class="maplegend"><span>⚔ battle</span><span>♜ elite</span><span>♨ rest</span><span>◆ shop</span><span>? event</span></div></div></div>`}
function visit(i){let t=G.map[i].t;G.floor++; if(t==='enemy'||t==='elite'||t==='boss')combat(t); else if(t==='rest')rest(); else if(t==='shop')shop(); else eventNode()}
function makeEnemies(type){let a=acts[G.act], specs=type==='boss'?[a.boss]:type==='elite'?[rnd(a.elite)]:[rnd(a.normal)];G.enemy=specs.map(s=>({name:s[0],hp:s[1],max:s[1],dmg:s[2],block:0,turn:0,weak:0,vuln:0,bleed:0,burn:0,str:0}))}
function combat(type){G.screen='combat';G.ctype=type;G.energy=3;G.block=0;G.turn=0;G.draw=[...G.deck];G.discard=[];G.exhaust=[];G.hand=[];G.log=[];makeEnemies(type);if(G.relics.includes('Shadow Cloak'))G.intangible++;if(G.relics.includes('Cursed Amulet'))G.enemy.forEach(e=>e.weak++);draw(5);renderCombat()}
function draw(n){for(let i=0;i<n;i++){if(!G.draw.length){G.draw=shuffle(G.discard);G.discard=[]}if(G.draw.length)G.hand.push(G.draw.pop())}}
function dmg(e,n,attack=true){if(attack&&e.vuln)n=Math.ceil(n*1.5);if(e.intangible)n=1;let rem=Math.max(0,n-e.block);e.block=Math.max(0,e.block-n);if(rem>0)e.hp-=rem;return n}
function hitPlayer(n,raw=true){if(G.intangible)n=1;else if(G.vuln)n=Math.ceil(n*1.5);let a=Math.max(0,n-G.block);G.block=Math.max(0,G.block-n);G.hp-=a;return a}
function addBlock(n){G.block+=n+G.dex}function loseHP(n){let actual=G.intangible?1:n;G.hp-=actual;if(G.relics.includes('Bloodstone'))G.hp=Math.min(G.max,G.hp+1)}
function play(i){let c=G.hand[i];if(!c||c.c>G.energy||c.t==='curse')return;G.energy-=c.c;G.hand.splice(i,1);let es=G.enemy.filter(e=>e.hp>0), target=es[0];let n=c.up?c.vu||c.v:c.v;
if(c.n==='Strike')dmg(target,n);else if(c.n==='Defend')addBlock(n);else if(c.n==='Sanguine Strike'){dmg(target,n);G.hp=Math.min(G.max,G.hp+(c.up?3:2))}else if(c.n==='Hemorrhage'){dmg(target,n);target.bleed+=c.up?4:3}else if(c.n==='Bloodletting'){loseHP(c.up?2:3);dmg(target,n)}else if(c.n==='Crimson Eruption'){es.forEach(e=>dmg(e,n));loseHP(2)}else if(c.n==='Crimson Shield'){addBlock(c.up?8:6);loseHP(2)}else if(c.n==='Blood Ward'){addBlock(c.up?4:3);G.regen+=c.up?2:1}else if(c.n==='Sacrificial Barrier'){addBlock(c.up?13:10);loseHP(5)}else if(c.n==='Vital Surge'){addBlock(G.max-G.hp+(c.up?3:0));G.exhaust.push(c)}else if(c.n==='Life Tap'){draw(c.up?3:2);loseHP(3)}else if(c.n==='Rend Flesh'){es.forEach(e=>e.bleed+=c.up?7:5);loseHP(2)}else if(c.n==='Soul Siphon'){G.powerSiphon=1}else if(c.n==='Blood Pact'){G.powerPact=1}else if(c.n==='Shadow Bolt'){dmg(target,c.up?8:6);target.weak+=c.up?2:1}else if(c.n==='Grasp of Despair'){dmg(target,c.up?5:4);target.vuln+=c.up?2:1}else if(c.n==='Nightmare Lash'){dmg(target,n+(target.weak? n:0))}else if(c.n==='Eternal Darkness'){for(let j=0;j<3;j++)dmg(rnd(es),n)}else if(c.n==='Cloak of Shadows'){addBlock(c.up?7:5);draw(1)}else if(c.n==='Haunting Mist'){addBlock(c.up?9:7);es.forEach(e=>e.weak++)}else if(c.n==='Phantom Armor'){addBlock(n);G.dex+=c.up?3:2}else if(c.n==='Whispering Shroud'){addBlock(n)}else if(c.n==='Shadowform'){G.intangible+=c.up?3:2;G.exhaust.push(c)}else if(c.n==='Curse of Misfortune'){target.str=Math.max(0,target.str-3);target.vuln+=c.up?2:1}else if(c.n==='Dark Ritual'){G.energy+=2;G.discard.push(card(['Dazed','curse',0,'Status','Unplayable.',0]))}else if(c.n==='Umbral Mastery'){G.powerUmbral=1}else if(c.n==='Firebolt'){dmg(target,n);target.burn+=c.up?3:2}else if(c.n==='Hellfire Blast'){es.forEach(e=>dmg(e,n))}else if(c.n==='Searing Strike'){dmg(target,n);G.nextEnergy=(G.nextEnergy||0)+1}else if(c.n==='Immolate'){dmg(target,n);G.exhaust.push(c)}else if(c.n==='Obsidian Skin'){addBlock(n)}else if(c.n==='Flame Ward'){addBlock(n);rnd(es).burn+=c.up?3:2}else if(c.n==='Demonic Resilience'){addBlock(n);G.hp=Math.min(G.max,G.hp+(c.up?3:2))}else if(c.n==='Brimstone Shield'){addBlock(n);G.str+=c.up?3:2}else if(c.n==='Summon Imp'||c.n==='Infernal Pact'){if(c.n==='Infernal Pact')loseHP(c.up?1:2);G.energy+=2;G.hand.push(card(['Imp Claw','attack',0,'Status',`Deal ${c.up?6:4} damage. Exhaust.`,c.up?6:4]))}else if(c.n==='Hellfire Aura'){G.powerAura=1}else if(c.n==='Soul Burn'){target.burn+=c.up?10:8;draw(1)}else if(c.n==='Imp Claw'){dmg(target,n);G.exhaust.push(c)}
if(c.t==='power')G.discard.push(c);else if(!G.exhaust.includes(c))G.discard.push(c);if(G.hp<=0)return end(false);if(!G.enemy.some(e=>e.hp>0))return end(true);renderCombat()}
function endTurn(){G.hand.forEach(c=>{if(c.n==='Doubt')loseHP(1);if(c.d!=='Ethereal')G.discard.push(c);else G.exhaust.push(c)});G.hand=[];G.block=0;G.enemy.filter(e=>e.hp>0).forEach(enemyTurn);if(G.hp<=0)return end(false);if(!G.enemy.some(e=>e.hp>0))return end(true);G.turn++;G.energy=3+(G.powerSiphon?1:0)+(G.nextEnergy||0);G.nextEnergy=0;G.dex=0;if(G.regen){G.hp=Math.min(G.max,G.hp+G.regen);G.regen--}G.enemy.forEach(e=>{e.block=0;e.weak=Math.max(0,e.weak-1);e.vuln=Math.max(0,e.vuln-1)});if(G.powerUmbral)G.enemy.forEach(e=>e.weak++);if(G.powerAura)G.enemy.forEach(e=>dmg(e,3,false));if(G.relics.includes('Bleeding Edge'))rnd(G.enemy.filter(e=>e.hp>0)).bleed++;G.intangible=Math.max(0,G.intangible-1);draw(5);renderCombat()}
function enemyTurn(e){if(e.hp<=0)return;if(e.bleed){hitPlayer(e.bleed);e.bleed=Math.floor(e.bleed/2)}if(e.burn){e.hp-=e.burn;e.burn=Math.floor(e.burn/2)}e.turn++;let phase=e.turn%3||3;if(G.ctype==='boss'&&G.hp<=30)phase=e.turn%3||3;if(phase===1){hitPlayer(e.dmg+e.str);if(G.ctype==='elite')e.block+=8}else if(phase===2){e.block+=G.ctype==='boss'?15: (e.name.includes('Brute')?0:8);if(e.name.includes('Brute'))G.weak+=2}else{hitPlayer(e.dmg+e.str);if(e.name.includes('Cultist'))e.str+=3}G.weak=Math.max(0,G.weak-1)}
function intent(e){let p=e.turn%3||3;return p===1?'⚔ Attack '+e.dmg:p===2?'◈ Defend / Debuff':'⚔ Attack '+e.dmg}
function renderCombat(){app.innerHTML=`<div class="screen">${header()}<div class="layout"><div class="panel combat"><div class="combathead"><h2>${G.ctype==='boss'?'☠ ':''}${acts[G.act].boss[0]==G.enemy[0]?.name?'': ''}${esc(G.enemy[0]?.name||'Battle')}</h2><span class="intent">Turn ${G.turn+1} · Player acts first</span></div><div class="enemies">${G.enemy.map((e,i)=>`<div class="enemy ${e.hp<=0?'dead':''}"><div class="figure">${e.hp<=0?'✝':G.ctype==='boss'?'☠':'♟'}</div><b>${esc(e.name)}</b><div class="bar"><i style="width:${Math.max(0,e.hp/e.max*100)}%"></i></div><span>${Math.max(0,e.hp)}/${e.max} HP</span> ${e.block?`<span class="block"> ◈${e.block}</span>`:''}<div class="badges">${intent(e)}${e.bleed?' · Bleed '+e.bleed:''}${e.burn?' · Burn '+e.burn:''}</div></div>`).join('')}</div><div class="tiny">Energy <b>${G.energy}</b> / 3 · Block <b class="block">${G.block}</b> · Strength ${G.str} · Weak ${G.weak} · Intangible ${G.intangible}</div><div class="hand">${G.hand.map((c,i)=>`<div class="card ${c.t} ${c.c>G.energy||c.t==='curse'?'unplay':''}" onclick="play(${i})"><span class="cost">${c.c}</span><h3>${esc(c.n)}${c.up?'+':''}</h3><small>${esc(c.d)}</small></div>`).join('')}</div><div class="combatfoot"><button onclick="endTurn()">End Turn ↗</button><span class="tiny">Draw ${G.draw.length} · Discard ${G.discard.length} · Exhaust ${G.exhaust.length}</span></div><div class="log">${G.log.slice(0,8).map(esc).join('<br>')}</div></div><aside class="panel side"><section><h3>Relics</h3><div class="list">${G.relics.map(x=>`<span class="tag">${x}</span>`).join('')||'None'}</div></section><section><h3>Potions</h3><div class="list">${G.potions.map((x,i)=>`<button class="tag" onclick="usePotion(${i})">${x[0]}</button>`).join('')||'None'}</div></section><section><h3>Deck (${G.deck.length})</h3><div class="list">${G.deck.map(c=>`<span class="tag">${c.n}${c.up?'+':''}</span>`).join('')}</div></section></aside></div></div>`}
function end(win){if(!win){app.innerHTML=`<div class="screen hero center"><h1>THE ABYSS CLAIMS YOU</h1><p class="subtitle">Your legend ends in darkness.</p><button onclick="start()">Begin Again</button></div>`;return}if(G.ctype==='boss'){if(G.act===2){app.innerHTML=`<div class="screen hero center"><div class="brand">✦ ASCENSION ✦</div><h1>THE ABYSS IS SILENT</h1><p class="subtitle">You have defeated the Archdemon. Against impossible odds, you endured.</p><button onclick="start()">Play Again</button></div>`;return}G.hp=G.max;G.act++;G.floor=1;makeMap();return renderMap()}reward()}
function reward(){let opts=shuffle(cardPool()).slice(0,3);let gold=G.ctype==='elite'?30:15;G.gold+=gold;if(G.ctype==='elite')G.relics.push(rnd(relics));if(Math.random()<.5&&G.potions.length<3)G.potions.push(rnd(potions));modal(`<h2>Victory</h2><p>You gain <span class="gold">${gold} gold</span>. ${G.ctype==='elite'?'A relic falls from the corpse.':''}</p><h3>Choose a card</h3><div class="rewardcards">${opts.map((c,i)=>`<div class="card ${c.t}" onclick="takeCard(${i},${JSON.stringify(opts).replace(/"/g,'"')})"><h3>${c.n}</h3><small>${c.d}</small><br><br><span class="tiny">${c.r} · ${c.c} energy</span></div>`).join('')}</div><br><button onclick="closeModal();renderMap()">Skip reward</button>`)}
function takeCard(i,opts){G.deck.push(opts[i]);closeModal();renderMap()}
function modal(x){let d=document.createElement('div');d.className='modal';d.innerHTML='<div class="panel">'+x+'</div>';document.body.append(d)}function closeModal(){document.querySelector('.modal')?.remove()}
function rest(){modal(`<h2>Rest Site</h2><p>The fire whispers beneath the stone.</p><button class="option" onclick="G.hp=Math.min(G.max,G.hp+Math.floor(G.max*.25));closeModal();renderMap()">♨ Rest — heal ${Math.floor(G.max*.25)} HP</button><button class="option" onclick="upgrade();">⚒ Forge — upgrade a card</button><button class="option" onclick="removeCard();">☽ Meditate — remove a card</button>`)}
function upgrade(){modal('<h2>Choose a card to upgrade</h2>'+G.deck.map((c,i)=>`<button class="option" onclick="G.deck[${i}].up=true;closeModal();renderMap()">${c.n} — ${c.d}</button>`).join(''))}function removeCard(){modal('<h2>Choose a card to remove</h2>'+G.deck.map((c,i)=>`<button class="option" onclick="G.deck.splice(${i},1);closeModal();renderMap()">${c.n}</button>`).join(''))}
function eventNode(){let e=rnd([{n:'The Forgotten Altar',t:'A blood-stained altar hums with power.',o:[['Offer 15 HP',()=>{loseHP(15);G.relics.push(rnd(relics))}],['Pray',()=>{G.hp=Math.min(G.max,G.hp+10);G.deck.push(card(['Doubt','curse',0,'Curse','Lose 1 HP at end of turn.',0]))}],['Leave',()=>{}]]},{n:'Cursed Spring',t:'Dark water bubbles beneath a dead moon.',o:[['Drink',()=>{loseHP(10);G.deck=G.deck.filter(c=>c.t!=='curse')}],['Bottle it',()=>G.potions.push(['Cursed Brew','Apply Vulnerable'])],['Leave',()=>{}]]},{n:'Restless Dead',t:'The ghosts offer a bargain in a dozen voices.',o:[['Accept — lose 20 HP, upgrade 2 cards',()=>{loseHP(20);shuffle(G.deck).slice(0,2).forEach(c=>c.up=true)}],['Refuse',()=>{}]]},{n:'Impish Gambler',t:'A grinning imp rattles a cup of bones.',o:[['Bet 30 Gold',()=>{if(G.gold>=30){G.gold-=30;if(Math.random()>.5)G.gold+=60}}],['Leave',()=>{}]]}]);modal(`<h2>${e.n}</h2><p class="eventtext">${e.t}</p>${e.o.map((o,i)=>`<button class="option" onclick="eventChoice(${i})">${o[0]}</button>`).join('')}`);G.event=e}
function eventChoice(i){G.event.o[i][1]();closeModal();if(G.hp<=0)return end(false);renderMap()}
function shop(){let items=shuffle(cardPool()).slice(0,3);modal(`<h2>Shadow Market</h2><p class="tiny">Gold: ${G.gold}</p><div class="shopgrid">${items.map((c,i)=>`<div class="shopitem"><b>${c.n}</b><p class="tiny">${c.d}</p><button onclick="buyCard(${i})">Buy ${c.r==='Rare'?150:c.r==='Uncommon'?75:50}g</button></div>`).join('')}<div class="shopitem"><b>Colorless: Flash of Insight</b><p class="tiny">Draw 2 cards.</p><button onclick="buyColorless()">Buy 60g</button></div>${relics.slice(0,2).map((r,i)=>`<div class="shopitem"><b>Relic: ${r}</b><p class="tiny">A mysterious passive boon.</p><button onclick="buyRelic(${i})">Buy 150g</button></div>`).join('')}</div><br><button onclick="closeModal();renderMap()">Leave shop</button>`);G.shop=items}
function buyCard(i){let c=G.shop[i],p=c.r==='Rare'?150:c.r==='Uncommon'?75:50;if(G.gold>=p){G.gold-=p;G.deck.push(c);closeModal();renderMap()}}function buyColorless(){if(G.gold>=60){G.gold-=60;G.deck.push(card(['Flash of Insight','skill',1,'Common','Draw 2 cards.',0]));closeModal();renderMap()}}function buyRelic(i){if(G.gold>=150){G.gold-=150;G.relics.push(relics[i]);closeModal();renderMap()}}
function usePotion(i){let p=G.potions[i],n=p[0],es=G.enemy.filter(e=>e.hp>0);if(n==='Blood Vial')G.hp=Math.min(G.max,G.hp+15);else if(n==='Firebomb')es.forEach(e=>dmg(e,10));else if(n==='Energy Elixir')G.energy+=2;else if(n==='Dex Tonic')G.dex+=3;else if(n==='Cursed Brew')es.forEach(e=>e.vuln+=3);else if(n==='Bleeding Flask')es[0].bleed+=6;else if(n==='Imp-in-a-Bottle')G.hand.push(card(['Imp Claw','attack',0,'Status','Deal 6 damage. Exhaust.',6]));G.potions.splice(i,1);renderCombat()}
start();
</script></body></html>