← Aethelgard results

Aethelgard

Gemma 4 31b · composer

Initial view of Aethelgard

Reviews & generation details

Reviews

Human review · Jul 16, 2026 · rubric 1

Historical path-based review. Association with these exact bytes is unverified.

Visual design
2 / 5
UX / feel
3 / 5
Works
2 / 5
Prompt spirit
3 / 5
Delight
2 / 5
good sound effects; broken game loop (enemy keeps attacking, not waiting for turn); poor/boring design with basic fonts

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 48,389 bytes · SHA-256 0c13c72f02f8
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Aethelgard: Resonant Harmony</title>
<script src="https://cdn.tailwindcss.com/"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&amp;family=Press+Start+2P&amp;display=swap">
<style>
/* ── css: styles ── */

:root {
    --bg-deep: #0f172a;
    --glass: rgba(30, 41, 59, 0.7);
    --glass-border: rgba(255, 255, 255, 0.1);
    --pyro: #ef4444;
    --hydro: #3b82f6;
    --flora: #22c55e;
    --zephyr: #a855f7;
    --earth: #f59e0b;
    --volt: #eab308;
    --cosmos: #94a3b8;
}

body {
    font-family: 'Inter', sans-serif;
    background-color: var(--bg-deep);
    color: white;
    overflow: hidden;
    user-select: none;
}

.retro-font {
    font-family: 'Press Start 2P', cursive;
}

.glass-panel {
    background: var(--glass);
    backdrop-filter: blur(12px);
    border: 1px solid var(--glass-border);
    box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
}

.neon-border-pyro { border-color: var(--pyro); box-shadow: 0 0 10px var(--pyro); }
.neon-border-hydro { border-color: var(--hydro); box-shadow: 0 0 10px var(--hydro); }
.neon-border-flora { border-color: var(--flora); box-shadow: 0 0 10px var(--flora); }
.neon-border-zephyr { border-color: var(--zephyr); box-shadow: 0 0 10px var(--zephyr); }
.neon-border-earth { border-color: var(--earth); box-shadow: 0 0 10px var(--earth); }
.neon-border-volt { border-color: var(--volt); box-shadow: 0 0 10px var(--volt); }
.neon-border-cosmos { border-color: var(--cosmos); box-shadow: 0 0 10px var(--cosmos); }

.hp-bar-fill {
    transition: width 0.5s ease-out, background-color 0.5s ease;
}

@keyframes bounce-idle {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-10px); }
}
.animate-bounce-idle {
    animation: bounce-idle 2s ease-in-out infinite;
}

@keyframes shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-5px); }
    75% { transform: translateX(5px); }
}
.animate-shake {
    animation: shake 0.2s ease-in-out 0s 2;
}

.screen-transition {
    transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

.node-active {
    filter: drop-shadow(0 0 8px white);
    cursor: pointer;
}
.node-locked {
    opacity: 0.5;
    cursor: not-allowed;
}
</style>
</head>
<body>
<div id="app" class="relative w-full h-screen overflow-hidden flex items-center justify-center p-4">
    <!-- Main Menu -->
    <div id="screen-menu" class="screen-transition absolute inset-0 z-20 flex flex-col items-center justify-center bg-slate-950">
        <div class="text-center mb-12">
            <h1 class="retro-font text-4xl md:text-6xl text-white mb-4 tracking-tighter drop-shadow-[0_0_15px_rgba(255,255,255,0.5)]">AETHELGARD</h1>
            <p class="text-slate-400 font-light tracking-widest uppercase">Resonant Harmony</p>
        </div>
        <div class="flex flex-col gap-4 w-64">
            <button id="btn-start" class="glass-panel py-3 px-6 rounded-lg hover:bg-white/10 transition-all text-center font-semibold uppercase tracking-wider">Begin Journey</button>
            <button id="btn-load" class="glass-panel py-3 px-6 rounded-lg hover:bg-white/10 transition-all text-center font-semibold uppercase tracking-wider">Continue</button>
            <button id="btn-reset" class="glass-panel py-3 px-6 rounded-lg hover:bg-white/10 transition-all text-center font-semibold uppercase tracking-wider text-red-400">Wipe Memory</button>
        </div>
        <div class="absolute bottom-8 text-slate-600 text-xs uppercase tracking-widest">Capture the Resonance</div>
    </div>

    <!-- Overworld Map -->
    <div id="screen-map" class="screen-transition hidden absolute inset-0 z-10 flex flex-col p-6">
        <div class="flex justify-between items-center mb-8">
            <div class="glass-panel px-4 py-2 rounded-full flex items-center gap-4">
                <span class="text-xs uppercase text-slate-400">Shards:</span>
                <span id="map-shards" class="font-bold text-yellow-400">0</span>
            </div>
            <h2 id="zone-title" class="retro-font text-lg text-white">The Verdant Canopy</h2>
            <button id="btn-open-dex" class="glass-panel px-4 py-2 rounded-full text-xs uppercase font-semibold hover:bg-white/10">Dex</button>
        </div>
        
        <div id="map-canvas-container" class="flex-1 relative overflow-hidden rounded-3xl border border-white/10 bg-slate-900/50">
            <svg id="map-svg" class="w-full h-full"></svg>
        </div>

        <div class="flex justify-center gap-4 mt-8">
            <button id="btn-open-shop" class="glass-panel px-8 py-3 rounded-xl font-semibold uppercase text-sm hover:bg-white/10">Camp Shop</button>
            <button id="btn-open-party" class="glass-panel px-8 py-3 rounded-xl font-semibold uppercase text-sm hover:bg-white/10">Resonant Team</button>
        </div>
    </div>

    <!-- Battle Screen -->
    <div id="screen-battle" class="screen-transition hidden absolute inset-0 z-30 flex flex-col bg-slate-950 p-6">
        <div class="flex-1 flex flex-col justify-between relative">
            <!-- Enemy Side -->
            <div class="flex justify-end items-start gap-6">
                <div id="enemy-stats" class="glass-panel p-4 rounded-2xl w-64">
                    <div class="flex justify-between items-center mb-2">
                        <span id="enemy-name" class="font-bold truncate">Enemy</span>
                        <span id="enemy-lv" class="text-xs text-slate-400">Lv 5</span>
                    </div>
                    <div class="w-full h-3 bg-slate-800 rounded-full overflow-hidden">
                        <div id="enemy-hp-bar" class="hp-bar-fill h-full w-full bg-green-500"></div>
                    </div>
                    <div class="text-right text-[10px] mt-1 text-slate-500 uppercase" id="enemy-hp-text">100 / 100</div>
                </div>
                <div id="enemy-sprite-container" class="w-48 h-48 animate-bounce-idle flex items-center justify-center">
                    <!-- SVG goes here -->
                </div>
            </div>

            <!-- Player Side -->
            <div class="flex justify-start items-end gap-6">
                <div id="player-sprite-container" class="w-48 h-48 animate-bounce-idle flex items-center justify-center">
                    <!-- SVG goes here -->
                </div>
                <div id="player-stats" class="glass-panel p-4 rounded-2xl w-64">
                    <div class="flex justify-between items-center mb-2">
                        <span id="player-name" class="font-bold truncate">Ally</span>
                        <span id="player-lv" class="text-xs text-slate-400">Lv 5</span>
                    </div>
                    <div class="w-full h-3 bg-slate-800 rounded-full overflow-hidden">
                        <div id="player-hp-bar" class="hp-bar-fill h-full w-full bg-green-500"></div>
                    </div>
                    <div class="text-right text-[10px] mt-1 text-slate-500 uppercase" id="player-hp-text">100 / 100</div>
                </div>
            </div>
        </div>

        <!-- Battle Console -->
        <div id="battle-console" class="glass-panel h-48 rounded-t-3xl p-6 flex flex-col gap-4 relative">
            <div id="battle-text" class="text-lg font-medium text-center py-2">A wild Resonant appeared!</div>
            <div id="battle-actions" class="grid grid-cols-2 gap-4 flex-1">
                <!-- Action buttons injected here -->
            </div>
            <!-- Battle Overlay / Transition -->
            <div id="battle-overlay" class="absolute inset-0 bg-black/60 hidden flex items-center justify-center rounded-t-3xl">
                <div id="battle-message" class="retro-font text-xl text-center px-8">...</div>
            </div>
        </div>
    </div>

    <!-- Modal Overlay -->
    <div id="modal-overlay" class="hidden absolute inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-6">
        <div id="modal-content" class="glass-panel max-w-2xl w-full max-h-[80vh] overflow-y-auto rounded-3xl p-8 relative">
            <button id="btn-close-modal" class="absolute top-4 right-4 text-slate-400 hover:text-white text-2xl">×</button>
            <div id="modal-body"></div>
        </div>
    </div>
</div>
<script>
'use strict';

// ── class: Synth ──
class Synth {
    constructor() {
        this.ctx = new (window.AudioContext || window.webkitAudioContext)();
    }

    async play(type) {
        const osc = this.ctx.createOscillator();
        const gain = this.ctx.createGain();
        osc.connect(gain);
        gain.connect(this.ctx.destination);

        const now = this.ctx.currentTime;

        switch (type) {
            case 'hover':
                osc.type = 'sine';
                osc.frequency.setValueAtTime(440, now);
                osc.frequency.exponentialRampToValueAtTime(880, now + 0.1);
                gain.gain.setValueAtTime(0.1, now);
                gain.gain.exponentialRampToValueAtTime(0.01, now + 0.1);
                osc.start();
                osc.stop(now + 0.1);
                break;
            case 'select':
                osc.type = 'square';
                osc.frequency.setValueAtTime(200, now);
                osc.frequency.exponentialRampToValueAtTime(600, now + 0.1);
                gain.gain.setValueAtTime(0.1, now);
                gain.gain.exponentialRampToValueAtTime(0.01, now + 0.1);
                osc.start();
                osc.stop(now + 0.1);
                break;
            case 'hit':
                osc.type = 'sawtooth';
                osc.frequency.setValueAtTime(150, now);
                osc.frequency.linearRampToValueAtTime(50, now + 0.2);
                gain.gain.setValueAtTime(0.2, now);
                gain.gain.linearRampToValueAtTime(0, now + 0.2);
                osc.start();
                osc.stop(now + 0.2);
                break;
            case 'critical':
                osc.type = 'square';
                osc.frequency.setValueAtTime(800, now);
                osc.frequency.exponentialRampToValueAtTime(1200, now + 0.1);
                osc.frequency.exponentialRampToValueAtTime(800, now + 0.2);
                gain.gain.setValueAtTime(0.2, now);
                gain.gain.linearRampToValueAtTime(0, now + 0.3);
                osc.start();
                osc.stop(now + 0.3);
                break;
            case 'levelUp':
                const notes = [440, 554, 659, 880];
                notes.forEach((f, i) => {
                    const o = this.ctx.createOscillator();
                    const g = this.ctx.createGain();
                    o.type = 'triangle';
                    o.frequency.setValueAtTime(f, now + i*0.1);
                    o.connect(g);
                    g.connect(this.ctx.destination);
                    g.gain.setValueAtTime(0.1, now + i*0.1);
                    g.gain.exponentialRampToValueAtTime(0.01, now + i*0.1 + 0.2);
                    o.start(now + i*0.1);
                    o.stop(now + i*0.1 + 0.2);
                });
                break;
            case 'capture':
                osc.type = 'sine';
                osc.frequency.setValueAtTime(200, now);
                osc.frequency.exponentialRampToValueAtTime(1000, now + 0.5);
                gain.gain.setValueAtTime(0.2, now);
                gain.gain.exponentialRampToValueAtTime(0.01, now + 0.5);
                osc.start();
                osc.stop(now + 0.5);
                break;
        }
    }
}

// ── const: ELEMENT_DATA ──
const ELEMENT_DATA = {
    Pyro: { color: '#ef4444', strong: 'Flora', weak: 'Hydro', borderColor: 'neon-border-pyro' },
    Hydro: { color: '#3b82f6', strong: 'Pyro', weak: 'Flora', borderColor: 'neon-border-hydro' },
    Flora: { color: '#22c55e', strong: 'Hydro', weak: 'Pyro', borderColor: 'neon-border-flora' },
    Zephyr: { color: '#a855f7', strong: 'Earth', weak: 'Volt', borderColor: 'neon-border-zephyr' },
    Earth: { color: '#f59e0b', strong: 'Volt', weak: 'Zephyr', borderColor: 'neon-border-earth' },
    Volt: { color: '#eab308', strong: 'Zephyr', weak: 'Earth', borderColor: 'neon-border-volt' },
    Cosmos: { color: '#94a3b8', strong: null, weak: null, borderColor: 'neon-border-cosmos' }
};

// ── const: RESONANTS_DB ──
const RESONANTS_DB = [
    // PYRO LINE
    {
        id: 'emberkit',
        name: 'Emberkit',
        type: 'Pyro',
        baseStats: { hp: 45, att: 55, def: 40, spd: 60 },
        evoLevel: 8,
        evoTo: 'pyrowolf',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><defs><radialGradient id="grad-pyro"><stop offset="0%" stop-color="#ff7e5f"/><stop offset="100%" stop-color="#feb47b"/></radialGradient></defs><path d="M50 20 L70 50 L50 80 L30 50 Z" fill="url(#grad-pyro)" /><path d="M40 40 Q50 30 60 40" stroke="white" fill="none" stroke-width="2"/><circle cx="40" cy="45" r="3" fill="white"/><circle cx="60" cy="45" r="3" fill="white"/></svg>`,
        moves: [
            { name: 'Singe', power: 40, acc: 95, type: 'Pyro', lv: 1 },
            { name: 'Flame Dash', power: 60, acc: 90, type: 'Pyro', lv: 5 },
            { name: 'Heat Wave', power: 80, acc: 85, type: 'Pyro', lv: 12 },
            { name: 'Inferno Burst', power: 110, acc: 80, type: 'Pyro', lv: 20 }
        ]
    },
    {
        id: 'pyrowolf',
        name: 'Pyrowolf',
        type: 'Pyro',
        baseStats: { hp: 65, att: 75, def: 60, spd: 80 },
        evoLevel: 15,
        evoTo: 'ignisaur',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M30 80 L50 40 L70 80 Z" fill="#ef4444"/><path d="M40 30 L50 10 L60 30" fill="#ef4444"/><circle cx="45" cy="40" r="3" fill="white"/><circle cx="55" cy="40" r="3" fill="white"/></svg>`,
        moves: [
            { name: 'Singe', power: 40, acc: 95, type: 'Pyro', lv: 1 },
            { name: 'Flame Dash', power: 60, acc: 90, type: 'Pyro', lv: 5 },
            { name: 'Heat Wave', power: 80, acc: 85, type: 'Pyro', lv: 12 },
            { name: 'Inferno Burst', power: 110, acc: 80, type: 'Pyro', lv: 20 }
        ]
    },
    {
        id: 'ignisaur',
        name: 'Ignisaur',
        type: 'Pyro',
        baseStats: { hp: 90, att: 100, def: 85, spd: 70 },
        evoLevel: null,
        evoTo: null,
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M20 80 Q50 20 80 80 Z" fill="#b91c1c"/><path d="M40 20 Q50 0 60 20" fill="#ef4444"/><circle cx="40" cy="40" r="4" fill="yellow"/><circle cx="60" cy="40" r="4" fill="yellow"/></svg>`,
        moves: [
            { name: 'Singe', power: 40, acc: 95, type: 'Pyro', lv: 1 },
            { name: 'Flame Dash', power: 60, acc: 90, type: 'Pyro', lv: 5 },
            { name: 'Heat Wave', power: 80, acc: 85, type: 'Pyro', lv: 12 },
            { name: 'Inferno Burst', power: 110, acc: 80, type: 'Pyro', lv: 20 }
        ]
    },
    // HYDRO LINE
    {
        id: 'droplet',
        name: 'Droplet',
        type: 'Hydro',
        baseStats: { hp: 50, att: 40, def: 50, spd: 65 },
        evoLevel: 8,
        evoTo: 'geoxus',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M50 20 Q80 50 50 80 Q20 50 50 20" fill="#3b82f6"/><circle cx="45" cy="40" r="2" fill="white"/><circle cx="55" cy="40" r="2" fill="white"/></svg>`,
        moves: [
            { name: 'Saturate', power: 40, acc: 95, type: 'Hydro', lv: 1 },
            { name: 'Aqua Jet', power: 60, acc: 90, type: 'Hydro', lv: 5 },
            { name: 'Hydro Pump', power: 80, acc: 85, type: 'Hydro', lv: 12 },
            { name: 'Oceanic Tide', power: 110, acc: 80, type: 'Hydro', lv: 20 }
        ]
    },
    {
        id: 'geoxus',
        name: 'Geoxus',
        type: 'Hydro',
        baseStats: { hp: 70, att: 60, def: 70, spd: 70 },
        evoLevel: 15,
        evoTo: 'neptunian',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M30 60 Q50 20 70 60 L70 80 L30 80 Z" fill="#2563eb"/><circle cx="45" cy="45" r="3" fill="white"/><circle cx="55" cy="45" r="3" fill="white"/></svg>`,
        moves: [
            { name: 'Saturate', power: 40, acc: 95, type: 'Hydro', lv: 1 },
            { name: 'Aqua Jet', power: 60, acc: 90, type: 'Hydro', lv: 5 },
            { name: 'Hydro Pump', power: 80, acc: 85, type: 'Hydro', lv: 12 },
            { name: 'Oceanic Tide', power: 110, acc: 80, type: 'Hydro', lv: 20 }
        ]
    },
    {
        id: 'neptunian',
        name: 'Neptunian',
        type: 'Hydro',
        baseStats: { hp: 95, att: 80, def: 90, spd: 60 },
        evoLevel: null,
        evoTo: null,
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M20 70 Q50 10 80 70 L80 90 L20 90 Z" fill="#1d4ed8"/><path d="M30 70 L20 60 M70 70 L80 60" stroke="#1d4ed8" stroke-width="4"/><circle cx="40" cy="40" r="4" fill="white"/><circle cx="60" cy="40" r="4" fill="white"/></svg>`,
        moves: [
            { name: 'Saturate', power: 40, acc: 95, type: 'Hydro', lv: 1 },
            { name: 'Aqua Jet', power: 60, acc: 90, type: 'Hydro', lv: 5 },
            { name: 'Hydro Pump', power: 80, acc: 85, type: 'Hydro', lv: 12 },
            { name: 'Oceanic Tide', power: 110, acc: 80, type: 'Hydro', lv: 20 }
        ]
    },
    // FLORA LINE
    {
        id: 'seedling',
        name: 'Seedling',
        type: 'Flora',
        baseStats: { hp: 60, att: 45, def: 55, spd: 50 },
        evoLevel: 8,
        evoTo: 'thornback',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><circle cx="50" cy="60" r="20" fill="#22c55e"/><path d="M50 40 L50 20 Q60 20 60 30" stroke="#16a34a" stroke-width="4" fill="none"/><circle cx="45" cy="55" r="2" fill="white"/><circle cx="55" cy="55" r="2" fill="white"/></svg>`,
        moves: [
            { name: 'Leaf Slap', power: 40, acc: 95, type: 'Flora', lv: 1 },
            { name: 'Vine Whip', power: 60, acc: 90, type: 'Flora', lv: 5 },
            { name: 'Solar Beam', power: 80, acc: 85, type: 'Flora', lv: 12 },
            { name: 'Gaea Crash', power: 110, acc: 80, type: 'Flora', lv: 20 }
        ]
    },
    {
        id: 'thornback',
        name: 'Thornback',
        type: 'Flora',
        baseStats: { hp: 80, att: 65, def: 75, spd: 60 },
        evoLevel: 15,
        evoTo: 'bloomgoliath',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M30 80 L70 80 L60 40 L40 40 Z" fill="#16a34a"/><path d="M30 80 L20 70 M70 80 L80 70" stroke="#16a34a" stroke-width="4"/><circle cx="45" cy="50" r="3" fill="white"/><circle cx="55" cy="50" r="3" fill="white"/></svg>`,
        moves: [
            { name: 'Leaf Slap', power: 40, acc: 95, type: 'Flora', lv: 1 },
            { name: 'Vine Whip', power: 60, acc: 90, type: 'Flora', lv: 5 },
            { name: 'Solar Beam', power: 80, acc: 85, type: 'Flora', lv: 12 },
            { name: 'Gaea Crash', power: 110, acc: 80, type: 'Flora', lv: 20 }
        ]
    },
    {
        id: 'bloomgoliath',
        name: 'Bloomgoliath',
        type: 'Flora',
        baseStats: { hp: 110, att: 85, def: 100, spd: 40 },
        evoLevel: null,
        evoTo: null,
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M20 80 Q50 30 80 80 Z" fill="#14532d"/><circle cx="50" cy="40" r="25" fill="#22c55e"/><circle cx="40" cy="35" r="4" fill="white"/><circle cx="60" cy="35" r="4" fill="white"/></svg>`,
        moves: [
            { name: 'Leaf Slap', power: 40, acc: 95, type: 'Flora', lv: 1 },
            { name: 'Vine Whip', power: 60, acc: 90, type: 'Flora', lv: 5 },
            { name: 'Solar Beam', power: 80, acc: 85, type: 'Flora', lv: 12 },
            { name: 'Gaea Crash', power: 110, acc: 80, type: 'Flora', lv: 20 }
        ]
    },
    // VOLT LINE
    {
        id: 'sparky',
        name: 'Sparky',
        type: 'Volt',
        baseStats: { hp: 40, att: 60, def: 40, spd: 80 },
        evoLevel: 8,
        evoTo: 'boltsaber',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M50 30 L60 50 L50 70 L40 50 Z" fill="#eab308"/><path d="M50 30 L40 20 M50 30 L60 20" stroke="#eab308" stroke-width="3"/><circle cx="45" cy="45" r="2" fill="black"/><circle cx="55" cy="45" r="2" fill="black"/></svg>`,
        moves: [
            { name: 'Static Zap', power: 40, acc: 95, type: 'Volt', lv: 1 },
            { name: 'Volt Tackle', power: 60, acc: 90, type: 'Volt', lv: 5 },
            { name: 'Thunder Bolt', power: 80, acc: 85, type: 'Volt', lv: 12 },
            { name: 'Plasma Storm', power: 110, acc: 80, type: 'Volt', lv: 20 }
        ]
    },
    {
        id: 'boltsaber',
        name: 'Boltsaber',
        type: 'Volt',
        baseStats: { hp: 60, att: 80, def: 60, spd: 100 },
        evoLevel: 15,
        evoTo: 'stormlord',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M30 70 L50 30 L70 70 Z" fill="#ca8a04"/><path d="M50 30 L50 10" stroke="#eab308" stroke-width="5"/><circle cx="44" cy="50" r="3" fill="black"/><circle cx="56" cy="50" r="3" fill="black"/></svg>`,
        moves: [
            { name: 'Static Zap', power: 40, acc: 95, type: 'Volt', lv: 1 },
            { name: 'Volt Tackle', power: 60, acc: 90, type: 'Volt', lv: 5 },
            { name: 'Thunder Bolt', power: 80, acc: 85, type: 'Volt', lv: 12 },
            { name: 'Plasma Storm', power: 110, acc: 80, type: 'Volt', lv: 20 }
        ]
    },
    {
        id: 'stormlord',
        name: 'Stormlord',
        type: 'Volt',
        baseStats: { hp: 85, att: 110, def: 70, spd: 110 },
        evoLevel: null,
        evoTo: null,
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M20 80 L50 20 L80 80 Z" fill="#a16207"/><path d="M30 30 L20 10 M70 30 L80 10" stroke="#eab308" stroke-width="4"/><circle cx="40" cy="50" r="4" fill="black"/><circle cx="60" cy="50" r="4" fill="black"/></svg>`,
        moves: [
            { name: 'Static Zap', power: 40, acc: 95, type: 'Volt', lv: 1 },
            { name: 'Volt Tackle', power: 60, acc: 90, type: 'Volt', lv: 5 },
            { name: 'Thunder Bolt', power: 80, acc: 85, type: 'Volt', lv: 12 },
            { name: 'Plasma Storm', power: 110, acc: 80, type: 'Volt', lv: 20 }
        ]
    },
    // ZEPHYR LINE
    {
        id: 'gusty',
        name: 'Gusty',
        type: 'Zephyr',
        baseStats: { hp: 45, att: 50, def: 40, spd: 85 },
        evoLevel: 8,
        evoTo: 'slipstream',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M50 20 Q80 50 50 80 Q20 50 50 20" fill="#a855f7" opacity="0.7"/><path d="M40 40 Q50 30 60 40" stroke="white" fill="none"/><circle cx="40" cy="45" r="2" fill="white"/><circle cx="60" cy="45" r="2" fill="white"/></svg>`,
        moves: [
            { name: 'Air Puff', power: 40, acc: 95, type: 'Zephyr', lv: 1 },
            { name: 'Wind Cut', power: 60, acc: 90, type: 'Zephyr', lv: 5 },
            { name: 'Cyclone', power: 80, acc: 85, type: 'Zephyr', lv: 12 },
            { name: 'Sky Rupture', power: 110, acc: 80, type: 'Zephyr', lv: 20 }
        ]
    },
    {
        id: 'slipstream',
        name: 'Slipstream',
        type: 'Zephyr',
        baseStats: { hp: 65, att: 70, def: 60, spd: 105 },
        evoLevel: 15,
        evoTo: 'zephyrus',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M20 50 L50 20 L80 50 L50 80 Z" fill="#8b5cf6"/><path d="M30 40 L20 40 M70 40 L80 40" stroke="white" stroke-width="2"/><circle cx="45" cy="45" r="3" fill="white"/><circle cx="55" cy="45" r="3" fill="white"/></svg>`,
        moves: [
            { name: 'Air Puff', power: 40, acc: 95, type: 'Zephyr', lv: 1 },
            { name: 'Wind Cut', power: 60, acc: 90, type: 'Zephyr', lv: 5 },
            { name: 'Cyclone', power: 80, acc: 85, type: 'Zephyr', lv: 12 },
            { name: 'Sky Rupture', power: 110, acc: 80, type: 'Zephyr', lv: 20 }
        ]
    },
    {
        id: 'zephyrus',
        name: 'Zephyrus',
        type: 'Zephyr',
        baseStats: { hp: 85, att: 90, def: 80, spd: 120 },
        evoLevel: null,
        evoTo: null,
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M10 50 L50 10 L90 50 L50 90 Z" fill="#6d28d9"/><path d="M20 50 L10 60 M80 50 L90 60" stroke="white" stroke-width="3"/><circle cx="40" cy="40" r="4" fill="white"/><circle cx="60" cy="40" r="4" fill="white"/></svg>`,
        moves: [
            { name: 'Air Puff', power: 40, acc: 95, type: 'Zephyr', lv: 1 },
            { name: 'Wind Cut', power: 60, acc: 90, type: 'Zephyr', lv: 5 },
            { name: 'Cyclone', power: 80, acc: 85, type: 'Zephyr', lv: 12 },
            { name: 'Sky Rupture', power: 110, acc: 80, type: 'Zephyr', lv: 20 }
        ]
    },
    // EARTH LINE
    {
        id: 'pebble',
        name: 'Pebble',
        type: 'Earth',
        baseStats: { hp: 65, att: 50, def: 70, spd: 30 },
        evoLevel: 8,
        evoTo: 'obsidian',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M30 80 L70 80 L80 50 L20 50 Z" fill="#f59e0b"/><circle cx="40" cy="60" r="2" fill="black"/><circle cx="60" cy="60" r="2" fill="black"/></svg>`,
        moves: [
            { name: 'Rock Throw', power: 40, acc: 95, type: 'Earth', lv: 1 },
            { name: 'Dirt Slam', power: 60, acc: 90, type: 'Earth', lv: 5 },
            { name: 'Tremor', power: 80, acc: 85, type: 'Earth', lv: 12 },
            { name: 'Mountain Split', power: 110, acc: 80, type: 'Earth', lv: 20 }
        ]
    },
    {
        id: 'obsidian',
        name: 'Obsidian',
        type: 'Earth',
        baseStats: { hp: 85, att: 70, def: 90, spd: 40 },
        evoLevel: 15,
        evoTo: 'terrasunder',
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M20 80 L80 80 L70 40 L30 40 Z" fill="#b45309"/><path d="M30 40 L40 30 L50 40 L60 30 L70 40" fill="none" stroke="#f59e0b" stroke-width="3"/><circle cx="40" cy="55" r="3" fill="black"/><circle cx="60" cy="55" r="3" fill="black"/></svg>`,
        moves: [
            { name: 'Rock Throw', power: 40, acc: 95, type: 'Earth', lv: 1 },
            { name: 'Dirt Slam', power: 60, acc: 90, type: 'Earth', lv: 5 },
            { name: 'Tremor', power: 80, acc: 85, type: 'Earth', lv: 12 },
            { name: 'Mountain Split', power: 110, acc: 80, type: 'Earth', lv: 20 }
        ]
    },
    {
        id: 'terrasunder',
        name: 'Terrasunder',
        type: 'Earth',
        baseStats: { hp: 120, att: 90, def: 110, spd: 20 },
        evoLevel: null,
        evoTo: null,
        svg: (lv) => `<svg viewBox="0 0 100 100" class="w-full h-full"><path d="M10 90 L90 90 L80 30 L20 30 Z" fill="#78350f"/><path d="M20 30 L30 10 L50 30 L70 10 L80 30" fill="none" stroke="#f59e0b" stroke-width="5"/><circle cx="35" cy="50" r="5" fill="black"/><circle cx="65" cy="50" r="5" fill="black"/></svg>`,
        moves: [
            { name: 'Rock Throw', power: 40, acc: 95, type: 'Earth', lv: 1 },
            { name: 'Dirt Slam', power: 60, acc: 90, type: 'Earth', lv: 5 },
            { name: 'Tremor', power: 80, acc: 85, type: 'Earth', lv: 12 },
            { name: 'Mountain Split', power: 110, acc: 80, type: 'Earth', lv: 20 }
        ]
    }
];

// ── class: GameState ──
class GameState {
    constructor() {
        this.player = {
            shards: 500,
            unlockedZone: 0,
            completedNodes: [],
            inventory: {
                prisms: 5,
                hyperPrisms: 2,
                elixirs: 3,
                cleansers: 3
            },
            party: []
        };
        this.zones = [
            {
                name: 'The Verdant Canopy',
                nodes: [
                    { id: 0, type: 'encounter', lv: [1, 3], resIds: ['seedling', 'gusty'], x: 100, y: 400, connected: [1] },
                    { id: 1, type: 'cache', reward: { shards: 100 }, x: 250, y: 300, connected: [0, 2] },
                    { id: 2, type: 'encounter', lv: [3, 5], resIds: ['seedling', 'gusty'], x: 400, y: 450, connected: [1, 3] },
                    { id: 3, type: 'boss', bossId: 'guardian_1', x: 550, y: 250, connected: [2], unlockNext: true }
                ]
            },
            {
                name: 'The Sunken Grotto',
                nodes: [
                    { id: 4, type: 'encounter', lv: [5, 8], resIds: ['droplet', 'pebble'], x: 100, y: 200, connected: [5] },
                    { id: 5, type: 'spring', x: 300, y: 300, connected: [4, 6] },
                    { id: 6, type: 'encounter', lv: [8, 11], resIds: ['droplet', 'pebble'], x: 500, y: 150, connected: [5, 7] },
                    { id: 7, type: 'boss', bossId: 'guardian_2', x: 700, y: 300, connected: [6], unlockNext: true }
                ]
            },
            {
                name: 'The Obsidian Crags',
                nodes: [
                    { id: 8, type: 'encounter', lv: [11, 14], resIds: ['emberkit', 'sparky'], x: 100, y: 400, connected: [9] },
                    { id: 9, type: 'cache', reward: { shards: 250 }, x: 300, y: 200, connected: [8, 10] },
                    { id: 10, type: 'encounter', lv: [14, 17], resIds: ['emberkit', 'sparky'], x: 500, y: 400, connected: [9, 11] },
                    { id: 11, type: 'boss', bossId: 'guardian_3', x: 700, y: 200, connected: [10], unlockNext: true }
                ]
            },
            {
                name: 'The Apex Pillar',
                nodes: [
                    { id: 12, type: 'encounter', lv: [15, 20], resIds: ['ignisaur', 'neptunian', 'bloomgoliath', 'stormlord', 'zephyrus', 'terrasunder'], x: 200, y: 400, connected: [13] },
                    { id: 13, type: 'boss', bossId: 'final_boss', x: 500, y: 200, connected: [12], unlockNext: false }
                ]
            }
        ];
    }

    save() {
        localStorage.setItem('aethelgard_save', JSON.stringify(this.player));
    }

    load() {
        const saved = localStorage.getItem('aethelgard_save');
        if (saved) {
            this.player = JSON.parse(saved);
        }
    }
}

// ── class: BattleEngine ──
class BattleEngine {
    constructor(playerSync, synth) {
        this.playerSync = playerSync;
        this.synth = synth;
        this.state = 'init'; // init, player-turn, enemy-turn, transition, win, loss
        this.playerRes = null;
        this.enemyRes = null;
        this.logEl = document.getElementById('battle-text');
        this.actionEl = document.getElementById('battle-actions');
        this.overlayEl = document.getElementById('battle-overlay');
        this.overlayMsgEl = document.getElementById('battle-message');
    }

    startBattle(enemyConfig) {
        // Setup Resonants
        const enemyBase = RESONANTS_DB.find(r => r.id === enemyConfig.resId);
        this.enemyRes = this.createResonant(enemyBase, enemyConfig.lv);
        this.playerRes = this.playerSync.party[0]; // Simplified: always first

        this.updateUI();
        this.setBattleState('player-turn');
        this.log('A wild ' + this.enemyRes.name + ' appeared!');
        this.renderActions();
    }

    createResonant(base, lv) {
        const stats = {};
        for (let s in base.baseStats) {
            stats[s] = Math.floor((base.baseStats[s] * (lv * 2 + 5)) / 10);
        }
        return {
            ...base,
            level: lv,
            hp: stats.hp,
            maxHp: stats.hp,
            stats: stats,
            currentHp: stats.hp,
            xp: 0,
            nextLv: lv * 100
        };
    }

    setBattleState(state) {
        this.state = state;
        if (state === 'player-turn') {
            this.renderActions();
        } else {
            this.actionEl.innerHTML = '';
        }
    }

    log(msg) {
        this.logEl.innerText = msg;
    }

    async showMessage(msg, duration = 1500) {
        this.overlayEl.classList.remove('hidden');
        this.overlayMsgEl.innerText = msg;
        await new Promise(r => setTimeout(r, duration));
        this.overlayEl.classList.add('hidden');
    }

    updateUI() {
        // Enemy UI
        document.getElementById('enemy-name').innerText = this.enemyRes.name;
        document.getElementById('enemy-lv').innerText = `Lv ${this.enemyRes.level}`;
        const enemyHpPerc = (this.enemyRes.currentHp / this.enemyRes.maxHp) * 100;
        document.getElementById('enemy-hp-bar').style.width = `${enemyHpPerc}%`;
        document.getElementById('enemy-hp-bar').className = 'hp-bar-fill h-full ' + this.getHpColor(enemyHpPerc);
        document.getElementById('enemy-hp-text').innerText = `${Math.ceil(this.enemyRes.currentHp)} / ${this.enemyRes.maxHp}`;
        document.getElementById('enemy-sprite-container').innerHTML = this.enemyRes.svg(this.enemyRes.level);

        // Player UI
        document.getElementById('player-name').innerText = this.playerRes.name;
        document.getElementById('player-lv').innerText = `Lv ${this.playerRes.level}`;
        const playerHpPerc = (this.playerRes.currentHp / this.playerRes.maxHp) * 100;
        document.getElementById('player-hp-bar').style.width = `${playerHpPerc}%`;
        document.getElementById('player-hp-bar').className = 'hp-bar-fill h-full ' + this.getHpColor(playerHpPerc);
        document.getElementById('player-hp-text').innerText = `${Math.ceil(this.playerRes.currentHp)} / ${this.playerRes.maxHp}`;
        document.getElementById('player-sprite-container').innerHTML = this.playerRes.svg(this.playerRes.level);
    }

    getHpColor(perc) {
        if (perc > 50) return 'bg-green-500';
        if (perc > 25) return 'bg-yellow-500';
        return 'bg-red-500';
    }

    renderActions() {
        this.actionEl.innerHTML = '';
        
        // Attack
        const attackBtn = this.createActionBtn('Attack', () => this.handleAttackSelection());
        this.actionEl.appendChild(attackBtn);

        // Prism
        const prismBtn = this.createActionBtn('Prism', () => this.handleCapture());
        this.actionEl.appendChild(prismBtn);

        // Items
        const itemBtn = this.createActionBtn('Items', () => this.handleItems());
        this.actionEl.appendChild(itemBtn);

        // Run
        const runBtn = this.createActionBtn('Run', () => this.handleRun());
        this.actionEl.appendChild(runBtn);
    }

    createActionBtn(text, callback) {
        const b = document.createElement('button');
        b.className = 'glass-panel py-2 px-4 rounded-xl hover:bg-white/10 transition-all font-semibold uppercase text-xs';
        b.innerText = text;
        b.onclick = () => {
            this.synth.play('select');
            callback();
        };
        return b;
    }

    async handleAttackSelection() {
        // For simplicity in this demo, we'll show a sub-menu of moves
        const moves = this.playerRes.moves.filter(m => m.lv <= this.playerRes.level);
        this.actionEl.innerHTML = '';
        
        moves.forEach(move => {
            const b = this.createActionBtn(move.name, () => this.executeMove(this.playerRes, this.enemyRes, move));
            this.actionEl.appendChild(b);
        });

        const back = this.createActionBtn('Back', () => this.renderActions());
        this.actionEl.appendChild(back);
    }

    async executeMove(attacker, target, move) {
        this.setBattleState('transition');
        
        // Math
        const typeMod = this.getTypeModifier(move.type, target.type);
        const crit = Math.random() < 0.1;
        const dmg = this.calcDamage(attacker, target, move) * typeMod * (crit ? 1.5 : 1);
        
        this.synth.play(crit ? 'critical' : 'hit');
        
        // Animation effect
        const targetSprite = attacker === this.playerRes ? 
            document.getElementById('enemy-sprite-container') : 
            document.getElementById('player-sprite-container');
            
        targetSprite.classList.add('animate-shake');
        setTimeout(() => targetSprite.classList.remove('animate-shake'), 200);

        target.currentHp -= dmg;
        if (target.currentHp < 0) target.currentHp = 0;
        
        this.updateUI();
        
        let msg = `${attacker.name} used ${move.name}!`;
        if (typeMod > 1) msg += " It's super effective!";
        if (typeMod < 1 && typeMod > 0) msg += " It's not very effective...";
        if (crit) msg += " CRITICAL HIT!";
        
        this.log(msg);
        await new Promise(r => setTimeout(r, 1500));

        if (target.currentHp <= 0) {
            this.handleVictory();
        } else {
            this.setBattleState('enemy-turn');
            setTimeout(() => this.enemyTurn(), 1000);
        }
    }

    calcDamage(attacker, target, move) {
        const atk = attacker.stats.att;
        const def = target.stats.def;
        const power = move.power;
        const lv = attacker.level;
        const base = (((2 * lv / 5 + 2) * power * (atk / def)) / 50 + 2);
        return base * (Math.random() * 0.15 + 0.85);
    }

    getTypeModifier(moveType, resType) {
        const data = ELEMENT_DATA[moveType];
        if (!data) return 1;
        if (data.strong === resType) return 2;
        if (data.weak === resType) return 0.5;
        return 1;
    }

    async enemyTurn() {
        const moves = this.enemyRes.moves.filter(m => m.lv <= this.enemyRes.level);
        const move = moves[Math.floor(Math.random() * moves.length)];
        
        await this.executeMove(this.enemyRes, this.playerRes, move);
        
        if (this.playerRes.currentHp <= 0) {
            this.handleLoss();
        } else if (this.state !== 'win' && this.state !== 'loss') {
            this.setBattleState('player-turn');
            this.renderActions();
        }
    }

    async handleCapture() {
        if (this.playerSync.inventory.prisms <= 0) {
            this.log("No Prisms left!");
            return;
        }

        this.setBattleState('transition');
        this.playerSync.inventory.prisms--;
        
        this.log("Throwing Aether Prism...");
        await new Promise(r => setTimeout(r, 1000));

        const rate = (1 - (this.enemyRes.currentHp / this.enemyRes.maxHp)) * 0.8 + 0.2;
        if (Math.random() < rate) {
            this.synth.play('capture');
            await this.showMessage("Successfully Captured " + this.enemyRes.name + "!");
            this.playerSync.party.push({...this.enemyRes});
            this.handleVictory();
        } else {
            this.log("The Resonant broke free!");
            await new Promise(r => setTimeout(r, 1500));
            this.setBattleState('player-turn');
            this.renderActions();
        }
    }

    async handleItems() {
        this.actionEl.innerHTML = '';
        const inv = this.playerSync.inventory;
        
        if (inv.elixirs > 0) {
            const b = this.createActionBtn(`Elixir (${inv.elixirs})`, () => {
                this.playerRes.currentHp = Math.min(this.playerRes.maxHp, this.playerRes.currentHp + this.playerRes.maxHp * 0.5);
                inv.elixirs--;
                this.log("Used Elixir! Restored HP.");
                this.updateUI();
                setTimeout(() => this.setBattleState('enemy-turn'), 1500);
                this.synth.play('select');
            });
            this.actionEl.appendChild(b);
        }

        const back = this.createActionBtn('Back', () => this.renderActions());
        this.actionEl.appendChild(back);
    }

    handleRun() {
        this.log("Escaped successfully!");
        setTimeout(() => {
            document.getElementById('screen-battle').classList.add('hidden');
            document.getElementById('screen-map').classList.remove('hidden');
        }, 1000);
    }

    handleVictory() {
        this.state = 'win';
        this.synth.play('levelUp');
        const xpGain = this.enemyRes.level * 50;
        this.playerRes.xp += xpGain;
        this.log(`Victory! Gained ${xpGain} XP and 50 Shards.`);
        this.playerSync.shards += 50;
        
        // Level up check
        if (this.playerRes.xp >= this.playerRes.nextLv) {
            this.playerRes.level++;
            this.playerRes.xp -= this.playerRes.nextLv;
            this.playerRes.nextLv *= 1.2;
            this.log(`${this.playerRes.name} leveled up to ${this.playerRes.level}!`);
        }

        setTimeout(() => {
            document.getElementById('screen-battle').classList.add('hidden');
            document.getElementById('screen-map').classList.remove('hidden');
        }, 3000);
    }

    handleLoss() {
        this.state = 'loss';
        this.log("Your Resonant fainted...");
        setTimeout(() => {
            document.getElementById('screen-battle').classList.add('hidden');
            document.getElementById('screen-map').classList.remove('hidden');
        }, 3000);
    }
}

// ── main ──

const synth = new Synth();
const state = new GameState();
const battle = new BattleEngine(state.player, synth);

// UI Elements
const screens = {
    menu: document.getElementById('screen-menu'),
    map: document.getElementById('screen-map'),
    battle: document.getElementById('screen-battle'),
    modal: document.getElementById('modal-overlay')
};

function showScreen(screenKey) {
    Object.values(screens).forEach(s => s.classList.add('hidden'));
    screens[screenKey].classList.remove('hidden');
}

function initPlayer() {
    if (state.player.party.length === 0) {
        // Starter choice
        const starter = RESONANTS_DB[0]; // Emberkit
        state.player.party.push({
            ...starter,
            level: 5,
            hp: 70,
            maxHp: 70,
            currentHp: 70,
            stats: { hp: 70, att: 60, def: 50, spd: 60 },
            xp: 0,
            nextLv: 500
        });
    }
}

function renderMap() {
    const zone = state.zones[state.player.unlockedZone];
    document.getElementById('zone-title').innerText = zone.name;
    document.getElementById('map-shards').innerText = state.player.shards;
    
    const svg = document.getElementById('map-svg');
    svg.innerHTML = '';
    
    // Draw connections first
    zone.nodes.forEach(node => {
        node.connected.forEach(connId => {
            const connNode = zone.nodes.find(n => n.id === connId);
            const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
            line.setAttribute('x1', node.x);
            line.setAttribute('y1', node.y);
            line.setAttribute('x2', connNode.x);
            line.setAttribute('y2', connNode.y);
            line.setAttribute('stroke', 'rgba(255,255,255,0.2)');
            line.setAttribute('stroke-width', '3');
            svg.appendChild(line);
        });
    });

    // Draw nodes
    zone.nodes.forEach(node => {
        const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        circle.setAttribute('cx', node.x);
        circle.setAttribute('cy', node.y);
        circle.setAttribute('r', '15');
        circle.setAttribute('fill', node.type === 'boss' ? '#ef4444' : '#3b82f6');
        
        const isUnlocked = state.player.completedNodes.includes(0) || node.id === 0 || 
            zone.nodes.filter(n => state.player.completedNodes.includes(n.id) && n.connected.includes(node.id)).length > 0;

        if (isUnlocked) {
            circle.classList.add('node-active');
            circle.onclick = () => handleNodeClick(node);
        } else {
            circle.classList.add('node-locked');
        }
        svg.appendChild(circle);
    });
}

function handleNodeClick(node) {
    synth.play('select');
    if (node.type === 'encounter') {
        const resId = node.resIds[Math.floor(Math.random() * node.resIds.length)];
        const lv = Math.floor(Math.random() * (node.lv[1] - node.lv[0] + 1)) + node.lv[0];
        showScreen('battle');
        battle.startBattle({ resId, lv });
    } else if (node.type === 'cache') {
        state.player.shards += node.reward.shards;
        state.player.completedNodes.push(node.id);
        openModal(`<b> Cache Found!</b><br>Found ${node.reward.shards} Aether Shards!`);
        renderMap();
    } else if (node.type === 'spring') {
        state.player.party.forEach(r => r.currentHp = r.maxHp);
        openModal(`<b> Healing Spring</b><br>Your team has been fully restored!`);
        renderMap();
    } else if (node.type === 'boss') {
        const boss = { resId: 'ignisaur', lv: 10 + state.player.unlockedZone * 5 };
        showScreen('battle');
        battle.startBattle(boss);
        // Logic to unlock next zone would go here in victory handler
    }
}

function openModal(html) {
    document.getElementById('modal-body').innerHTML = `<div class="text-center">${html}</div>`;
    screens.modal.classList.remove('hidden');
}

document.getElementById('btn-close-modal').onclick = () => screens.modal.classList.add('hidden');

document.getElementById('btn-start').onclick = () => {
    synth.play('select');
    initPlayer();
    showScreen('map');
    renderMap();
};

document.getElementById('btn-load').onclick = () => {
    synth.play('select');
    state.load();
    initPlayer();
    showScreen('map');
    renderMap();
};

document.getElementById('btn-reset').onclick = () => {
    synth.play('select');
    localStorage.removeItem('aethelgard_save');
    location.reload();
};

document.getElementById('btn-open-party').onclick = () => {
    synth.play('select');
    let html = '<h3 class="retro-font text-xl mb-4">Resonant Team</h3><div class="grid gap-4">';
    state.player.party.forEach((r, i) => {
        html += `
            <div class="glass-panel p-4 rounded-xl flex items-center justify-between">
                <div>
                    <div class="font-bold">${r.name} (Lv ${r.level})</div>
                    <div class="text-xs text-slate-400">${r.type} | HP: ${r.currentHp}/${r.maxHp}</div>
                </div>
                <div class="w-16 h-16">${r.svg(r.level)}</div>
            </div>`;
    });
    html += '</div>';
    openModal(html);
};

document.getElementById('btn-open-shop').onclick = () => {
    synth.play('select');
    let html = '<h3 class="retro-font text-xl mb-4">Camp Shop</h3><div class="grid gap-4">';
    const items = [
        { name: 'Harmonizer Prism', price: 100, key: 'prisms', amount: 5 },
        { name: 'Hyper Prism', price: 300, key: 'hyperPrisms', amount: 2 },
        { name: 'Core Restorer', price: 150, key: 'elixirs', amount: 3 }
    ];
    items.forEach(item => {
        html += `
            <div class="glass-panel p-4 rounded-xl flex items-center justify-between">
                <div>
                    <div class="font-bold">${item.name}</div>
                    <div class="text-xs text-yellow-400">${item.price} Shards</div>
                </div>
                <button onclick="buyItem('${item.key}', ${item.price})" class="bg-white/20 px-4 py-2 rounded-lg text-xs uppercase font-bold hover:bg-white/30">Buy</button>
            </div>`;
    });
    html += '</div>';
    openModal(html);
};

window.buyItem = (key, price) => {
    if (state.player.shards >= price) {
        state.player.shards -= price;
        state.player.inventory[key]++;
        synth.play('select');
        alert('Item purchased!');
        renderMap();
    } else {
        alert('Not enough shards!');
    }
};

document.getElementById('btn-open-dex').onclick = () => {
    synth.play('select');
    let html = '<h3 class="retro-font text-xl mb-4">Resonants Dex</h3><div class="grid grid-cols-2 gap-4">';
    RESONANTS_DB.forEach(r => {
        html += `
            <div class="glass-panel p-4 rounded-xl flex flex-col items-center text-center">
                <div class="w-20 h-20 mb-2">${r.svg(1)}</div>
                <div class="font-bold text-sm">${r.name}</div>
                <div class="text-[10px] text-slate-400 uppercase">${r.type}</div>
            </div>`;
    });
    html += '</div>';
    openModal(html);
};
</script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"composer","sessionId":"3d3a11f9-8dfa-4bbe-9c48-a9274d7dbadc","tokensIn":28874,"tokensOut":17124,"tokensTotal":45998,"turns":2,"toolCalls":11,"failedToolCalls":0,"timestamp":"2026-07-14T16:20:24.344Z"} -->