Aethelgard
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 24,287 bytes · SHA-256 f92278dd3965
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Aethelgard // Resonance Frontier</title>
<script src="https://cdn.tailwindcss.com"></script><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Press+Start+2P&display=swap" rel="stylesheet">
<style>
:root{--bg:#080a18;--panel:rgba(20,24,48,.72);--line:rgba(148,163,184,.18);--ink:#e7e9ff;--muted:#8c94bb;--cyan:#63e6ff;--violet:#9b8cff}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 15% 10%,#1d2050 0,#0a0c1c 38%,#060712 100%);color:var(--ink);font-family:Inter,system-ui;min-height:100vh}.glass{background:var(--panel);border:1px solid var(--line);box-shadow:0 20px 70px #0005,inset 0 1px #fff0f;border-radius:24px;backdrop-filter:blur(18px)}.pixel{font-family:'Press Start 2P',monospace}.screen{display:none;animation:in .42s ease both}.screen.active{display:block}@keyframes in{from{opacity:0;transform:translateX(22px)}to{opacity:1;transform:none}}@keyframes pulse{50%{transform:scale(1.035);filter:brightness(1.35)}}@keyframes shake{20%{transform:translate(-7px,3px)}40%{transform:translate(6px,-3px)}60%{transform:translate(-3px,2px)}80%{transform:translate(4px,0)}}@keyframes float{50%{transform:translateY(-7px)}}@keyframes drift{from{transform:translateY(20px);opacity:0}30%{opacity:1}to{transform:translateY(-70px);opacity:0}}.active-mon{animation:float 2.4s ease-in-out infinite}.shake{animation:shake .35s ease}.flash{animation:flash .3s ease}@keyframes flash{50%{filter:brightness(2.5) saturate(1.5)}}.hp{height:9px;background:#11152a;border-radius:99px;overflow:hidden}.hp i{display:block;height:100%;border-radius:99px;background:linear-gradient(90deg,#35e19a,#a5e94c);transition:width .5s}.hp i.warn{background:linear-gradient(90deg,#ffd166,#ff8a3d)}.hp i.danger{background:linear-gradient(90deg,#ff5c7a,#db244f)}button{transition:.2s;cursor:pointer}button:hover:not(:disabled){transform:translateY(-2px);filter:brightness(1.15)}button:disabled{opacity:.4;cursor:not-allowed}.btn{border:1px solid #ffffff20;background:#ffffff0d;border-radius:13px;padding:11px 15px;font-weight:700}.primary{background:linear-gradient(135deg,#7066ed,#42b9e8);border:0}.danger{color:#ff8299}.tag{font-size:10px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;border-radius:99px;padding:4px 8px;background:#ffffff12}.node{width:82px;height:82px;border-radius:24px;position:relative}.node:after{content:'';position:absolute;inset:-7px;border:1px solid #ffffff1c;border-radius:30px;transform:rotate(45deg)}.node.done{filter:grayscale(.3);opacity:.7}.trail{height:2px;background:linear-gradient(90deg,#675de9,#47d6e9);opacity:.6}.modal{background:#0d1127ed;backdrop-filter:blur(20px)}svg{overflow:visible}.toast{position:fixed;right:22px;bottom:22px;z-index:20;animation:in .3s ease}.choice{min-height:74px;text-align:left}.choice b{display:block;color:#fff}.choice small{color:var(--muted)}
</style></head>
<body><div id="app" class="max-w-7xl mx-auto p-4 md:p-8">
<header class="flex items-center justify-between mb-7"><div><div class="pixel text-[11px] text-cyan-300 tracking-widest">AETHELGARD</div><div class="text-xs text-slate-400 mt-2">RESONANCE FRONTIER // CONDUCTOR NETWORK</div></div><div id="topStatus" class="hidden md:flex gap-3 text-sm"></div></header>
<section id="menu" class="screen active"><div class="grid lg:grid-cols-[1.2fr_.8fr] gap-6 items-center min-h-[72vh]"><div class="py-10"><div class="tag text-cyan-300 inline-block mb-5">A new frequency awakens</div><h1 class="text-5xl md:text-7xl font-extrabold leading-none tracking-tight">Tune the<br><span class="text-transparent bg-clip-text bg-gradient-to-r from-cyan-300 to-violet-400">unknown.</span></h1><p class="text-slate-400 max-w-lg mt-7 text-lg leading-relaxed">A strategic monster-collection RPG of elemental spirits, forgotten harmonics, and the frontier beyond the Apex Pillar.</p><div class="flex flex-wrap gap-3 mt-9"><button class="btn primary px-7" onclick="startGame()">Enter the frontier</button><button class="btn" onclick="show('dex')">Conductor's Dex</button></div><button class="text-xs text-slate-500 mt-9 hover:text-rose-300" onclick="resetGame()">Reset progress</button></div><div class="glass p-7 relative overflow-hidden"><div class="absolute -right-16 -top-16 w-56 h-56 rounded-full bg-violet-500/20 blur-3xl"></div><div class="text-xs text-slate-400 mb-5">FIELD NOTE 001 // THE FIRST SIGNAL</div><div id="heroSprite" class="h-56 flex items-center justify-center"></div><div class="border-t border-white/10 pt-5"><div class="flex justify-between"><b>Emberkit</b><span class="tag text-orange-300">PYRO • RESONANT</span></div><p class="text-sm text-slate-400 mt-2">“Every pulse is a promise. Every battle, a conversation.”</p></div></div></div></section>
<section id="map" class="screen"><div class="flex flex-wrap items-end justify-between gap-4 mb-5"><div><div class="tag text-cyan-300" id="zoneTag"></div><h2 id="zoneTitle" class="text-3xl font-extrabold mt-2"></h2><p id="zoneDesc" class="text-slate-400 mt-1"></p></div><div class="flex gap-2"><button class="btn" onclick="show('dex')">Dex</button><button class="btn" onclick="openShop()">Camp Shop</button><button class="btn" onclick="show('menu')">Menu</button></div></div><div class="grid lg:grid-cols-[1fr_310px] gap-5"><div class="glass p-6 md:p-10"><div id="mapNodes" class="min-h-[420px] flex flex-col justify-center"></div></div><aside class="space-y-4"><div class="glass p-5"><div class="flex justify-between items-center mb-4"><b>Your Signal</b><span id="shards" class="text-amber-300 font-bold"></span></div><div id="partyMini" class="space-y-3"></div></div><div class="glass p-5"><div class="text-xs text-slate-400 uppercase tracking-widest">Field log</div><div id="log" class="text-sm text-slate-300 mt-3 leading-relaxed"></div></div></aside></div></section>
<section id="battle" class="screen"><div class="flex justify-between items-center mb-4"><button class="btn" onclick="retreat()">← Retreat</button><div class="tag" id="battleKind"></div></div><div class="glass p-5 md:p-8 overflow-hidden"><div class="flex justify-between text-xs text-slate-400 mb-3"><span id="battleZone"></span><span id="turnLabel">YOUR TURN</span></div><div class="grid md:grid-cols-2 gap-8 items-center"><div id="enemyCard"></div><div id="playerCard"></div></div><div id="battleMessage" class="text-center min-h-[34px] mt-5 text-cyan-200 font-semibold"></div><div id="actions" class="mt-4"></div></div></section>
<section id="dex" class="screen"><div class="flex justify-between items-center mb-5"><div><div class="tag text-violet-300">Archive // 018 frequencies</div><h2 class="text-3xl font-extrabold mt-2">Conductor's Dex</h2></div><button class="btn" onclick="show(state.screen==='dex'?'map':'map')">← Return</button></div><div id="dexGrid" class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4"></div></section>
<div id="modal" class="fixed inset-0 hidden z-10 items-center justify-center p-4"><div class="modal glass p-6 max-w-md w-full" id="modalBody"></div></div><div id="toast" class="toast hidden glass px-5 py-3 text-sm"></div></div>
<script>
class Synth{constructor(){this.ctx=null}tone(f=440,d=.1,type='square',vol=.035){try{this.ctx??=new(window.AudioContext||window.webkitAudioContext)();let o=this.ctx.createOscillator(),g=this.ctx.createGain();o.type=type;o.frequency.value=f;g.gain.setValueAtTime(vol,this.ctx.currentTime);g.gain.exponentialRampToValueAtTime(.001,this.ctx.currentTime+d);o.connect(g).connect(this.ctx.destination);o.start();o.stop(this.ctx.currentTime+d)}catch(e){}}ui(){this.tone(640,.06,'sine')}select(){this.tone(420,.08);setTimeout(()=>this.tone(680,.1),70)}hit(el=false){this.tone(el?180:120,.12,'sawtooth');setTimeout(()=>this.tone(el?480:250,.14),55)}level(){[440,550,660,880].forEach((x,i)=>setTimeout(()=>this.tone(x,.12),i*80))}capture(){[660,880,1100].forEach((x,i)=>setTimeout(()=>this.tone(x,.12,'sine'),i*110))}}const audio=new Synth();
const types={Pyro:{strong:['Flora'],weak:['Hydro'],color:'#ff765c'},Hydro:{strong:['Pyro'],weak:['Flora'],color:'#54cfff'},Flora:{strong:['Hydro'],weak:['Pyro'],color:'#75e58b'},Zephyr:{strong:['Earth'],weak:['Volt'],color:'#c19cff'},Earth:{strong:['Volt'],weak:['Zephyr'],color:'#d8ae73'},Volt:{strong:['Zephyr'],weak:['Earth'],color:'#ffe46b'},Cosmos:{strong:[],weak:[],color:'#d9d7ff'}};
const moveSets={Pyro:[['Cinder Peck',38,96,1],['Searline',58,90,5],['Solar Fang',78,86,10],['Inferno Spiral',106,78,15]],Hydro:[['Ripple Jab',38,96,1],['Tide Lash',58,90,5],['Abyss Ray',78,86,10],['Maelstrom',106,78,15]],Flora:[['Vine Snap',38,96,1],['Spore Burst',58,90,5],['Bramble Crown',78,86,10],['Worldroot',106,78,15]],Zephyr:[['Gale Claw',38,96,1],['Crosswind',58,90,5],['Jetstream',78,86,10],['Sky Rend',106,78,15]],Earth:[['Gravel Tack',38,96,1],['Faultline',58,90,5],['Titan Knuckle',78,86,10],['Continental',106,78,15]],Volt:[['Static Nip',38,96,1],['Arc Pulse',58,90,5],['Thunder Edge',78,86,10],['Overcharge',106,78,15]],Cosmos:[['Null Tap',38,96,1],['Orbit Crash',58,90,5],['Event Horizon',78,86,10],['Starfall',106,78,15]]};
const names=[['Emberkit','Pyro'],['Pyrowolf','Pyro'],['Ignisaur','Pyro'],['Droplet','Hydro'],['Geoxus','Hydro'],['Neptunian','Hydro'],['Seedling','Flora'],['Thornback','Flora'],['Bloomgoliath','Flora'],['Sparky','Volt'],['Boltsaber','Volt'],['Stormlord','Volt'],['Gusty','Zephyr'],['Slipstream','Zephyr'],['Zephyrus','Zephyr'],['Pebble','Earth'],['Obsidian','Earth'],['Terrasunder','Earth']];
const dex=names.map((n,i)=>{let tier=i%3,level=tier?([8,15][tier-1]):1,t=n[1],m=moveSets[t];return{id:i+1,name:n[0],type:t,level,base:{hp:48+tier*22+(i%3)*4,attack:30+tier*14+(i%4)*3,defense:29+tier*13+(i%5)*2,speed:28+tier*15+(i%6)*2},evo:tier<2?([8,15][tier]):null,moves:m.map(x=>({name:x[0],power:x[1],accuracy:x[2],element:t,unlock:x[3]})),shape:tier,seen:false}});
const zones=[{name:'Verdant Canopy',levels:'LEVELS 1—5',desc:'Moss-lit ruins where leafsong and high winds mingle.',nodes:[['Wild Resonance','wild'],['Whisper Cache','cache'],['Mara, Trail Conductor','trainer'],['Lumen Spring','heal'],['Canopy Gatekeeper','boss']]},{name:'Sunken Grotto',levels:'LEVELS 5—10',desc:'A drowned cathedral pulsing beneath the glasswater.',nodes:[['Wild Resonance','wild'],['Pearl Cache','cache'],['Orin, Tide Conductor','trainer'],['Aqua Spring','heal'],['Grotto Gatekeeper','boss']]},{name:'Obsidian Crags',levels:'LEVELS 10—15',desc:'Black mountains split by molten harmonic fault lines.',nodes:[['Wild Resonance','wild'],['Core Cache','cache'],['Vela, Crag Conductor','trainer'],['Ember Spring','heal'],['Crag Gatekeeper','boss']]},{name:'Apex Pillar',levels:'LEVEL 15+',desc:'The world-spire, where the first frequency still rings.',nodes:[['Wild Resonance','wild'],['Astral Cache','cache'],['The First Conductor','boss']] }];
let state={screen:'menu',zone:0,node:0,party:[],active:0,shards:120,inv:{basic:5,hyper:1,elixir:3,cleanser:2},unlocked:1,bosses:[],completed:[],log:'Your Aether Prism hums in anticipation.'},battle=null;
function save(){localStorage.setItem('aethelgard-save',JSON.stringify(state))}function load(){try{let x=JSON.parse(localStorage.getItem('aethelgard-save'));if(x)state={...state,...x}}catch(e){}}function resetGame(){if(confirm('Erase your entire Conductor record?')){localStorage.removeItem('aethelgard-save');location.reload()}}function show(id){document.querySelectorAll('.screen').forEach(x=>x.classList.remove('active'));document.getElementById(id).classList.add('active');state.screen=id;if(id==='map')renderMap();if(id==='dex')renderDex();save()}function startGame(){if(!state.party.length){state.party=[makeMon(0,3),makeMon(12,3)];state.log='Emberkit and Gusty synchronize with your tuning device.'}show('map');audio.select()}function makeMon(id,lvl=3){let d=dex[id],max=d.base.hp+lvl*5;return{id:d.id,name:d.name,type:d.type,level:lvl,xp:0,maxHp:max,hp:max,attack:d.base.attack+lvl*3,defense:d.base.defense+lvl*2,speed:d.base.speed+lvl*2,status:null}}function current(){return state.party[state.active]}
function sprite(mon,large=false){let t=types[mon.type],c=t.color;let p={Pyro:'M 35 160 Q 15 105 55 75 Q 32 30 86 12 Q 84 54 125 36 Q 160 64 148 112 Q 166 150 125 170 Z',Hydro:'M 88 8 Q 150 55 145 105 Q 137 165 88 174 Q 37 165 30 110 Q 28 55 88 8 Z',Flora:'M 88 170 Q 50 140 58 96 Q 20 92 10 55 Q 58 45 85 74 Q 108 33 160 35 Q 160 82 119 98 Q 130 142 88 170 Z',Volt:'M 102 5 L 44 86 L 78 88 L 58 170 L 145 70 L 108 70 Z',Zephyr:'M 10 92 Q 80 4 164 44 Q 108 55 95 91 Q 124 118 166 121 Q 87 170 10 92 Z',Earth:'M 26 150 L 43 44 L 92 16 L 143 48 L 154 150 L 92 174 Z',Cosmos:'M 88 10 A 78 78 0 1 0 88 166 A 78 78 0 1 0 88 10 Z'}[mon.type];return `<svg width="${large?210:74}" height="${large?190:74}" viewBox="0 0 176 180"><defs><linearGradient id="g${mon.id}" x1="0" x2="1"><stop stop-color="${c}"/><stop offset="1" stop-color="#fff" stop-opacity=".35"/></linearGradient></defs><path d="${p}" fill="url(#g${mon.id})" stroke="${c}" stroke-width="4"/><circle cx="65" cy="78" r="8" fill="#091020"/><circle cx="116" cy="78" r="8" fill="#091020"/><path d="M68 116 Q88 130 112 114" fill="none" stroke="#091020" stroke-width="5" stroke-linecap="round"/><path d="M${mon.type==='Volt'?'88 0':'88 20'} V${mon.type==='Volt'?'22':'38'}" stroke="#fff" stroke-opacity=".5" stroke-width="3"/></svg>`}
function renderTop(){document.getElementById('topStatus').innerHTML=`<span class="tag text-amber-300">✦ ${state.shards} shards</span><span class="tag text-cyan-300">${state.party.length}/3 resonants</span>`}function renderMap(){let z=zones[state.zone];document.getElementById('zoneTag').textContent=`ZONE 0${state.zone+1} • ${z.levels}`;document.getElementById('zoneTitle').textContent=z.name;document.getElementById('zoneDesc').textContent=z.desc;document.getElementById('shards').textContent=`✦ ${state.shards}`;document.getElementById('log').textContent=state.log;document.getElementById('partyMini').innerHTML=state.party.map((m,i)=>`<div class="flex items-center gap-3 ${i===state.active?'text-white':'text-slate-500'}">${sprite(m)}<div class="flex-1"><div class="flex justify-between text-sm font-bold"><span>${m.name}</span><span>Lv ${m.level}</span></div><div class="hp mt-1"><i class="${m.hp/m.maxHp<.3?'danger':''}" style="width:${m.hp/m.maxHp*100}%"></i></div></div></div>`).join('');document.getElementById('mapNodes').innerHTML=`<div class="flex items-center justify-center flex-wrap gap-5 md:gap-8">${z.nodes.map((n,i)=>`${i?'<div class="trail w-5 md:w-12"></div>':''}<button class="node glass flex flex-col items-center justify-center ${state.completed.includes(state.zone+'-'+i)?'done':''}" onclick="visit(${i})" ${i>state.node?'disabled':''}><span class="text-2xl">${{wild:'◈',cache:'✦',trainer:'♢',heal:'✚',boss:'♜'}[n[1]]}</span><span class="text-[10px] mt-1 text-slate-300">${n[0]}</span></button>`).join('')}</div><div class="text-center mt-16 text-xs text-slate-500">Select an unlocked harmonic node to continue</div>`;renderTop()}
function visit(i){audio.select();let z=zones[state.zone],n=z.nodes[i];state.node=Math.max(state.node,i+1);state.completed.push(state.zone+'-'+i);if(n[1]==='wild')beginBattle('wild');else if(n[1]==='trainer')beginBattle('trainer');else if(n[1]==='boss')beginBattle('boss');else if(n[1]==='heal'){state.party.forEach(m=>m.hp=m.maxHp);state.log='The spring restores every Resonant in your party.';toast('Party restored');renderMap()}else{let gain=35+state.zone*25;state.shards+=gain;let item=state.zone?'hyper':'basic';state.inv[item]++;state.log=`A cache yields ${gain} shards and a ${item==='basic'?'Harmonizer':'Hyper-Conductive'} Prism.`;toast('Cache opened');renderMap()}save()}
function beginBattle(kind){let pool=state.zone===0?[0,3,6,12,15]:state.zone===1?[3,4,15,16]:state.zone===2?[0,9,10,1]:[2,5,8,11];let id=kind==='boss'?[2,5,11,17][state.zone]:pool[Math.floor(Math.random()*pool.length)];let lv=kind==='boss'?[7,11,16,20][state.zone]:Math.max(2,state.zone*4+Math.floor(Math.random()*4));battle={kind,enemy:makeMon(id,lv),wild:kind==='wild',turn:'player',over:false};show('battle');renderBattle()}
function mult(a,d){if(types[a].strong.includes(d))return 1.7;if(types[a].weak.includes(d))return .55;return 1}function renderBattle(){let e=battle.enemy,p=current(),ep=e.hp/e.maxHp*100,pp=p.hp/p.maxHp*100;document.getElementById('battleKind').textContent=battle.kind==='wild'?'WILD FREQUENCY':battle.kind==='trainer'?'CONDUCTOR DUEL':'GATEKEEPER // BOSS';document.getElementById('battleZone').textContent=zones[state.zone].name;document.getElementById('turnLabel').textContent=battle.turn==='player'?'YOUR TURN':'OPPONENT TUNING';document.getElementById('enemyCard').innerHTML=card(e,ep,true);document.getElementById('playerCard').innerHTML=card(p,pp,false);document.getElementById('actions').innerHTML=battle.over?'':battle.turn==='player'?actions():`<div class="text-center text-slate-400 animate-pulse">${e.name} is finding your frequency…</div>`}
function card(m,pct,enemy){return `<div class="${!enemy?'text-right':''}"><div class="flex ${enemy?'':'justify-end'} items-center gap-3"><div><b class="text-xl">${m.name}</b><div class="text-xs text-slate-400">${m.type} • LEVEL ${m.level}</div></div><span class="tag">${Math.ceil(m.hp)}/${m.maxHp}</span></div><div class="hp mt-3"><i class="${pct<30?'danger':pct<55?'warn':''}" style="width:${Math.max(0,pct)}%"></i></div><div class="flex ${enemy?'':'justify-end'} mt-1 text-xs text-slate-500">${m.status?`STATUS: ${m.status}`:'STABLE'}</div><div class="${enemy?'':'text-right'} mt-5 ${!enemy?'active-mon':''}">${sprite(m,true)}</div></div>`}
function actions(){let p=current(),d=dex[p.id-1],moves=d.moves.filter(x=>x.unlock<=p.level);return `<div class="grid sm:grid-cols-2 gap-3">${moves.map((m,i)=>`<button class="btn choice" onclick="attack(${i})"><b>${m.name} <span class="float-right text-xs text-cyan-300">${m.element}</span></b><small>Power ${m.power} • Accuracy ${m.accuracy}%</small></button>`).join('')}</div><div class="grid grid-cols-3 gap-3 mt-3"><button class="btn" onclick="prism('basic')">◈ Prism <small>(${state.inv.basic})</small></button><button class="btn" onclick="useItem('elixir')">✚ Elixir <small>(${state.inv.elixir})</small></button><button class="btn" onclick="exchange()">⇄ Exchange</button></div>`}
function attack(i){if(battle.turn!=='player')return;let p=current(),e=battle.enemy,m=dex[p.id-1].moves.filter(x=>x.unlock<=p.level)[i];if(Math.random()*100>m.accuracy){message(`${p.name} misses the frequency.`);return enemyTurn()}let crit=Math.random()<.1,dam=(((2*p.level/5+2)*m.power*(p.attack/e.defense))/50+2)*mult(m.element,e.type)*(0.85+Math.random()*.15)*(crit?1.5:1);e.hp=Math.max(0,Math.floor(dam));audio.hit(mult(m.element,e.type)>1);document.getElementById('enemyCard').classList.add('flash');setTimeout(()=>document.getElementById('enemyCard').classList.remove('flash'),300);message(`${p.name} uses ${m.name}! ${crit?'CRITICAL! ':''}${mult(m.element,e.type)>1?'Super effective! ':''}${Math.floor(dam)} damage.`);if(e.hp<=0)return winBattle();enemyTurn()}
function enemyTurn(){battle.turn='enemy';renderBattle();setTimeout(()=>{let e=battle.enemy,p=current(),m=dex[e.id-1].moves.filter(x=>x.unlock<=e.level).slice(-1)[0];let dam=Math.max(1,Math.floor((((2*e.level/5+2)*m.power*(e.attack/p.defense))/50+2)*mult(m.element,p.type)*(.85+Math.random()*.15)));p.hp=Math.max(0,p.hp-dam);if(Math.random()<.12)p.status='Dissonant';message(`${e.name} answers with ${m.name}, dealing ${dam} damage.`);if(p.hp<=0){let next=state.party.findIndex(x=>x.hp>0);if(next>=0){state.active=next;message(`${p.name} fades. ${current().name} takes the field.`)}else{state.party.forEach(x=>x.hp=x.maxHp);battle.over=true;message('Your whole signal collapses. The camp restores you.');setTimeout(()=>show('map'),1500)}}battle.turn='player';renderBattle();save()},650)}
function message(x){document.getElementById('battleMessage').textContent=x}function winBattle(){battle.over=true;let gain=25+state.zone*30+(battle.kind==='boss'?100:0);state.shards+=gain;let p=current(),xp=35+state.zone*25+(battle.kind==='boss'?100:0);p.xp+=xp;while(p.xp>=p.level*45){p.xp-=p.level*45;p.level++;p.maxHp+=6;p.hp=p.maxHp;p.attack+=3;p.defense+=2;p.speed+=2;audio.level();message(`${p.name} resonates upward! Level ${p.level}.`)}if(battle.kind==='boss'){state.bosses.push(state.zone);state.unlocked=Math.min(4,state.zone+2);state.zone=Math.min(3,state.zone+1);state.node=0}message(`${battle.enemy.name} is silenced. +${gain} shards, +${xp} XP.`);audio.level();renderBattle();setTimeout(()=>show('map'),1700);save()}
function prism(t){if(!battle.wild||!state.inv[t]){message('A Prism cannot harmonize with this target.');return}state.inv[t]--;let rate=(t==='hyper'?.72:.38)*(1-battle.enemy.hp/battle.enemy.maxHp+.2)*(battle.enemy.status?.1.25:1);if(Math.random()<rate){let m=makeMon(battle.enemy.id,battle.enemy.level);m.hp=Math.max(1,battle.enemy.hp);if(state.party.length<3)state.party.push(m);state.log=`${m.name} joins your frequency.`;audio.capture();message(`Capture successful! ${m.name} joins the party.`);battle.over=true;setTimeout(()=>show('map'),1400)}else{message('The Prism fractures against the wild signal.');enemyTurn()}renderBattle();save()}
function exchange(){let next=(state.active+1)%state.party.length;if(state.party[next].hp<=0){message('No conscious Resonant available.');return}state.active=next;message(`You exchange to ${current().name}.`);enemyTurn()}
function useItem(x){if(!state.inv[x])return;state.inv[x]--;if(x==='elixir'){current().hp=Math.min(current().maxHp,current().hp+Math.floor(current().maxHp*.5));message('Core Restorer restores 50% HP.')}renderBattle();save()}
function retreat(){if(battle&&battle.kind==='wild'){show('map')}else{message('A Conductor duel cannot be abandoned.')}}
function openShop(){let items=[['basic','Harmonizer Prism',25,'Capture wild Resonants'],['hyper','Hyper-Conductive Prism',90,'High capture rate'],['elixir','Core Restorer',45,'Restore 50% HP'],['cleanser','Resonance Elixir',35,'Cleanse status']];document.getElementById('modal').classList.remove('hidden');document.getElementById('modal').classList.add('flex');document.getElementById('modalBody').innerHTML=`<div class="flex justify-between"><h3 class="text-2xl font-bold">Camp Shop</h3><button onclick="closeModal()">×</button></div><p class="text-slate-400 text-sm mt-2">Spend shards, strengthen the signal.</p><div class="space-y-2 mt-5">${items.map(x=>`<button class="btn w-full flex justify-between items-center" onclick="buy('${x[0]}',${x[2]})"><span class="text-left"><b>${x[1]}</b><small class="block text-slate-400">${x[3]}</small></span><strong class="text-amber-300">✦ ${x[2]}</strong></button>`).join('')}</div>`}function buy(x,c){if(state.shards<c)return toast('Not enough shards');state.shards-=c;state.inv[x]++;toast('Purchase added to inventory');renderMap();save()}function closeModal(){document.getElementById('modal').classList.add('hidden');document.getElementById('modal').classList.remove('flex')}function toast(s){let x=document.getElementById('toast');x.textContent=s;x.classList.remove('hidden');setTimeout(()=>x.classList.add('hidden'),2200)}
function renderDex(){document.getElementById('dexGrid').innerHTML=dex.map(d=>`<article class="glass p-4 flex gap-4 items-center" style="border-color:${types[d.type].color}55"><div>${sprite({...d,hp:1,maxHp:1})}</div><div class="min-w-0"><div class="flex items-center gap-2"><b>${d.name}</b><span class="tag" style="color:${types[d.type].color}">${d.type}</span></div><div class="text-xs text-slate-400 mt-1">Tier ${d.shape+1} • Evolves ${d.evo?'at Lv '+d.evo:'—'}</div><div class="text-xs text-slate-500 mt-2">HP ${d.base.hp} · ATK ${d.base.attack} · DEF ${d.base.defense} · SPD ${d.base.speed}</div><div class="text-[10px] text-slate-400 mt-2">${d.moves.map(m=>m.name).join(' • ')}</div></div></article>`).join('')}
load();document.getElementById('heroSprite').innerHTML=sprite(makeMon(0,3),true);if(state.screen==='map'&&state.party.length)show('map');else renderTop();document.addEventListener('mouseover',e=>{if(e.target.matches('button'))audio.ui()});
</script></body></html>