Deckbuilder
GPT 6 Luna · full

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 42,364 bytes · SHA-256 48c519822c45
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shadows of Aethelgard</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700;800&family=DM+Sans:wght@400;500;600;700&display=swap');
:root{--ink:#090b10;--panel:#11151c;--panel2:#171c25;--gold:#d2ad69;--gold2:#f0d69b;--muted:#89909c;--line:rgba(211,178,114,.23);--red:#d76855;--blue:#7ca9c7;--violet:#aa84c7;--green:#91b589;--serif:'Cinzel',Georgia,serif;--sans:'DM Sans',sans-serif}
*{box-sizing:border-box}body{margin:0;background:#080a0f;color:#e9e5dd;font-family:var(--sans);min-height:100vh;background-image:radial-gradient(ellipse at 50% -20%,#22212a 0%,transparent 57%),linear-gradient(180deg,#0c0e14,#080a0f 70%)}button{font:inherit;color:inherit;cursor:pointer}button:focus-visible{outline:2px solid var(--gold2);outline-offset:3px}.game{max-width:1500px;min-height:100vh;margin:auto;padding:0 34px 24px;position:relative}.topbar{height:76px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);position:relative;z-index:2}.brand{display:flex;align-items:center;gap:13px}.sigil{width:34px;height:34px;border:1px solid #9d7945;transform:rotate(45deg);display:grid;place-items:center;color:var(--gold);font-size:17px}.sigil span{transform:rotate(-45deg)}.brand h1{font:600 17px var(--serif);letter-spacing:2.4px;margin:0}.brand small{display:block;font:10px var(--sans);letter-spacing:3px;color:#90909a;margin-top:3px}.topstats{display:flex;align-items:center;gap:30px}.stat{display:flex;align-items:center;gap:9px;color:#b8b5af;font-size:12px;letter-spacing:.7px}.stat b{color:#f1e9d8;font:600 14px var(--serif)}.stat .ico{font-size:17px;color:var(--gold)}.top-actions{display:flex;gap:9px}.icon-btn{border:1px solid #37343a;background:#14161c;color:#b7b1a6;padding:8px 12px;font-size:11px;letter-spacing:1px;text-transform:uppercase}.icon-btn:hover{border-color:var(--gold);color:var(--gold2)}
.main{padding-top:23px}.eyebrow{color:var(--gold);font:600 10px var(--sans);letter-spacing:3px;text-transform:uppercase}.room-head{display:flex;align-items:end;justify-content:space-between;margin:0 0 17px}.room-head h2{font:500 23px var(--serif);margin:5px 0 0;letter-spacing:1px}.room-head p{color:#858995;font-size:12px;margin:0 0 3px}.progress{display:flex;align-items:center;gap:6px}.progress i{display:block;width:27px;height:3px;background:#292b31}.progress i.done{background:var(--gold)}.progress i.now{background:#f0d69b;box-shadow:0 0 9px #d5ad68}
.battle-grid{display:grid;grid-template-columns:225px minmax(440px,1fr) 235px;gap:17px;min-height:370px}.panel{background:linear-gradient(145deg,rgba(25,29,37,.96),rgba(14,17,23,.97));border:1px solid #34343a;position:relative;box-shadow:0 14px 38px #0005}.panel:before{content:'';position:absolute;inset:4px;border:1px solid rgba(255,255,255,.025);pointer-events:none}.hero-panel{padding:19px 17px;display:flex;flex-direction:column}.panel-title{font:500 11px var(--sans);letter-spacing:2px;color:#a8a7a4;text-transform:uppercase;display:flex;justify-content:space-between;align-items:center}.hero-portrait{flex:1;min-height:170px;display:grid;place-items:center;position:relative;overflow:hidden}.hero-portrait:before{content:'';position:absolute;width:150px;height:150px;border:1px solid #6f5d40;transform:rotate(45deg);opacity:.36}.hero-portrait:after{content:'';position:absolute;bottom:0;left:8%;right:8%;height:45%;background:linear-gradient(0deg,#12151c,transparent)}.hero-portrait svg{height:172px;width:150px;z-index:1;filter:drop-shadow(0 9px 13px #000)}.hero-name{text-align:center;font:600 17px var(--serif);letter-spacing:1px;margin:0}.hero-class{text-align:center;font-size:10px;letter-spacing:2px;color:var(--gold);text-transform:uppercase;margin:5px 0 16px}.meter-label{display:flex;justify-content:space-between;font-size:10px;color:#a3a4aa;margin:11px 0 6px}.meter-label b{font-weight:600;color:#eee}.bar{height:7px;background:#29282d;border:1px solid #3b3738;overflow:hidden}.bar span{height:100%;display:block;transition:width .35s}.hp-fill{background:linear-gradient(90deg,#8f302d,#df6c59);box-shadow:0 0 10px #d64e3c}.block-row{display:flex;justify-content:space-between;align-items:center;margin-top:14px;padding-top:12px;border-top:1px solid #303039;color:#999aa1;font-size:11px}.block-row b{color:#b8d4e8;font-size:16px}.center-panel{position:relative;min-height:370px;overflow:hidden;background:radial-gradient(ellipse at 50% 64%,#382f34 0%,#1c2028 42%,#131720 77%);border:1px solid #39363a}.center-panel:before{content:'';position:absolute;inset:0;background:linear-gradient(180deg,transparent 62%,rgba(5,7,10,.8)),repeating-linear-gradient(115deg,transparent 0 76px,rgba(210,177,117,.025) 77px 78px)}.scene-top{position:absolute;top:16px;left:20px;right:20px;display:flex;justify-content:space-between;align-items:flex-start;z-index:3}.room-tag{font-size:9px;color:#cfb57f;letter-spacing:2px;text-transform:uppercase;background:#11151bc9;border:1px solid #62523a;padding:7px 9px}.intent{font-size:11px;color:#d8c4a0;text-align:right}.intent b{color:#e17c65;font:600 13px var(--serif)}.battle-art{position:absolute;inset:30px 0 0;display:grid;place-items:center}.battle-art svg{width:min(330px,65%);height:265px;filter:drop-shadow(0 14px 18px #0008);transition:transform .18s}.battle-art svg.hit{transform:translateX(9px) rotate(2deg);filter:drop-shadow(0 0 20px #d76855)}.ground{position:absolute;bottom:52px;width:68%;height:23px;border-radius:50%;background:#0b0c10;box-shadow:0 0 25px 14px #111319;z-index:0}.enemy-info{position:absolute;bottom:16px;left:12%;right:12%;z-index:3}.enemy-heading{display:flex;justify-content:space-between;align-items:center;margin-bottom:7px}.enemy-heading h3{font:600 15px var(--serif);letter-spacing:1.5px;margin:0}.enemy-heading .hp-text{font-size:11px;color:#d3c7b6}.enemy-bar{height:8px;background:#291c20;border:1px solid #66443f}.enemy-fill{background:linear-gradient(90deg,#852e32,#d25b50);box-shadow:0 0 12px #aa3939;transition:width .35s}.enemy-status{position:absolute;bottom:48px;right:12%;display:flex;gap:5px;z-index:4}.badge{padding:4px 7px;font-size:9px;letter-spacing:.5px;border:1px solid #71533a;background:#18140f;color:#e9bd70}.badge.poison{border-color:#52724c;color:#aed398}.badge.stun{border-color:#65778e;color:#b3cbe5}.boss-ability{font-size:9px;color:#c0a574;text-align:center;margin-top:5px;letter-spacing:.7px}
.side-panel{padding:17px 16px;display:flex;flex-direction:column}.energy-title{display:flex;justify-content:space-between;align-items:center;font:500 11px var(--sans);letter-spacing:2px;color:#a8a7a4}.energy-title span{color:#83b4df;font-size:11px}.energy-orbs{display:flex;gap:7px;margin:17px 0 15px}.orb{height:23px;width:23px;border-radius:50%;border:1px solid #567d9c;background:radial-gradient(circle at 35% 30%,#d0e8f7,#5282a5 38%,#172e45 75%);box-shadow:0 0 9px #4c8cbb80}.orb.empty{background:#202832;border-color:#354554;box-shadow:none;opacity:.62}.side-divider{height:1px;background:#34333a;margin:2px 0 14px}.side-stat{display:flex;justify-content:space-between;align-items:center;margin:9px 0;color:#9b9ca3;font-size:11px}.side-stat b{color:#e4dccd;font-size:12px}.side-stat .goldval{color:#e4c47b}.target-control{width:100%;margin-top:7px;text-align:left;background:#171a21;border:1px solid #373840;padding:10px;color:#bab9ba;font-size:10px;letter-spacing:.6px}.target-control:hover{border-color:#a38a5d}.shop-btn{margin-top:auto;width:100%;border:1px solid #886c42;background:linear-gradient(180deg,#3a3021,#211d18);padding:12px 8px;font:600 11px var(--sans);letter-spacing:1.4px;color:#e3c582;text-transform:uppercase}.shop-btn:hover{background:#493920}.shop-btn:disabled{opacity:.4;cursor:not-allowed}.shop-note{text-align:center;color:#747681;font-size:9px;margin:8px 0 0;line-height:1.5}
.hand-section{margin-top:21px;border-top:1px solid #36343a;padding-top:13px}.hand-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:11px}.hand-head h3{font:500 11px var(--sans);letter-spacing:2px;text-transform:uppercase;color:#b7b4ad;margin:0}.hand-head h3 span{color:#7c7d86;font-size:10px;margin-left:8px}.hand-tools{display:flex;gap:8px;align-items:center;color:#82848d;font-size:10px}.hand{display:flex;justify-content:center;gap:12px;min-height:207px;padding:0 0 5px}.card{--accent:#d76855;width:154px;height:201px;flex:0 0 154px;position:relative;border:0;padding:0;background:none;transition:transform .18s,filter .18s;filter:drop-shadow(0 7px 7px #0008);text-align:left}.card:hover:not(:disabled){transform:translateY(-8px);z-index:2;filter:drop-shadow(0 12px 12px #000a)}.card:disabled{filter:grayscale(.7) brightness(.65);cursor:not-allowed}.card-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}.card .card-content{position:absolute;inset:0;display:flex;flex-direction:column;padding:14px 13px 13px}.card-top{display:flex;justify-content:space-between;align-items:center}.cost{width:27px;height:27px;border-radius:50%;background:#192532;border:1px solid #95b4c4;color:#f0eadc;display:grid;place-items:center;font:600 14px var(--serif);box-shadow:0 0 8px #557c9560}.card-code{font-size:8px;letter-spacing:1px;color:#d0c5b0}.card-title{font:600 13px var(--serif);color:#f0e6d0;text-align:center;margin:9px 0 6px;text-shadow:0 2px 3px #000}.art-placeholder{height:69px;border:1px solid color-mix(in srgb,var(--accent) 42%,#24232b);background:radial-gradient(ellipse,#38343a,#17191f 70%);display:grid;place-items:center;position:relative;overflow:hidden}.art-placeholder:before{content:'';position:absolute;width:50px;height:50px;transform:rotate(45deg);border:1px solid color-mix(in srgb,var(--accent) 55%,transparent);opacity:.5}.art-placeholder span{font-size:27px;color:var(--accent);z-index:1;text-shadow:0 0 15px var(--accent)}.card-desc{font-size:9px;line-height:1.32;text-align:center;color:#c0b9ad;margin:7px 0 2px;flex:1}.card-stats{display:flex;justify-content:center;gap:13px;font-size:10px;color:#eee1ca}.card-stats .atk{color:#e98970}.card-stats .def{color:#95c0dc}.card-type{font-size:7px;text-align:center;letter-spacing:2px;color:var(--accent);text-transform:uppercase;margin-top:5px}.card.attack{--accent:#d76855}.card.defense{--accent:#7ca9c7}.card.other{--accent:#aa84c7}.card.empty{width:100%;max-width:600px;height:auto;min-height:150px;border:1px dashed #3a3a41;background:#11141b;color:#9a9aa2;display:grid;place-items:center;font-size:12px;filter:none}
.controls{display:flex;justify-content:center;gap:12px;margin:9px 0 0}.action-btn{border:1px solid #8f744a;background:linear-gradient(#393022,#211d18);color:#e4ce9b;padding:11px 25px;font:600 10px var(--sans);letter-spacing:1.6px;text-transform:uppercase;min-width:185px}.action-btn:hover{background:#514128}.action-btn.secondary{background:#171a20;border-color:#45434a;color:#aaa8a1;min-width:auto}.action-btn:disabled{opacity:.45;cursor:not-allowed}.combat-log{position:fixed;left:18px;bottom:18px;z-index:10;pointer-events:none;display:flex;flex-direction:column;gap:5px;max-width:250px}.toast{background:#191b20eF;border-left:2px solid var(--gold);padding:9px 12px;color:#d8d1c2;font-size:11px;box-shadow:0 5px 15px #0008;animation:toastin .25s ease}.toast.bad{border-color:#cb6858}.toast.good{border-color:#9cb687}@keyframes toastin{from{transform:translateX(-10px);opacity:0}to{transform:none;opacity:1}}
.overlay{position:fixed;inset:0;background:#050609df;z-index:20;display:flex;justify-content:center;align-items:center;padding:20px;backdrop-filter:blur(7px)}.modal{width:min(850px,96vw);max-height:92vh;overflow:auto;border:1px solid #706044;background:linear-gradient(145deg,#1b1c22,#101218);box-shadow:0 20px 100px #000;position:relative;padding:29px}.modal:before{content:'';position:absolute;inset:6px;border:1px solid #ffffff09;pointer-events:none}.modal-head{text-align:center;margin-bottom:23px}.modal-head .eyebrow{margin-bottom:8px}.modal-head h2{font:500 26px var(--serif);letter-spacing:2px;margin:0;color:#e8dec9}.modal-head p{color:#9999a0;font-size:12px;margin:8px 0 0}.class-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:13px}.class-option{background:#151920;border:1px solid #3b3b42;padding:17px 15px;text-align:center;transition:.2s;cursor:pointer}.class-option:hover{transform:translateY(-4px);border-color:#bb965a;background:#1c1d22}.class-option svg{width:112px;height:130px;display:block;margin:0 auto 8px}.class-option h3{font:600 16px var(--serif);letter-spacing:1px;margin:4px}.class-option p{font-size:10px;line-height:1.5;color:#a5a4a5;min-height:30px}.class-option .pick{font-size:9px;letter-spacing:2px;color:var(--gold);text-transform:uppercase}.modal-footer{text-align:center;color:#777983;font-size:10px;margin:17px 0 0}.shop-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:14px;justify-items:center}.shop-item{text-align:center}.shop-item .card{display:block}.buy-btn{border:1px solid #72603f;background:#242016;padding:8px 12px;color:#dfc27f;font-size:10px;letter-spacing:1px;width:154px}.buy-btn:hover{background:#453821}.buy-btn:disabled{opacity:.45;cursor:not-allowed}.modal-close{position:absolute;right:19px;top:16px;border:0;background:transparent;color:#aaa;font-size:22px}.result-symbol{font:70px var(--serif);color:var(--gold);text-align:center;margin:5px}.result-btn{display:block;margin:20px auto 0}.deck-preview{display:flex;justify-content:center;gap:20px;flex-wrap:wrap}.back-card{width:146px;height:202px;filter:drop-shadow(0 8px 9px #0009)}.deck-label{text-align:center;color:#bcb6a9;font-size:10px;letter-spacing:1px;margin-top:8px}
#gameRoot[hidden],.overlay[hidden]{display:none!important}
@media(max-width:1050px){.game{padding:0 17px}.battle-grid{grid-template-columns:190px minmax(350px,1fr) 190px;gap:11px}.card{width:140px;flex-basis:140px;height:190px}.card .card-content{padding:12px}.card-desc{font-size:8px}.hand{gap:7px}}
@media(max-width:780px){.game{padding:0 11px 20px}.topbar{height:64px}.brand h1{font-size:13px}.brand small{font-size:8px}.topstats{gap:10px}.stat{font-size:9px;gap:5px}.stat b{font-size:11px}.top-actions{display:none}.main{padding-top:15px}.battle-grid{grid-template-columns:1fr 1fr;min-height:auto}.center-panel{grid-column:1/-1;grid-row:1;min-height:300px}.hero-panel,.side-panel{min-height:250px}.hero-portrait{min-height:120px}.hero-portrait svg{height:135px}.room-head h2{font-size:18px}.hand{justify-content:flex-start;overflow-x:auto;padding:4px 5px 12px}.card{width:140px;flex-basis:140px;height:190px}.hand-section{margin-top:14px}.class-grid{grid-template-columns:1fr}.class-option{display:grid;grid-template-columns:90px 1fr;align-items:center;text-align:left}.class-option svg{width:80px;height:95px;grid-row:span 3;margin:0}.class-option p{min-height:0}.modal{padding:23px 17px}.room-head p{display:none}}
</style></head>
<body>
<div id="gameRoot" hidden>
<div class="game">
<header class="topbar"><div class="brand"><div class="sigil"><span>✦</span></div><div><h1>SHADOWS OF AETHELGARD</h1><small>THE VOID DESCENDS</small></div></div><div class="topstats"><div class="stat"><span class="ico">◈</span><span>GOLD</span><b id="goldTop">0</b></div><div class="stat"><span class="ico">⌖</span><span>FLOOR</span><b id="floorTop">I</b></div><div class="stat"><span class="ico">⚔</span><span>ROOM</span><b id="roomTop">01 / 04</b></div></div><div class="top-actions"><button class="icon-btn" id="deckBtn">▤ Deck</button><button class="icon-btn" id="helpBtn">? How to play</button></div></header>
<main class="main"><div class="room-head"><div><div class="eyebrow" id="floorEyebrow">THE SUNDERED GATE · FLOOR I</div><h2 id="roomTitle">A Hollow Crossing</h2></div><div class="progress" id="progress"></div><p id="roomSub">The air tastes of ash and old iron.</p></div>
<div class="battle-grid"><section class="panel hero-panel"><div class="panel-title"><span>Wanderer</span><span id="heroStatus"></span></div><div class="hero-portrait" id="heroPortrait"></div><h3 class="hero-name" id="heroName">—</h3><div class="hero-class" id="heroClass">—</div><div class="meter-label"><span>VITALITY</span><b id="hpText">30 / 30</b></div><div class="bar"><span id="hpBar" class="hp-fill" style="width:100%"></span></div><div class="block-row"><span>ACTIVE GUARD</span><b id="blockText">0</b></div></section>
<section class="center-panel"><div class="scene-top"><div class="room-tag" id="roomTag">ENCOUNTER I</div><div class="intent" id="intent">INTENT<br><b>ATTACK · 3</b></div></div><div class="battle-art" id="enemyArt"></div><div class="ground"></div><div class="enemy-status" id="enemyStatus"></div><div class="enemy-info"><div class="enemy-heading"><h3 id="enemyName">—</h3><span id="enemyHpText" class="hp-text">20 / 20 HP</span></div><div class="bar enemy-bar"><span class="enemy-fill" id="enemyBar" style="width:100%"></span></div><div class="boss-ability" id="bossAbility"></div></div></section>
<aside class="panel side-panel"><div class="energy-title"><span>✧ VIGOR</span><span id="energyText">3 / 3</span></div><div id="energyOrbs" class="energy-orbs"></div><div class="side-divider"></div><div class="side-stat"><span>Draw pile</span><b id="drawCount">0</b></div><div class="side-stat"><span>Discard pile</span><b id="discardCount">0</b></div><div class="side-stat"><span>Run gold</span><b class="goldval" id="goldSide">0 ◈</b></div><button class="target-control" id="targetBtn">◉ TARGET · <b id="targetLabel">FOE</b></button><button class="shop-btn" id="shopBtn">♜ Enter the Reliquary</button><p class="shop-note" id="shopNote">Shop opens between encounters</p></aside></div>
<section class="hand-section"><div class="hand-head"><h3>Your hand <span id="handCount">0 / 4</span></h3><div class="hand-tools"><span>Choose a card to play</span><span>·</span><button class="icon-btn" id="discardBtn" style="padding:5px 8px">Discard hand</button></div></div><div class="hand" id="hand"></div><div class="controls"><button class="action-btn secondary" id="passBtn">↻ End turn</button><button class="action-btn" id="continueBtn" hidden>Continue journey →</button></div></section></main></div></div>
<div id="overlay" class="overlay"><div class="modal" id="modal"></div></div><div class="combat-log" id="log"></div>
<script>
const classes={
bastion:{name:'The Bastion',subtitle:'THE IMMOVABLE',color:'#8fb3c6',start:35,energy:3,hand:4,desc:'An iron-hearted sentinel. Turn every blow aside, then answer with earth-shaking force.',symbol:'⬟'},
wither:{name:'The Wither',subtitle:'ROT & EMBER',color:'#b38572',start:28,energy:3,hand:4,desc:'A keeper of forbidden flame. Let poison and cinder do their slow, merciless work.',symbol:'✹'},
shroud:{name:'The Shroud',subtitle:'KNIFE IN THE DARK',color:'#a68bc1',start:30,energy:3,hand:4,desc:'A phantom blade. Slip past a guard, strike true, and vanish before the answer.',symbol:'☽'}
};
const rawCards={
bastion:[['BA-A01','attack',2,3,0,'Slam','Deal 3 damage. Draw 1 if at full health.'],['BA-A02','attack',3,4,0,'Earthquake','Deal 4 damage to the foe.'],['BA-A03','attack',1,2,0,'Stone Fist','Deal 2 damage.'],['BA-A04','attack',4,6,0,'Boulder','Deal 6 damage.'],['BA-D01','defense',0,0,4,'Shield Wall','Gain 4 Guard this turn.'],['BA-D02','defense',2,0,5,'Iron Skin','Gain 5 permanent armor. Discard 1.'],['BA-D03','defense',1,0,2,'Stand Firm','Gain 2 Guard.'],['BA-D04','defense',2,0,3,'Taunt','Gain 3 Guard. Stun the foe.'],['BA-U01','other',2,0,0,'Search','Draw 1 card.'],['BA-U02','other',3,0,0,'Repair','Restore 6 vitality.'],['BA-U03','other',1,0,0,'Rally','Draw 1. Gain 1 Vigor.'],['BA-U04','other',2,0,0,'Loot','Find 5 gold.']],
wither:[['WI-A01','attack',2,3,0,'Fireball','Deal 3 damage. Apply 1 Burn.'],['WI-A02','attack',3,4,0,'Inferno','Deal 4 damage. Apply 3 Burn.'],['WI-A03','attack',1,2,0,'Ember','Deal 2 damage. Apply 1 Burn.'],['WI-A04','attack',4,5,0,'Pyre','Deal 5 damage and scorch for 1.'],['WI-D01','defense',2,0,3,'Scorch Shield','Gain 3 Guard. Resist Burn.'],['WI-D02','defense',1,0,1,'Heat Resist','Gain 1 Guard and Vigor.'],['WI-D03','defense',2,0,4,'Smoke','Gain 4 Guard. Resist Burn.'],['WI-D04','defense',3,0,2,'Endure','Gain 2 Guard. Restore 2 vitality.'],['WI-U01','other',2,0,0,'Poison','Apply 2 Poison.'],['WI-U02','other',3,0,0,'Burn','Deal 3 damage. Apply 2 Burn.'],['WI-U03','other',1,0,0,'Slow','Draw 1. Stun the foe.'],['WI-U04','other',2,0,0,'Wither','Restore 5 vitality. Weaken the foe.']],
shroud:[['SH-A01','attack',2,4,0,'Backstab','Deal 4 damage. +1 against no Guard.'],['SH-A02','attack',3,5,0,'Shadow Bolt','Deal 5 damage, bypass Guard.'],['SH-A03','attack',1,3,0,'Silence','Deal 3 damage.'],['SH-A04','attack',3,6,0,'Vengeance','Deal 6 damage. Gain 2 Vigor.'],['SH-D01','defense',3,0,2,'Blur','Gain 2 Guard. Evade next blow.'],['SH-D02','defense',2,0,3,'Step','Gain 3 Guard and Vigor.'],['SH-D03','defense',1,0,1,'Evasion','Gain 1 Guard. Evade the foe.'],['SH-D04','defense',2,0,4,'Phantom','Gain 4 Guard. Evade next blow.'],['SH-U01','other',1,0,0,'Dash','Gain 1 Vigor and evade.'],['SH-U02','other',2,0,0,'Teleport','Deal 2 damage. Evade.'],['SH-U03','other',2,0,0,'Hide','Gain 1 Guard. Draw 2 cards.'],['SH-U04','other',3,0,0,'Assassinate','Deal 8 unblockable damage.']]
};
const cards={};Object.keys(rawCards).forEach(cl=>rawCards[cl].forEach(r=>cards[r[0]]={id:r[0],type:r[1],cost:r[2],atk:r[3],def:r[4],name:r[5],desc:r[6],cls:cl}));
const rooms=[['A Hollow Crossing','The Sundered Gate'],['The Rootbound Chapel','The Sundered Gate'],['A Crown of Thorns','The Sundered Gate'],['THE GATEKEEPER','BOSS · THE SUNDERED GATE'],['The Drowned Gallery','The Blackened Choir'],['The Red Reliquary','The Blackened Choir'],['The Bloodless Court','The Blackened Choir'],['THE CORRUPTED','BOSS · THE BLACKENED CHOIR'],['The Ashen Stair','The Void March'],['The Nameless Choir','The Void March'],['The Last Vigil','The Void March'],['THE VOID LORD','BOSS · THE VOID MARCH']];
const enemyNames=[['Hollow Goblin','Ash Rat','Mire Wretch'],['Vampire Knight','Grave Ogre','Blood Cultist'],['Void Warrior','Rift Acolyte','Hollow Stalker']];
const heroSvg=(cl,pose='idle')=>{let color=classes[cl].color;let weapon=cl==='bastion'?'<path d="M83 95L122 44l6 5-37 52" stroke="#c2a66c" stroke-width="5"/><path d="M113 46l16-15 2 21-11 7" fill="#d5c8a4"/>':cl==='wither'?'<path d="M91 100Q112 77 116 36" stroke="#795447" stroke-width="6"/><path d="M110 41q-17-10-4-24 17 2 8 19 8-21 18-11-3 18-20 18" fill="#d67b4f"/>':'<path d="M87 100L123 40" stroke="#b2a1cb" stroke-width="3"/><path d="M117 43l10-17 4 19-8 8" fill="#dfd5ed"/>';let shift=pose==='attack'?'translate(7,-2)':pose==='hit'?'translate(-4,1)':'';return `<svg viewBox="0 0 180 210" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="cape${cl}" x2="0" y2="1"><stop stop-color="${color}" stop-opacity=".72"/><stop offset="1" stop-color="#161923"/></linearGradient></defs><g transform="${shift}"><path d="M89 58Q61 69 48 160l39-17 43 17Q124 77 98 57Z" fill="url(#cape${cl})" stroke="#11141a" stroke-width="4"/><path d="M73 94l-11 54 28 6 9-57" fill="#30323a"/><path d="M99 92l18 57 19-7-20-57" fill="#282a32"/><path d="M64 157l-4 13 35 1 2-14M112 153l3 17 34-2-7-13" fill="#171920"/><path d="M75 57Q85 43 103 56l8 32-23 15-24-21Z" fill="#a4846b"/><path d="M72 55Q72 29 94 28q21 3 19 30l-14-9-22 14" fill="#1a1d25" stroke="${color}" stroke-width="2"/><path d="M78 69l14 3 12-4" stroke="#dbc8a6" stroke-width="2"/><path d="M79 84l15 5 15-8" fill="none" stroke="${color}" stroke-width="2"/><path d="M72 79l-20 34 14 8 21-28M112 82l22 19-8 12-24-16" fill="#32343d" stroke="#171920" stroke-width="3"/>${weapon}</g></svg>`};
const enemySvg=(kind,boss=false)=>{let c=boss?'#827080':'#90806b';let horns= boss?'<path d="M64 81L40 40l30 23M115 63l28-30-12 46" fill="#554657" stroke="#b59a8f" stroke-width="3"/>':'<path d="M66 78L45 49l31 16M115 66l20-32-5 43" fill="#665d51" stroke="#ac9c83" stroke-width="3"/>';let body=boss?'<path d="M54 78Q86 52 126 77l25 91-45 18-61-19Z" fill="#292632" stroke="#a78e7c" stroke-width="4"/><path d="M67 104l21 14 28-13-8 42-36-2Z" fill="#52404c"/>':'<path d="M60 83Q82 65 116 79l20 76-41 19-47-20Z" fill="#554c43" stroke="#a2957c" stroke-width="3"/><path d="M69 105l17 13 24-12-8 37-31-3Z" fill="#72614f"/>';return `<svg viewBox="0 0 180 210" xmlns="http://www.w3.org/2000/svg"><defs><radialGradient id="eye"><stop stop-color="#fff3ae"/><stop offset=".4" stop-color="#ee7846"/><stop offset="1" stop-color="#8d302d"/></radialGradient></defs><g>${horns}${body}<path d="M67 71Q84 46 117 68l8 37-31 20-34-21Z" fill="${c}" stroke="#18171d" stroke-width="4"/><path d="M70 78l20 8 19-10" fill="none" stroke="#484047" stroke-width="4"/><ellipse cx="78" cy="91" rx="5" ry="3" fill="url(#eye)"/><ellipse cx="106" cy="91" rx="5" ry="3" fill="url(#eye)"/><path d="M80 111l12-5 13 6-12 5Z" fill="#20171a"/><path d="M58 100l-25 42 12 8 28-32M121 100l29 36-12 11-33-30" fill="#65564a" stroke="#27252a" stroke-width="5"/><path d="M57 153l-5 29 22 1 7-26M108 154l10 27 23-5-13-30" fill="#26252b" stroke="#111319" stroke-width="4"/><path d="M41 187l38-1m39 1 27-5" stroke="#b3986c" stroke-width="5"/></g></svg>`};
function frameSvg(cl,type,back=false){let palettes={bastion:['#7e9cac','#182a36','#bd9d61'],wither:['#bc765c','#35211e','#cc9a57'],shroud:['#a68bc1','#251d33','#b49a73']};let [a,b,g]=palettes[cl];if(back)return `<svg class="back-card" viewBox="0 0 150 208" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="back${cl}" x2="1" y2="1"><stop stop-color="${b}"/><stop offset="1" stop-color="#0c0e14"/></linearGradient></defs><path d="M8 2h134l6 6v192l-6 6H8l-6-6V8Z" fill="url(#back${cl})" stroke="${g}" stroke-width="2"/><path d="M14 9h122v190H14z" fill="none" stroke="${a}"/><path d="M75 31l42 73-42 73-42-73z" fill="none" stroke="${g}" stroke-width="2"/><path d="M75 53l29 51-29 51-29-51z" fill="none" stroke="${a}"/><circle cx="75" cy="104" r="17" fill="#11131a" stroke="${g}"/><text x="75" y="112" fill="${a}" text-anchor="middle" font-size="23" font-family="serif">${classes[cl].symbol}</text><path d="M18 20h14M18 20v14M132 20h-14m14 0v14M18 188h14m-14 0v-14m114 14h-14m14 0v-14" stroke="${g}" stroke-width="2"/><text x="75" y="186" text-anchor="middle" fill="${g}" font-size="6" letter-spacing="2">AETHELGARD</text></svg>`;let [accent,bg]=type==='attack'?['#d76855','#301b1b']:type==='defense'?['#7ca9c7','#182630']:['#aa84c7','#271c30'];return `<svg class="card-svg" viewBox="0 0 154 201" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="cg${cl}${type}" x2="0" y2="1"><stop stop-color="#28242a"/><stop offset="1" stop-color="#111319"/></linearGradient></defs><path d="M9 1h136l8 8v183l-7 8H8l-7-8V9Z" fill="url(#cg${cl}${type})" stroke="${accent}" stroke-width="1.6"/><path d="M7 6h140v189H7z" fill="none" stroke="${g}" stroke-opacity=".65"/><path d="M12 34h130M12 162h130" stroke="${accent}" stroke-opacity=".58"/><path d="M10 10h13M10 10v13m134-13h-13m13 0v13M10 191h13m-13 0v-13m134 13h-13m13 0v-13" stroke="${g}" stroke-width="1.3"/><path d="M77 52l4 5 6-1-2 6 3 5-7 1-4 5-4-5-6-1 3-5-2-6 6 1z" fill="${accent}" opacity=".35"/></svg>`}
const state={cl:null,hp:0,maxHp:0,energy:0,maxEnergy:3,block:0,armor:0,gold:0,floor:1,room:0,deck:[],hand:[],discard:[],draw:[],inventory:{},enemy:null,won:false,over:false,phase:'choose',evade:false,stun:false,weak:0,burnResist:false,roomNo:0,turn:0};
const $=id=>document.getElementById(id);const roman=['I','II','III'];
function shuffle(a){for(let i=a.length-1;i>0;i--){let j=Math.random()*(i+1)|0;[a[i],a[j]]=[a[j],a[i]]}return a}
function costOf(id){let n=state.inventory[id]||0;return Math.min(50,n>=3?10+(n-3):1+n)}
function log(msg,t=''){let el=document.createElement('div');el.className='toast '+t;el.textContent=msg;$('log').prepend(el);setTimeout(()=>el.remove(),2800)}
function start(cl){state.cl=cl;state.maxHp=classes[cl].start;state.hp=state.maxHp;state.maxEnergy=classes[cl].energy;state.energy=state.maxEnergy;state.gold=0;state.floor=1;state.room=0;state.block=0;state.armor=0;state.inventory={};state.deck=rawCards[cl].map(x=>x[0]);state.hand=[];state.discard=[];state.draw=shuffle([...state.deck]);state.phase='combat';state.over=false;state.won=false;state.roomNo=0;state.evade=false;state.turn=0; $('overlay').hidden=true;$('gameRoot').hidden=false;makeEnemy();drawUp();render();log(`${classes[cl].name} enters the Hollow.`,'good')}
function makeEnemy(){let index=state.room%4;let floor=state.floor;let boss=index===3;let hp=boss?(floor===3?40:80):[20,30,40][floor-1];let dmg=boss?15:[3,5,8][floor-1];let name=boss?['The Gatekeeper','The Corrupted','The Void Lord'][floor-1]:enemyNames[floor-1][Math.floor(Math.random()*3)];state.enemy={name,hp,max:hp,dmg,defense:boss&&floor===1?5:0,burn:0,poison:0,boss,floor,dead:false};state.roomNo++}
function drawCard(){if(!state.draw.length){state.draw=shuffle(state.discard.splice(0));if(!state.draw.length)return}let c=state.draw.pop();state.hand.push(c)}function drawUp(n=4){while(state.hand.length<n)drawCard()}
function render(){if(!state.cl)return;let c=classes[state.cl],e=state.enemy; $('goldTop').textContent=state.gold;$('goldSide').textContent=state.gold+' ◈';$('floorTop').textContent=roman[state.floor-1];$('roomTop').textContent=String((state.room%4)+1).padStart(2,'0')+' / 04';$('floorEyebrow').textContent=['THE SUNDERED GATE','THE BLACKENED CHOIR','THE VOID MARCH'][state.floor-1]+' · FLOOR '+roman[state.floor-1];$('roomTitle').textContent=rooms[(state.floor-1)*4+state.room%4][0];$('roomSub').textContent=e.boss?'A presence older than the dark itself stirs.':'Something hungry moves in the gloom.';$('progress').innerHTML=Array.from({length:4},(_,i)=>`<i class="${i<state.room%4?'done':i===state.room%4?'now':''}"></i>`).join('');$('heroPortrait').innerHTML=heroSvg(state.cl);$('heroName').textContent=c.name;$('heroClass').textContent=c.subtitle;$('hpText').textContent=`${Math.max(0,state.hp)} / ${state.maxHp}`;$('hpBar').style.width=Math.max(0,state.hp/state.maxHp*100)+'%';$('blockText').textContent=state.block+(state.armor?` · ${state.armor} ARMOR`:state.armor);$('energyText').textContent=state.energy+' / '+state.maxEnergy;$('energyOrbs').innerHTML=Array.from({length:state.maxEnergy},(_,i)=>`<i class="orb ${i>=state.energy?'empty':''}"></i>`).join('');$('drawCount').textContent=state.draw.length;$('discardCount').textContent=state.discard.length;$('handCount').textContent=state.hand.length+' / 4';$('enemyName').textContent=e.name.toUpperCase();$('enemyHpText').textContent=`${Math.max(0,e.hp)} / ${e.max} HP`; $('enemyBar').style.width=Math.max(0,e.hp/e.max*100)+'%';$('enemyArt').innerHTML=`<div class="ground"></div>${enemySvg(e.name,e.boss)}`;$('roomTag').textContent=e.boss?'FLOOR '+roman[state.floor-1]+' · BOSS':'ENCOUNTER '+String(state.room%4+1).padStart(2,'0');let intent=e.boss&&e.floor===2&&state.energy<3?20:e.dmg;$('intent').innerHTML=`INTENT<br><b>${state.stun?'STUNNED':state.evade?'EVADE READY':'ATTACK · '+intent}</b>`;$('bossAbility').textContent=e.boss?['WARD · 5 GUARD / TURN · FALTERS BELOW 20 HP','RAGE · 20 DAMAGE IF YOUR VIGOR IS LOW','REGEN · RESTORES 10 HP EACH TURN · NO GUARD'][e.floor-1]:'';$('enemyStatus').innerHTML=(e.burn?`<span class="badge">🔥 BURN ${e.burn}</span>`:'')+(e.poison?`<span class="badge poison">☠ POISON ${e.poison}</span>`:'')+(state.stun?'<span class="badge stun">STUN</span>':'');$('heroStatus').textContent=state.armor?'✦ '+state.armor+' ARMOR':'';$('shopBtn').disabled=state.phase==='combat';$('shopNote').textContent=state.phase==='combat'?'Shop opens between encounters':'The Reliquary awaits · cards from 1 gold';$('passBtn').disabled=state.phase!=='combat';$('passBtn').hidden=state.phase!=='combat';$('continueBtn').hidden=state.phase!=='clear';$('continueBtn').textContent=state.room===3&&state.floor===3?'Claim the victory portal →':'Continue journey →';$('hand').innerHTML='';if(state.phase==='combat'){state.hand.forEach((id,i)=>$('hand').appendChild(cardEl(id,i)))}else{$('hand').innerHTML='<div class="card empty">✧ The room is quiet. Gather your breath before descending.</div>'} $('discardBtn').disabled=state.phase!=='combat';$('targetLabel').textContent=e.name.toUpperCase();}
function cardEl(id,index,shop=false){let c=cards[id],el=document.createElement('button');el.className='card '+c.type;el.disabled=state.phase!=='combat'&&!shop||(!shop&&state.energy<c.cost);el.innerHTML=frameSvg(c.cls,c.type)+`<div class="card-content"><div class="card-top"><span class="cost">${c.cost}</span><span class="card-code">${c.id}</span></div><div class="card-title">${c.name}</div><div class="art-placeholder"><span>${c.type==='attack'?'⚔':c.type==='defense'?'⬟':'✧'}</span></div><div class="card-desc">${c.desc}</div><div class="card-stats">${c.atk?`<span class="atk">⚔ ${c.atk}</span>`:''}${c.def?`<span class="def">⬟ ${c.def}</span>`:''}</div><div class="card-type">${c.type} · COMMON</div></div>`;if(!shop)el.onclick=()=>play(index);return el}
function damage(n,{pierce=false}={}){let e=state.enemy;if(e.boss&&e.floor===1&&e.hp>=20&&!pierce){let soak=Math.min(e.defense,n);e.defense-=soak;n-=soak;if(soak)log(`Gatekeeper absorbs ${soak} damage.`)}e.hp-=n;if(n)log(`${e.name} takes ${n} damage.`,'good');$('enemyArt').querySelector('svg')?.classList.add('hit');setTimeout(()=>render(),220);if(e.hp<=0)winEncounter()}
function play(i){if(state.phase!=='combat')return;let id=state.hand[i],c=cards[id];if(state.energy<c.cost)return;state.energy-=c.cost;state.hand.splice(i,1);let e=state.enemy;log(`${c.name} · ${c.cost} Vigor`);if(c.type==='attack'){let n=c.atk;if(c.id==='BA-A01'&&state.hp===state.maxHp)drawCard();if(c.id==='SH-A01'&&e.defense===0)n++;damage(n,{pierce:c.id==='SH-A02'||c.id==='SH-U04'});if(c.id==='WI-A01')e.burn++;if(c.id==='WI-A02')e.burn+=3;if(c.id==='WI-A03')e.burn++;if(c.id==='WI-A04')e.burn++;if(c.id==='SH-A04')state.energy=Math.min(state.maxEnergy,state.energy+2)}else if(c.type==='defense'){state.block+=c.def;if(c.id==='BA-D02'){state.armor+=5;if(state.hand.length)state.discard.push(state.hand.pop())}if(c.id==='BA-D04'||c.id==='WI-U03')state.stun=true;if(c.id==='WI-D01'||c.id==='WI-D03')state.burnResist=true;if(c.id==='WI-D02'||c.id==='SH-D02')state.energy=Math.min(state.maxEnergy,state.energy+1);if(c.id==='WI-D04')state.hp=Math.min(state.maxHp,state.hp+2);if(['SH-D01','SH-D03','SH-D04'].includes(c.id))state.evade=true;if(c.id==='SH-D03')state.skipAttack=true}else{switch(c.id){case'BA-U01':case'BA-U03':case'SH-U03':drawUp(state.hand.length+(c.id==='SH-U03'?2:1));break;case'BA-U02':state.hp=Math.min(state.maxHp,state.hp+6);break;case'BA-U03':state.energy=Math.min(state.maxEnergy,state.energy+1);break;case'BA-U04':state.gold+=5;break;case'WI-U01':e.poison+=2;break;case'WI-U02':damage(3);e.burn+=2;break;case'WI-U03':drawCard();state.stun=true;break;case'WI-U04':state.hp=Math.min(state.maxHp,state.hp+5);e.dmg=Math.max(1,e.dmg-1);break;case'SH-U01':state.energy=Math.min(state.maxEnergy,state.energy+1);state.evade=true;break;case'SH-U02':damage(2);state.evade=true;break;case'SH-U04':damage(8,{pierce:true});break;}}state.discard.push(id);render()}
function enemyTick(){if(state.phase!=='combat')return;let e=state.enemy;state.turn++;if(e.poison){e.hp-=e.poison;log(`Poison deals ${e.poison} damage.`,'good')}if(e.burn){e.hp-=e.burn;log(`Burn deals ${e.burn} damage.`,'good')}if(e.boss&&e.floor===3&&e.hp>0){e.hp=Math.min(e.max,e.hp+10);log('The Void Lord restores 10 vitality.')}if(e.hp<=0){winEncounter();return}if(e.boss&&e.floor===1&&e.hp>=20)e.defense=Math.min(5,e.defense+5);let attack=state.stun?0:(e.boss&&e.floor===2&&state.energy<3?20:e.dmg);if(state.stun){log(`${e.name} is stunned and loses its turn.`);state.stun=false}else if(state.evade||state.skipAttack){log('You slip clear of the enemy strike.','good');state.evade=false;state.skipAttack=false}else{let raw=attack;let reduced=Math.max(0,raw-state.block-state.armor);state.hp-=reduced;if(reduced)log(`You take ${reduced} damage.`,'bad');else log('Your guard turns the blow aside.','good')}state.block=0;state.burnResist=false;if(state.hp<=0){gameOver();return}state.energy=Math.min(state.maxEnergy,state.energy+1);state.hand.forEach(id=>state.discard.push(id));state.hand=[];state.phase='combat';drawUp();render()}
function endTurn(){if(state.phase!=='combat')return;enemyTick();render()}
function winEncounter(){if(state.phase!=='combat')return;state.phase='clear';let e=state.enemy;let reward=e.boss?15:5;state.gold+=reward;state.hand.forEach(id=>state.discard.push(id));state.hand=[];log(`${e.name} falls. +${reward} gold.`,'good');render()}
function nextRoom(){if(state.room===3){if(state.floor===3){state.won=true;result(true);return}state.floor++;state.room=0}else state.room++;state.block=0;state.stun=false;state.evade=false;state.skipAttack=false;state.phase='combat';makeEnemy();state.energy=state.maxEnergy;drawUp();render();log('A new shadow stirs ahead.')}
function gameOver(){state.phase='over';state.over=true;result(false)}function result(win){$('overlay').hidden=false;$('modal').innerHTML=`<div class="modal-head"><div class="result-symbol">${win?'✧':'☠'}</div><div class="eyebrow">${win?'THE VOID RECEDES':'THE DARK CLAIMS ANOTHER'}</div><h2>${win?'AETHELGARD REMEMBERS':'YOUR LIGHT IS SPENT'}</h2><p>${win?'The portal opens. For one impossible moment, dawn touches the world.':'Your descent ends here. The shadows keep what they are given.'}</p></div><div style="text-align:center;color:#b2aa9b;font-size:12px">${win?'Three floors conquered':'Reached Floor '+roman[state.floor-1]+' · '+state.enemy.name+' remains'}</div><button class="action-btn result-btn" onclick="location.reload()">Return to the beginning</button>`}
function shop(){if(state.phase==='combat')return;$('overlay').hidden=false;$('modal').innerHTML=`<button class="modal-close" onclick="closeModal()">×</button><div class="modal-head"><div class="eyebrow">AETHELGARD · WAYFARER'S RELIQUARY</div><h2>Choose What Survives</h2><p>One gold for a card. Repeated cards grow costlier · limit five copies. You carry <b style="color:#e1c780">${state.gold} gold</b>.</p></div><div class="shop-grid" id="shopGrid"></div><div class="modal-footer">A door opens only between battles. Take what you need; the Void waits.</div>`;let grid=$('shopGrid');rawCards[state.cl].forEach(r=>{let id=r[0],count=state.inventory[id]||0;if(count>=5)return;let cost=costOf(id),box=document.createElement('div');box.className='shop-item';box.appendChild(cardEl(id,0,true));let b=document.createElement('button');b.className='buy-btn';b.disabled=state.gold<cost;b.textContent=`◈ ${cost} · ${count} OWNED`;b.onclick=()=>{if(state.gold<cost)return;state.gold-=cost;state.inventory[id]=count+1;state.deck.push(id);b.disabled=state.gold<cost;b.textContent=`◈ ${costOf(id)} · ${state.inventory[id]} OWNED`;renderShopStats();log(`${cards[id].name} joins your deck.`,'good')};box.appendChild(b);grid.appendChild(box)});if(!grid.children.length)grid.innerHTML='<p>Every relic is already yours.</p>'}
function renderShopStats(){let p=$('modal').querySelector('p');if(p)p.innerHTML=`One gold for a card. Repeated cards grow costlier · limit five copies. You carry <b style="color:#e1c780">${state.gold} gold</b>.`;render()}
function closeModal(){ $('overlay').hidden=true;render()}
function deckModal(){let piles=[...new Set(state.deck.map(id=>cards[id].cls))];$('overlay').hidden=false;$('modal').innerHTML=`<button class="modal-close" onclick="closeModal()">×</button><div class="modal-head"><div class="eyebrow">YOUR COLLECTION · ${state.deck.length} CARDS</div><h2>The Three Orders</h2><p>Each order bears its own seal. Your deck contains ${state.deck.length} cards.</p></div><div class="deck-preview">${piles.map(cl=>`<div>${frameSvg(cl,'',true)}<div class="deck-label">${classes[cl].name.toUpperCase()} · ${cl===state.cl?state.deck.length+' CARDS':'SEALED'}</div></div>`).join('')}</div><div class="modal-footer">Starter decks begin with all 12 cards of the chosen order. Added cards join your draw pile.</div>`}
function help(){ $('overlay').hidden=false;$('modal').innerHTML=`<button class="modal-close" onclick="closeModal()">×</button><div class="modal-head"><div class="eyebrow">FIELD NOTES · SURVIVE THE DESCENT</div><h2>How to Play</h2></div><div style="max-width:620px;margin:auto;color:#c0b9ad;font-size:12px;line-height:1.8"><p><b style="color:#e5cc91">Your turn.</b> Play cards from your hand by clicking them. Each costs Vigor; unused cards are discarded when you end your turn. Vigor recovers by 1 each turn. Guard absorbs incoming damage and resets each turn; armor lasts.</p><p><b style="color:#e5cc91">The descent.</b> Clear three encounters and a boss on each floor. Enemies reward 5 gold, bosses 15. After a victory, visit the Reliquary to buy cards, then continue.</p><p><b style="color:#e5cc91">Beware.</b> Burn and poison tick after each turn. Stun cancels an enemy action. The Gatekeeper's ward returns each turn until it falls below 20 vitality. The Corrupted punishes low Vigor. The Void Lord heals each turn.</p></div><div class="modal-footer">Tip: Wither cards stack status effects. Shroud cards can bypass Guard.</div>`}
$('passBtn').onclick=endTurn;$('continueBtn').onclick=nextRoom;$('shopBtn').onclick=shop;$('deckBtn').onclick=deckModal;$('helpBtn').onclick=help;$('discardBtn').onclick=()=>{if(state.phase!=='combat')return;state.discard.push(...state.hand);state.hand=[];render();log('Hand discarded.')};$('targetBtn').onclick=()=>log('The foe is selected.','good');
function menu(){ $('gameRoot').hidden=true;$('overlay').hidden=false;$('modal').innerHTML=`<div class="modal-head" style="margin-bottom:20px"><div class="eyebrow">A ROGUELITE CARD-BATTler · DESCEND INTO DARKNESS</div><h2 style="font-size:31px;letter-spacing:3px">SHADOWS OF AETHELGARD</h2><p>Three floors below the dying light, something waits to be remembered.</p></div><div class="class-grid">${Object.entries(classes).map(([cl,c])=>`<div class="class-option" onclick="start('${cl}')">${heroSvg(cl)}<h3>${c.name}</h3><p>${c.desc}</p><div class="pick">CHOOSE YOUR FATE →</div></div>`).join('')}</div><div class="modal-footer">Choose an order to begin · 12 unique cards in each starter deck · 3 floors · 12 encounters</div>`}
menu();
</script>
</body></html>