← Creature Collection results

Creature Collection

Qwen 3.5 9b · composer

Initial view of Creature Collection

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 94,759 bytes · SHA-256 a3dd272a5d36
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>New Binding - Chronicle of the Resonant</title>
<style>
/* ── css: core ── */
/* New Binding - Chronicle of the Resonant */
* { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
html, body { width: 100%; height: 100%; overflow: hidden; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #0a0a12; color: #e8e8ff; }

/* Color palette - Resonance types */
:root { --bg-dark: #0a0a12; --bg-light: #151524; --accent: #7dd3fc; --accent-glow: rgba(125,211,252,0.3); --text-main: #e8e8ff; --text-muted: #9ca3af; --danger: #f87171; --success: #6ee7b7; --warning: #fbbf24; }
#app { position: relative; width: 100vw; height: 100vh; }

/* Canvas layers */
#world-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; }
#battle-canvas { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 800px; height: 600px; z-index: 2; border-radius: 12px; box-shadow: 0 0 40px rgba(0,0,0,0.5); display: none; }

/* Glassmorphism UI */
.ui-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10; pointer-events: none; }
.modal-overlay { position: fixed; inset: 0; background: rgba(10,10,18,0.95); z-index: 20; display: flex; align-items: center; justify-content: center; pointer-events: auto; }
.modal { background: linear-gradient(135deg, rgba(21,21,36,0.95), rgba(10,10,18,0.98)); border-radius: 16px; padding: 32px; min-width: 480px; max-width: 90vw; max-height: 90vh; overflow-y: auto; box-shadow: 0 20px 60px rgba(0,0,0,0.6); backdrop-filter: blur(8px); border: 1px solid rgba(255,255,255,0.1); }

/* Buttons */
.btn { display: inline-flex; align-items: center; justify-content: center; padding: 14px 28px; border: none; border-radius: 10px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; text-transform: uppercase; letter-spacing: 0.5px; }
.btn-primary { background: linear-gradient(135deg, var(--accent), #60a5fa); color: #0f172a; box-shadow: 0 4px 20px var(--accent-glow); }
.btn-secondary { background: rgba(255,255,255,0.1); color: var(--text-main); border: 1px solid rgba(255,255,255,0.2); }
.btn:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(0,0,0,0.4); }
.btn:active { transform: translateY(0); }

/* Text styling */
h1 { font-size: 32px; text-align: center; background: linear-gradient(90deg, var(--accent), #c4b5fd, var(--accent)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 16px; }
h2 { font-size: 24px; color: var(--accent); margin-bottom: 12px; }
h3 { font-size: 18px; color: var(--text-main); margin-bottom: 8px; }
p { line-height: 1.6; color: var(--text-muted); margin-bottom: 12px; }

/* HUD elements */
.hp-bar { height: 20px; background: rgba(0,0,0,0.5); border-radius: 10px; overflow: hidden; position: relative; margin-top: 4px; }
.hp-fill { height: 100%; transition: width 0.3s ease; }
.hp-full { background: linear-gradient(90deg, #6ee7b7, var(--accent)); }
.hp-half { background: linear-gradient(90deg, #fbbf24, var(--accent)); }
.hp-low { background: linear-gradient(90deg, var(--danger), var(--accent)); }

/* Animations */
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }
@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }
@keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } }
@keyframes glow { 0%, 100% { box-shadow: 0 0 10px var(--accent-glow); } 50% { box-shadow: 0 0 30px var(--accent-glow); } }

.animate-pulse { animation: pulse 2s ease-in-out infinite; }
.animate-float { animation: float 3s ease-in-out infinite; }
.animate-shake { animation: shake 0.3s ease-in-out; }
.animate-glow { animation: glow 2s ease-in-out infinite; }

/* Type colors */
.type-flux { border-color: #fbbf24; box-shadow: 0 0 15px rgba(251,191,36,0.3); }
.type-crysta { border-color: #818cf8; box-shadow: 0 0 15px rgba(129,140,248,0.3); }
.type-void { border-color: #7c3aed; box-shadow: 0 0 15px rgba(124,58,237,0.3); }
.type-ember { border-color: #f97316; box-shadow: 0 0 15px rgba(249,115,22,0.3); }
.type-storm { border-color: #06b6d4; box-shadow: 0 0 15px rgba(6,182,212,0.3); }
.type-iron { border-color: #eab308; box-shadow: 0 0 15px rgba(234,179,8,0.3); }
.type-spirit { border-color: #ec4899; box-shadow: 0 0 15px rgba(236,72,153,0.3); }

/* Scrollbar styling */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: var(--bg-dark); }
::-webkit-scrollbar-thumb { background: var(--text-muted); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: var(--accent); }

/* Selection indicators */
.selected { outline: 3px solid var(--accent) !important; box-shadow: inset 0 0 20px rgba(125,211,252,0.2); }

/* Floating text container */
.floating-texts { position: absolute; pointer-events: none; font-weight: 700; font-size: 16px; text-shadow: 0 2px 4px rgba(0,0,0,0.5); z-index: 100; }
/* ── css: ui-systems ── */
/* Title Screen */
#title-screen { position: absolute; inset: 0; z-index: 50; display: flex; flex-direction: column; align-items: center; justify-content: center; background: radial-gradient(ellipse at center, #1a1a35 0%, var(--bg-dark) 60%); }
.title-logo { font-size: 72px; font-weight: 900; text-align: center; background: linear-gradient(180deg, var(--accent), #c4b5fd, var(--accent)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-shadow: 0 0 60px rgba(125,211,252,0.5); animation: float 3s ease-in-out infinite; margin-bottom: 40px; }
.title-subtitle { font-size: 20px; color: var(--text-muted); letter-spacing: 4px; text-transform: uppercase; margin-bottom: 60px; }
.title-buttons { display: flex; gap: 20px; flex-wrap: wrap; justify-content: center; }

/* Battle Screen */
#battle-layout { position: relative; width: 100%; height: 100%; display: grid; grid-template-rows: auto 1fr auto; padding: 20px; gap: 16px; }
.battle-ui-top { display: flex; justify-content: space-between; align-items: center; background: rgba(21,21,36,0.8); border-radius: 12px; padding: 16px; backdrop-filter: blur(4px); }
.enemy-info { flex: 1; max-width: 350px; }
.enemy-name { font-size: 20px; color: var(--accent); margin-bottom: 8px; }
.enemy-hp-container { width: 100%; height: 24px; background: rgba(0,0,0,0.4); border-radius: 12px; overflow: hidden; }
.enemy-hp-bar { height: 100%; transition: width 0.3s ease; }
.battle-ui-center { display: flex; gap: 40px; align-items: center; justify-content: center; height: 100%; }
.battle-entity { position: relative; }
.entity-card { background: linear-gradient(135deg, rgba(21,21,36,0.9), rgba(10,10,18,0.9)); border-radius: 16px; padding: 16px; border: 2px solid transparent; transition: all 0.2s ease; }
.entity-name { font-size: 18px; color: var(--accent); text-align: center; margin-bottom: 8px; }
.entity-stats { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-muted); }
.entity-status-icons { display: flex; gap: 4px; justify-content: center; margin-top: 8px; }
.status-icon { width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; background: rgba(0,0,0,0.3); }

.battle-ui-bottom { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.move-btn { height: 64px; border: none; border-radius: 10px; background: rgba(255,255,255,0.08); color: var(--text-main); font-weight: 600; cursor: pointer; transition: all 0.2s ease; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 8px; }
.move-btn:hover { background: rgba(255,255,255,0.15); transform: translateY(-4px); box-shadow: 0 8px 20px rgba(0,0,0,0.3); }
.move-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
.move-name { font-size: 14px; color: var(--text-main); }
.move-type { font-size: 10px; text-transform: uppercase; letter-spacing: 1px; opacity: 0.7; margin-top: 4px; }

/* Party Selection */
.party-panel { position: absolute; right: 20px; top: 50%; transform: translateY(-50%); background: rgba(21,21,36,0.9); border-radius: 16px; padding: 16px; display: flex; flex-direction: column; gap: 8px; min-width: 180px; }
.party-member { position: relative; background: rgba(255,255,255,0.05); border-radius: 10px; padding: 10px; cursor: pointer; border: 2px solid transparent; transition: all 0.2s ease; }
.party-member:hover { background: rgba(255,255,255,0.1); }
.party-member.selected { border-color: var(--accent); box-shadow: inset 0 0 10px rgba(125,211,252,0.2); }

/* Inventory */
.inventory-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 10px; padding: 16px; max-height: 400px; overflow-y: auto; }
.item-slot { width: 76px; height: 76px; background: rgba(255,255,255,0.05); border-radius: 8px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s ease; font-size: 24px; }
.item-slot:hover { background: rgba(255,255,255,0.1); transform: scale(1.05); }

/* Team */
.team-member { display: flex; align-items: center; gap: 12px; padding: 12px; margin-bottom: 8px; background: rgba(255,255,255,0.05); border-radius: 10px; cursor: pointer; transition: all 0.2s ease; }
.team-member:hover { background: rgba(255,255,255,0.1); }
.team-member.selected { background: rgba(125,211,252,0.15); border-left: 3px solid var(--accent); }
.team-avatar { width: 48px; height: 48px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 24px; background: rgba(255,255,255,0.1); }
.team-info { flex: 1; }
.team-level { font-weight: bold; color: var(--accent); }
.team-affinity { font-size: 11px; text-transform: uppercase; padding: 2px 6px; border-radius: 4px; display: inline-block; }

/* Encyclopedia entry */
.enc-entry { background: rgba(255,255,255,0.03); border-radius: 12px; overflow: hidden; margin-bottom: 16px; }
.enc-header { padding: 16px; display: flex; gap: 16px; align-items: center; }
.enc-avatar { width: 80px; height: 80px; border-radius: 12px; background: rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; font-size: 36px; }
.enc-content { flex: 1; }

/* Dialog */
#dialog-container { position: absolute; bottom: 80px; left: 50%; transform: translateX(-50%); width: max-content; background: linear-gradient(180deg, rgba(21,21,36,0.98), rgba(10,10,18,0.95)); padding: 20px 32px; border-radius: 16px; margin-bottom: 24px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); max-width: 80%; }
.dialog-text { font-size: 16px; line-height: 1.8; }

/* Floating damage numbers */
.floating-text { position: absolute; pointer-events: none; animation: floatUp 1s ease-out forwards; text-shadow: 0 2px 4px rgba(0,0,0,0.5); z-index: 100; font-weight: 700; }
.floating-text.crit { color: #fbbf24; font-size: 24px; transform: scale(1.3); }
.floating-text.heal { color: #6ee7b7; }
.floating-text.super-effective { color: #c4b5fd; text-shadow: 0 0 10px rgba(196,181,253,0.5); }

/* Notification area */
#notifications { position: fixed; top: 20px; right: 20px; display: flex; flex-direction: column; gap: 8px; pointer-events: none; z-index: 100; }
.notification { padding: 12px 20px; background: rgba(10,10,18,0.9); border-radius: 8px; font-size: 14px; animation: slideIn 0.3s ease-out; border-left: 4px solid var(--accent); box-shadow: 0 4px 20px rgba(0,0,0,0.3); }
@keyframes slideIn { from { opacity: 0; transform: translateX(100%); } to { opacity: 1; transform: translateX(0); } }

/* Type badges */
.type-badge.flux { background: linear-gradient(135deg, #fbbf24, #fb923c); color: #7c2d12; }
.type-badge.crysta { background: linear-gradient(135deg, #818cf8, #6366f1); color: #312e81; }
.type-badge.void { background: linear-gradient(135deg, #7c3aed, #6d28d9); color: #4c1d95; }
.type-badge.ember { background: linear-gradient(135deg, #f97316, #ea580c); color: #7c2d12; }
.type-badge.storm { background: linear-gradient(135deg, #06b6d4, #0891b2); color: #164e63; }
.type-badge.iron { background: linear-gradient(135deg, #eab308, #ca8a04); color: #573b02; }
.type-badge.spirit { background: linear-gradient(135deg, #ec4899, #db2777); color: #831843; }

/* Story vignette */
.story-vignette { position: fixed; inset: 0; z-index: 60; pointer-events: none; opacity: 0; transition: opacity 1s ease; background: var(--bg-dark); }
.story-vignette.active { opacity: 1; }

/* Overlay for battle end */
.battle-overlay { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; z-index: 60; opacity: 0; transition: opacity 0.5s ease; }
.battle-overlay.visible { opacity: 1; }
.battle-message { font-size: 42px; font-weight: 900; text-transform: uppercase; letter-spacing: 8px; text-align: center; }
/* ── css: screens ── */
/* Inventory Screen */
.screen-title { font-size: 28px; color: var(--accent); margin-bottom: 16px; padding-bottom: 12px; border-bottom: 2px solid rgba(255,255,255,0.1); }
.screen-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.back-btn { padding: 8px 24px; background: rgba(255,255,255,0.1); color: var(--text-main); border-radius: 8px; cursor: pointer; transition: all 0.2s; font-weight: 600; }
.back-btn:hover { background: rgba(255,255,255,0.2); }

/* Item types */
.item-type-herb { color: #6ee7b7; }
.item-type-catalyst { color: #fbbf24; }
.item-type-collection { color: #ec4899; }

/* Encyclopedia */
.enc-tabs { display: flex; gap: 8px; margin-bottom: 16px; }
.enc-tab { padding: 10px 20px; background: rgba(255,255,255,0.08); border-radius: 8px; cursor: pointer; transition: all 0.2s; color: var(--text-muted); font-weight: 600; }
.enc-tab.active { background: var(--accent); color: #1e293b; }

/* Map */
#map-container { position: absolute; inset: 0; z-index: 5; }
.map-overlay { position: absolute; inset: 0; display: grid; grid-template-rows: auto 1fr auto; padding: 24px; gap: 16px; }
.map-header { display: flex; justify-content: space-between; align-items: center; background: rgba(21,21,36,0.9); border-radius: 12px; padding: 16px; backdrop-filter: blur(4px); }
.map-info h2 { font-size: 20px; color: var(--accent); margin-bottom: 8px; }
.map-stats { display: flex; gap: 16px; }

/* Zone info */
.zone-badge { display: inline-block; padding: 4px 12px; background: rgba(255,255,255,0.1); border-radius: 20px; font-size: 12px; text-transform: uppercase; letter-spacing: 1px; }

/* Shrine */
.shrine-icon { font-size: 36px; margin-bottom: 8px; display: block; animation: pulse 2s ease-in-out infinite; }

/* Shop */
.shop-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; max-height: 50vh; overflow-y: auto; }
.shop-item { background: rgba(255,255,255,0.05); border-radius: 10px; padding: 16px; text-align: center; cursor: pointer; transition: all 0.2s; border: 2px solid transparent; }
.shop-item:hover { background: rgba(255,255,255,0.1); transform: scale(1.02); }
.shop-item.bought { opacity: 0.4; cursor: default; }

/* Transform notification */
.transform-notification { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: linear-gradient(135deg, rgba(21,21,36,0.98), rgba(10,10,18,0.95)); border-radius: 20px; padding: 32px; text-align: center; box-shadow: 0 20px 60px rgba(125,211,252,0.3); z-index: 80; pointer-events: none; opacity: 0; transition: all 0.5s ease; }
.transform-notification.visible { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.transform-title { font-size: 32px; color: var(--accent); margin-bottom: 16px; animation: float 3s ease-in-out infinite; }

/* Tutorial */
.tutorial-highlight { position: relative; }
.tutorial-highlight::after { content: ''; position: absolute; inset: -4px; border-radius: 8px; box-shadow: inset 0 0 0 2px var(--accent), inset 0 0 30px rgba(125,211,252,0.3); opacity: 0; transition: opacity 0.3s; pointer-events: none; }
.tutorial-highlight:hover::after { opacity: 1; }

/* Loading */
#loading-screen { position: fixed; inset: 0; z-index: 100; background: var(--bg-dark); display: flex; flex-direction: column; align-items: center; justify-content: center; }
.loading-spinner { width: 64px; height: 64px; border: 4px solid rgba(125,211,252,0.2); border-top-color: var(--accent); border-radius: 50%; animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Screen transitions */
.screen-transition { position: fixed; inset: 0; z-index: 70; background: var(--bg-dark); pointer-events: none; opacity: 0; transition: opacity 0.5s ease; }
.screen-transition.fade-out { opacity: 1; }

/* Affinity type classes */
.type-flux { border-color: #fbbf24 !important; color: #fbbf24; background: rgba(251,191,36,0.1); }
.type-crysta { border-color: #818cf8 !important; color: #818cf8; background: rgba(129,140,248,0.1); }
.type-void { border-color: #7c3aed !important; color: #7c3aed; background: rgba(124,58,237,0.1); }
.type-ember { border-color: #f97316 !important; color: #f97316; background: rgba(249,115,22,0.1); }
.type-storm { border-color: #06b6d4 !important; color: #06b6d4; background: rgba(6,182,212,0.1); }
.type-iron { border-color: #eab308 !important; color: #eab308; background: rgba(234,179,8,0.1); }
.type-spirit { border-color: #ec4899 !important; color: #ec4899; background: rgba(236,72,153,0.1); }

/* XP bar */
.xp-bar-container { height: 8px; background: rgba(0,0,0,0.4); border-radius: 4px; overflow: hidden; margin-top: 4px; }
.xp-fill { height: 100%; width: 0%; transition: width 0.3s ease; background: linear-gradient(90deg, var(--accent), #8b5cf6); }
</style>
</head>
<body>
<div id="app"></div>
<div id="loading-screen"><div class="loading-spinner"></div><p style="margin-top: 16px; color: var(--text-muted)">Loading New Binding...</p></div>
<div id="title-screen" class="modal-overlay">
    <h1 class="title-logo">New Binding</h1>
    <p class="title-subtitle">Chronicle of the Resonant</p>
    <div class="title-buttons">
        <button id="btn-start-new" class="btn btn-primary">New Journey</button>
        <button id="btn-continue" class="btn btn-secondary">Continue</button>
    </div>
</div>
<div id="story-vignette" class="story-vignette"></div>
<div id="dialog-container" class="ui-layer"></div>
<div id="notifications"></div>
<canvas id="world-canvas" width="800" height="600"></canvas>
<canvas id="battle-canvas" width="800" height="600"></canvas>
<div id="transform-notification" class="transform-notification">
    <h2 class="transform-title">Evolution!</h2>
</div>
<script>
'use strict';

// ── const: AFFINITIES ──
/* Type System - 7 Resonance Affinities */
const AFFINITIES = {
    FLUX: { id: 'flux', name: 'Flux', color: '#fbbf24', description: "Unstable energy that shatters barriers." },
    CRYSTA: { id: 'crysta', name: 'Crysta', color: '#818cf8', description: "Hard crystal structures that deflect attacks." },
    VOID: { id: 'void', name: 'Void', color: '#7c3aed', description: "Manipulates shadows and reality itself." },
    EMBER: { id: 'ember', name: 'Ember', color: '#f97316', description: "Burning heat and explosive fire." },
    STORM: { id: 'storm', name: 'Storm', color: '#06b6d4', description: "Electrical discharge and atmospheric force." },
    IRON: { id: 'iron', name: 'Iron', color: '#eab308', description: "Unbreakable metal and industrial might." },
    SPIRIT: { id: 'spirit', name: 'Spirit', color: '#ec4899', description: "Ethereal essence that transcends form." }
};

// ── const: TYPE_CHART ──
const TYPE_CHART = {
    flux: { strong_against: ['crysta'], weak_against: ['ember'] },
    crysta: { strong_against: ['flux'], weak_against: ['storm'] },
    void: { strong_against: ['spirit'], weak_against: ['iron'] },
    ember: { strong_against: ['void', 'crysta'], weak_against: ['crysta', 'void'] },
    storm: { strong_against: ['flux', 'ember'], weak_against: ['crysta', 'void'] },
    iron: { strong_against: ['storm', 'ember'], weak_against: ['spirit'] },
    spirit: { strong_against: ['iron', 'storm', 'flux', 'ember'], weak_against: ['flux'] }
};

// ── const: TYPE_COLORS ──
const TYPE_COLORS = { flux: '#fbbf24', crysta: '#818cf8', void: '#7c3aed', ember: '#f97316', storm: '#06b6d4', iron: '#eab308', spirit: '#ec4899' };

// ── const: CREATURE_DATA ──
/* Base Creatures - 18 unique entities */
const CREATURE_DATA = [
    // Starter Trio
    { id: 'lumivolt', name: 'Lumivolt', affinity: 'flux', lore: "A small creature of living lightning that dances on currents of energy.", hp: 28, atk: 14, def: 10, spd: 16, abilities: [
        { id: 'spark', name: 'Static Spark', power: 12, accuracy: 95, pp: 25, type: 'flux', effect: 'none' },
        { id: 'electro-blast', name: 'Electro Blast', power: 22, accuracy: 88, pp: 15, type: 'flux', effect: 'speed_down' },
        { id: 'overcharge', name: 'Overcharge', power: 28, accuracy: 78, pp: 10, type: 'flux', effect: 'self_heal_10' },
        { id: 'voltic-fury', name: 'Voltic Fury', power: 38, accuracy: 65, pp: 6, type: 'flux', effect: 'crit_boost' }
    ]},
    { id: 'petallia', name: 'Petallia', affinity: 'crysta', lore: "A crystalline being that forms beautiful geometric patterns from pure energy.", hp: 30, atk: 12, def: 16, spd: 14, abilities: [
        { id: 'ice-shard', name: 'Frost Shard', power: 10, accuracy: 98, pp: 25, type: 'crysta', effect: 'none' },
        { id: 'glacial-surge', name: 'Glacial Surge', power: 24, accuracy: 90, pp: 12, type: 'crysta', effect: 'speed_down' },
        { id: 'freezing-gaze', name: 'Freezing Gaze', power: 18, accuracy: 85, pp: 15, type: 'crysta', effect: 'frost' },
        { id: 'ice-wall', name: 'Ice Wall', power: 20, accuracy: 95, pp: 12, type: 'crysta', effect: 'shield_def_30' }
    ]},
    { id: 'obsidia', name: 'Obsidia', affinity: 'void', lore: "Shadows manifest as this creature, able to bend light and reality.", hp: 26, atk: 16, def: 12, spd: 18, abilities: [
        { id: 'shadow-strike', name: 'Shadow Strike', power: 14, accuracy: 95, pp: 25, type: 'void', effect: 'none' },
        { id: 'dark-pulse', name: 'Dark Pulse', power: 26, accuracy: 88, pp: 12, type: 'void', effect: 'speed_down' },
        { id: 'phantom-vein', name: 'Phantom Vein', power: 30, accuracy: 75, pp: 8, type: 'void', effect: 'self_heal_15' },
        { id: 'eclipse', name: 'Eclipse', power: 42, accuracy: 60, pp: 5, type: 'void', effect: 'crit_boost' }
    ]},

    // Zone 1 Creatures
    { id: 'ferrum', name: 'Ferrum', affinity: 'iron', lore: "A mechanical construct built from scrap metal that seeks to protect its territory.", hp: 32, atk: 18, def: 18, spd: 10, abilities: [
        { id: 'metal-bash', name: 'Metal Bash', power: 16, accuracy: 95, pp: 25, type: 'iron', effect: 'none' },
        { id: 'steel-slam', name: 'Steel Slam', power: 28, accuracy: 85, pp: 12, type: 'iron', effect: 'speed_down' },
        { id: 'reinforce', name: 'Reinforce', power: 0, accuracy: 100, pp: 15, type: 'iron', effect: 'def_up_20' },
        { id: 'heavy-fist', name: 'Heavy Fist', power: 36, accuracy: 70, pp: 8, type: 'iron', effect: 'self_heal_12' }
    ]},
    { id: 'wraithwing', name: 'Wraithwing', affinity: 'spirit', lore: "A spectral bird that haunts the ruins of fallen civilizations.", hp: 24, atk: 20, def: 10, spd: 20, abilities: [
        { id: 'phantom-strike', name: 'Phantom Strike', power: 15, accuracy: 95, pp: 25, type: 'spirit', effect: 'none' },
        { id: 'shadow-dive', name: 'Shadow Dive', power: 26, accuracy: 85, pp: 12, type: 'spirit', effect: 'crit_boost' },
        { id: 'soul-sever', name: 'Soul Sever', power: 34, accuracy: 70, pp: 8, type: 'spirit', effect: 'self_heal_10' },
        { id: 'spectral-wake', name: 'Spectral Wake', power: 22, accuracy: 95, pp: 12, type: 'spirit', effect: 'speed_up' }
    ]},

    // Zone 2 Creatures
    { id: 'infernum', name: 'Infernum', affinity: 'ember', lore: "A fiery construct born from the heart of ancient volcanoes.", hp: 34, atk: 20, def: 16, spd: 12, abilities: [
        { id: 'fire-bolt', name: 'Fire Bolt', power: 18, accuracy: 95, pp: 25, type: 'ember', effect: 'none' },
        { id: 'flame-ring', name: 'Flame Ring', power: 20, accuracy: 90, pp: 15, type: 'ember', effect: 'burn' },
        { id: 'inferno-fury', name: 'Inferno Fury', power: 32, accuracy: 75, pp: 8, type: 'ember', effect: 'none' },
        { id: 'volcanic-eruption', name: 'Volcanic Eruption', power: 40, accuracy: 65, pp: 6, type: 'ember', effect: 'burn_2' }
    ]},
    { id: 'tempestrix', name: 'Tempestrix', affinity: 'storm', lore: "A creature of swirling clouds that controls lightning with telekinetic force.", hp: 30, atk: 18, def: 14, spd: 22, abilities: [
        { id: 'thunder-jolt', name: 'Thunder Jolt', power: 16, accuracy: 95, pp: 25, type: 'storm', effect: 'none' },
        { id: 'lightning-swarm', name: 'Lightning Swarm', power: 24, accuracy: 88, pp: 12, type: 'storm', effect: 'burn_30' },
        { id: 'cyclone-whirl', name: 'Cyclone Whirl', power: 26, accuracy: 85, pp: 10, type: 'storm', effect: 'speed_down' },
        { id: 'thunder-cage', name: 'Thunder Cage', power: 34, accuracy: 70, pp: 6, type: 'storm', effect: 'paralyze' }
    ]},

    // Zone 3 Creatures
    { id: 'nethshade', name: 'Nethshade', affinity: 'void', lore: "A manifestation of the void that devours light and hope.", hp: 36, atk: 22, def: 14, spd: 18, abilities: [
        { id: 'void-stab', name: 'Void Stab', power: 18, accuracy: 95, pp: 25, type: 'void', effect: 'none' },
        { id: 'dark-swarm', name: 'Dark Swarm', power: 26, accuracy: 90, pp: 12, type: 'void', effect: 'speed_down' },
        { id: 'void-wisp', name: 'Void Wisp', power: 30, accuracy: 85, pp: 10, type: 'void', effect: 'self_heal_12' },
        { id: 'abyss-call', name: 'Abyss Call', power: 44, accuracy: 60, pp: 5, type: 'void', effect: 'crit_boost' }
    ]},
    { id: 'quartzix', name: 'Quartzix', affinity: 'crysta', lore: "A living crystal that sings with the frequency of pure energy.", hp: 32, atk: 16, def: 20, spd: 15, abilities: [
        { id: 'shard-thrust', name: 'Shard Thrust', power: 14, accuracy: 98, pp: 25, type: 'crysta', effect: 'none' },
        { id: 'prism-shield', name: 'Prism Shield', power: 18, accuracy: 95, pp: 15, type: 'crysta', effect: 'shield_def_40' },
        { id: 'shatter-point', name: 'Shatter Point', power: 28, accuracy: 80, pp: 10, type: 'crysta', effect: 'crit_boost' },
        { id: 'refractive-blast', name: 'Refractive Blast', power: 36, accuracy: 65, pp: 6, type: 'crysta', effect: 'speed_down' }
    ]},

    // Zone 4 Creatures
    { id: 'pyreheart', name: 'Pyreheart', affinity: 'ember', lore: "A being of pure conflagration that burns with cosmic fire.", hp: 38, atk: 24, def: 16, spd: 14, abilities: [
        { id: 'blaze-fang', name: 'Blaze Fang', power: 20, accuracy: 95, pp: 25, type: 'ember', effect: 'none' },
        { id: 'inferno-dash', name: 'Inferno Dash', power: 26, accuracy: 88, pp: 12, type: 'ember', effect: 'speed_up' },
        { id: 'bonfire-surge', name: 'Bonfire Surge', power: 30, accuracy: 80, pp: 10, type: 'ember', effect: 'burn' },
        { id: 'apocalypse-flare', name: 'Apocalypse Flare', power: 42, accuracy: 65, pp: 5, type: 'ember', effect: 'burn_30' }
    ]},
    { id: 'titanforged', name: 'Titanforged', affinity: 'iron', lore: "A massive war construct that channels the power of ancient factories.", hp: 40, atk: 22, def: 22, spd: 10, abilities: [
        { id: 'plating-swing', name: 'Plating Swing', power: 18, accuracy: 95, pp: 25, type: 'iron', effect: 'none' },
        { id: 'industrial-stomp', name: 'Industrial Stomp', power: 26, accuracy: 85, pp: 12, type: 'iron', effect: 'speed_down' },
        { id: 'hardened-core', name: 'Hardened Core', power: 0, accuracy: 100, pp: 12, type: 'iron', effect: 'def_up_30' },
        { id: 'planet-crush', name: 'Planet Crush', power: 40, accuracy: 65, pp: 6, type: 'iron', effect: 'self_heal_15' }
    ]},

    // Zone 5 Creatures
    { id: 'zephyron', name: 'Zephyron', affinity: 'storm', lore: "A storm giant that channels atmospheric pressure into devastating attacks.", hp: 36, atk: 24, def: 18, spd: 16, abilities: [
        { id: 'air-blast', name: 'Air Blast', power: 16, accuracy: 98, pp: 25, type: 'storm', effect: 'none' },
        { id: 'hurricane-spin', name: 'Hurricane Spin', power: 24, accuracy: 88, pp: 12, type: 'storm', effect: 'speed_down' },
        { id: 'tempest-call', name: 'Tempest Call', power: 28, accuracy: 80, pp: 10, type: 'storm', effect: 'speed_up' },
        { id: 'typhoon-storm', name: 'Typhoon Storm', power: 40, accuracy: 65, pp: 6, type: 'storm', effect: 'paralyze' }
    ]},
    { id: 'etheria', name: 'Etheria', affinity: 'spirit', lore: "A spirit bound to the world itself that manipulates souls and luck.", hp: 34, atk: 22, def: 16, spd: 20, abilities: [
        { id: 'spirit-touch', name: 'Spirit Touch', power: 14, accuracy: 98, pp: 25, type: 'spirit', effect: 'heal_10' },
        { id: 'phantom-flurry', name: 'Phantom Flurry', power: 26, accuracy: 85, pp: 12, type: 'spirit', effect: 'crit_boost' },
        { id: 'soul-link', name: 'Soul Link', power: 0, accuracy: 100, pp: 10, type: 'spirit', effect: 'self_heal_15' },
        { id: 'ether-blade', name: 'Ether Blade', power: 38, accuracy: 70, pp: 6, type: 'spirit', effect: 'crit_boost' }
    ]},

    // Zone 6 Creatures (Guardians/Bosses)
    { id: 'golemx', name: 'Golemx', affinity: 'iron', lore: "The iron guardian of the first zone, a massive construct from ancient machinery.", hp: 80, atk: 28, def: 35, spd: 12, abilities: [
        { id: 'golem-punch', name: 'Golem Punch', power: 22, accuracy: 95, pp: 25, type: 'iron', effect: 'none' },
        { id: 'steel-tremor', name: 'Steel Tremor', power: 28, accuracy: 85, pp: 12, type: 'iron', effect: 'speed_down' },
        { id: 'fortification', name: 'Fortification', power: 0, accuracy: 100, pp: 10, type: 'iron', effect: 'def_up_40' },
        { id: 'tectonic-slash', name: 'Tectonic Slash', power: 38, accuracy: 75, pp: 8, type: 'iron', effect: 'none' }
    ]},
    { id: 'magmaheart', name: 'Magmaheart', affinity: 'ember', lore: "The heart of the volcanic zone, a being of pure magma and fire.", hp: 90, atk: 30, def: 28, spd: 14, abilities: [
        { id: 'lava-spit', name: 'Lava Spit', power: 24, accuracy: 95, pp: 25, type: 'ember', effect: 'burn' },
        { id: 'magma-dome', name: 'Magma Dome', power: 30, accuracy: 85, pp: 10, type: 'ember', effect: 'shield_def_50' },
        { id: 'volcanic-eruption', name: 'Volcanic Eruption', power: 40, accuracy: 70, pp: 6, type: 'ember', effect: 'burn_30' },
        { id: 'inferno-heart', name: 'Inferno Heart', power: 35, accuracy: 80, pp: 10, type: 'ember', effect: 'self_heal_20' }
    ]},
    { id: 'voidwalker', name: 'Voidwalker', affinity: 'void', lore: "A being from beyond reality that walks the boundary between worlds.", hp: 85, atk: 32, def: 24, spd: 20, abilities: [
        { id: 'shadow-dagger', name: 'Shadow Dagger', power: 26, accuracy: 95, pp: 25, type: 'void', effect: 'none' },
        { id: 'dark-swarm', name: 'Dark Swarm', power: 28, accuracy: 85, pp: 10, type: 'void', effect: 'speed_down' },
        { id: 'void-step', name: 'Void Step', power: 36, accuracy: 75, pp: 8, type: 'void', effect: 'crit_boost' },
        { id: 'reality-break', name: 'Reality Break', power: 42, accuracy: 70, pp: 6, type: 'void', effect: 'self_heal_15' }
    ]},
    { id: 'soulguard', name: 'Soulguard', affinity: 'spirit', lore: "The guardian of souls that watches over all living creatures.", hp: 75, atk: 26, def: 30, spd: 18, abilities: [
        { id: 'soul-aura', name: 'Soul Aura', power: 18, accuracy: 98, pp: 25, type: 'spirit', effect: 'none' },
        { id: 'phantom-fang', name: 'Phantom Fang', power: 26, accuracy: 85, pp: 12, type: 'spirit', effect: 'crit_boost' },
        { id: 'soul-shield', name: 'Soul Shield', power: 0, accuracy: 100, pp: 10, type: 'spirit', effect: 'shield_def_40' },
        { id: 'siphon-soul', name: 'Siphon Soul', power: 32, accuracy: 80, pp: 8, type: 'spirit', effect: 'self_heal_20' }
    ]}
];

// ── const: ITEMS ──
/* Item Database */
const ITEMS = {
    // Collection Items (3 tiers)
    capture_basic: { id: 'capture_basic', name: 'Resonance Orb', type: 'collection', tier: 1, rarity: 'common', description: 'A basic resonant orb that captures creatures.', effect: 'capture' },
    capture_adv: { id: 'capture_adv', name: 'Prism Orb', type: 'collection', tier: 2, rarity: 'uncommon', description: 'A refined orb with enhanced capture properties.', effect: 'capture' },
    capture_elite: { id: 'capture_elite', name: 'Void Core', type: 'collection', tier: 3, rarity: 'rare', description: 'A powerful core that guarantees high capture success.', effect: 'capture' },
    
    // Restoration Consumables
    potion_small: { id: 'potion_small', name: 'Essence Vial', type: 'heal', effect: 'heal_25' },
    potion_medium: { id: 'potion_medium', name: 'Essence Flask', type: 'heal', effect: 'heal_50' },
    potion_large: { id: 'potion_large', name: 'Primal Elixir', type: 'heal', effect: 'heal_full' },
    bandage: { id: 'bandage', name: 'Field Bandage', type: 'heal', effect: 'heal_30' },
    
    // Catalysts for Transformation
    catalyst_basic: { id: 'catalyst_basic', name: 'Resonance Crystal', type: 'catalyst', tier: 1, description: 'Transforms creatures at level 20+.' },
    catalyst_advanced: { id: 'catalyst_advanced', name: 'Prism Shard', type: 'catalyst', tier: 2, description: 'Transforms creatures at level 35+.' },
    catalyst_elite: { id: 'catalyst_elite', name: 'Void Fragment', type: 'catalyst', tier: 3, description: 'Transforms creatures at level 45+.' },
    
    // Field Tools
    map_scroll: { id: 'map_scroll', name: 'World Map Scroll', type: 'tool', effect: 'view_map' },
    compass: { id: 'compass', name: 'Resonance Compass', type: 'tool', effect: 'locate_nearby' }
};

// ── const: ZONES ──
/* Zone Definitions */
const ZONES = [
    { id: 0, name: 'Shimmering Fields', color: '#86efac', gradient: 'linear-gradient(180deg, #2d5a3e 0%, #166534 100%)', background: 'linear-gradient(180deg, #2d5a3e 0%, #166534 100%)', particleType: 'sparkle', creatureRates: { flux: 0.3, crysta: 0.2, void: 0.2, ember: 0.1, storm: 0.1, iron: 0.1, spirit: 0.1 }, bossId: 'golemx' },
    { id: 1, name: 'Crystal Caverns', color: '#818cf8', gradient: 'linear-gradient(180deg, #3730a3 0%, #4c1d95 100%)', background: 'linear-gradient(180deg, #3730a3 0%, #4c1d95 100%)', particleType: 'crystal', creatureRates: { flux: 0.2, crysta: 0.3, void: 0.2, ember: 0.1, storm: 0.1, iron: 0.1, spirit: 0.1 }, bossId: null },
    { id: 2, name: 'Obsidian Wastes', color: '#7c3aed', gradient: 'linear-gradient(180deg, #4a044e 0%, #2e1065 100%)', background: 'linear-gradient(180deg, #4a044e 0%, #2e1065 100%)', particleType: 'ember', creatureRates: { flux: 0.1, crysta: 0.1, void: 0.3, ember: 0.2, storm: 0.1, iron: 0.1, spirit: 0.1 }, bossId: null },
    { id: 3, name: 'Prism Depths', color: '#fbbf24', gradient: 'linear-gradient(180deg, #713f12 0%, #9a3412 100%)', background: 'linear-gradient(180deg, #713f12 0%, #9a3412 100%)', particleType: 'shard', creatureRates: { flux: 0.15, crysta: 0.25, void: 0.2, ember: 0.2, storm: 0.15, iron: 0.15, spirit: 0.1 }, bossId: 'magmaheart' },
    { id: 4, name: 'Storm Peaks', color: '#06b6d4', gradient: 'linear-gradient(180deg, #0c4a6e 0%, #0f766e 100%)', background: 'linear-gradient(180deg, #0c4a6e 0%, #0f766e 100%)', particleType: 'lightning', creatureRates: { flux: 0.2, crysta: 0.2, void: 0.15, ember: 0.15, storm: 0.25, iron: 0.1, spirit: 0.1 }, bossId: null },
    { id: 5, name: 'Ethereal Sanctum', color: '#ec4899', gradient: 'linear-gradient(180deg, #831843 0%, #7f1d1d 100%)', background: 'linear-gradient(180deg, #831843 0%, #7f1d1d 100%)', particleType: 'spirit', creatureRates: { flux: 0.2, crysta: 0.2, void: 0.15, ember: 0.15, storm: 0.2, iron: 0.1, spirit: 0.3 }, bossId: 'voidwalker' }
];

// ── const: STORY_NODES ──
/* Story Nodes */
const STORY_NODES = [
    { zone: 0, text: "The journey begins in the Shimmering Fields... A mysterious force awakens within you.", color: '#86efac', title: "Awakening" },
    { zone: 1, text: "Crystal Caverns hold secrets of the ancients. Your path grows clearer...", color: '#818cf8', title: "The Crystalline Path" },
    { zone: 3, text: "Prism Depths burn with a strange heat. The guardian stirs... Defy it!", color: '#fbbf24', title: "Volcanic Challenge" },
    { zone: 5, text: "The Ethereal Sanctum pulses with otherworldly power. You stand ready...", color: '#ec4899', title: "Final Confrontation" }
];

// ── const: STATUS_EFFECTS ──
/* Status Effects */
const STATUS_EFFECTS = {
    burn: { name: 'Burn', icon: '🔥', duration: 3, description: 'Takes damage each turn' },
    frost: { name: 'Frost', icon: '❄️', duration: 3, description: 'Speed reduced by 50%' },
    shock: { name: 'Shock', icon: '⚡', duration: 2, description: 'Chance to skip a turn' },
    barrier: { name: 'Barrier', icon: '🛡️', duration: 1, description: 'Deflects one hit' },
    regen: { name: 'Regen', icon: '💚', duration: 3, description: 'Heals each turn' }
};

// ── const: TRANSFORMATIONS ──
/* Transform Forms */
const TRANSFORMATIONS = [
    { base: 'lumivolt', form: 'lumivolt-primal', prefix: 'Primal', levelMin: 25, catalyst: 'catalyst_basic' },
    { base: 'petallia', form: 'petallia-primal', prefix: 'Primal', levelMin: 25, catalyst: 'catalyst_basic' },
    { base: 'obsidia', form: 'obsidia-primal', prefix: 'Primal', levelMin: 25, catalyst: 'catalyst_basic' },
    { base: 'ferrum', form: 'ferrum-primal', prefix: 'Primal', levelMin: 30, catalyst: 'catalyst_advanced' },
    { base: 'wraithwing', form: 'wraithwing-primal', prefix: 'Primal', levelMin: 25, catalyst: 'catalyst_basic' },
    { base: 'infernum', form: 'infernum-primal', prefix: 'Primal', levelMin: 30, catalyst: 'catalyst_advanced' },
    { base: 'tempestrix', form: 'tempestrix-primal', prefix: 'Primal', levelMin: 25, catalyst: 'catalyst_basic' },
    { base: 'nethshade', form: 'nethshade-primal', prefix: 'Primal', levelMin: 30, catalyst: 'catalyst_advanced' },
    { base: 'quartzix', form: 'quartzix-primal', prefix: 'Primal', levelMin: 25, catalyst: 'catalyst_basic' }
];

// ── const: ABILITIES ──
/* Ability Registry */
const ABILITIES = {}; // Will be populated from creature data

// ── function: initAbilities ──
function initAbilities() {
    CREATURE_DATA.forEach(c => {
        c.abilities.forEach(abi => {
            if (!ABILITIES[abi.id]) {
                ABILITIES[abi.id] = {
                    id: abi.id,
                    name: abi.name,
                    power: abi.power || 10,
                    accuracy: abi.accuracy,
                    pp: abi.pp,
                    type: abi.type,
                    effect: abi.effect
                };
            }
        });
    });
}

// ── function: getTypeMultiplier ──
// Get damage multiplier from type chart
function getTypeMultiplier(attackerType, defenderType) {
    const chart = TYPE_CHART[attackerType];
    if (chart && chart.strong_against.includes(defenderType)) return 1.5; // Super effective
    return 1.0;
}

// ── function: getTypeDamage ──
// Check for weakness/resistance
function getTypeDamage(attackerType, defenderType) {
    const multiplier = getTypeMultiplier(attackerType, defenderType);
    if (multiplier > 1) return { multiplier: multiplier, isWeakness: true };
    return { multiplier: 1.0, isWeakness: false };
}

// ── function: calcStats ──
// Calculate creature stats based on level and base stats
function calcStats(baseCreature, level) {
    // Level scaling for stats
    const levelMultiplier = (level / 20); // Max at level 50
    
    return {
        id: baseCreature.id,
        name: baseCreature.name,
        affinity: baseCreature.affinity,
        lore: baseCreature.lore,
        
        // Level-up scaled stats
        maxHp: Math.floor(30 * levelMultiplier + (baseCreature.hp - 30) * levelMultiplier / 10),
        atk: Math.floor(14 * levelMultiplier + (baseCreature.atk - 14) * levelMultiplier / 10),
        def: Math.floor(12 * levelMultiplier + (baseCreature.def - 12) * levelMultiplier / 10),
        spd: Math.floor(14 * levelMultiplier + (baseCreature.spd - 14) * levelMultiplier / 10),
        
        abilities: baseCreature.abilities.map(a => ({
            ...a,
            currentPP: a.pp
        })),
        
        // Form data for transformations
        formPrefix: null,
        isTransformed: false,
        
        xpToNextLevel: Math.floor(100 * (level + 1))
    };
}

// ── function: calculateDamage ──
// Calculate damage using the formula provided
function calculateDamage(attacker, defender, ability) {
    // Get type multiplier
    const typeData = getTypeDamage(ability.type, defender.affinity);
    
    // Apply STAB bonus if same affinity
    let stabMultiplier = 1.0;
    if (ability.type === attacker.affinity) {
        stabMultiplier = 1.25;
    }
    
    // Critical hit check
    const critChance = ability.power > 30 ? 0.06 : 0.04; // Higher power moves more likely to crit
    const isCrit = Math.random() < critChance;
    const critMultiplier = isCrit ? 1.5 : 1.0;
    
    // Damage formula: (0.4 × Level × ATK / DEF × Multiplier) + small random
    const baseDamage = Math.floor(0.4 * attacker.level * attacker.atk / defender.def);
    const damage = Math.max(1, Math.floor(baseDamage * typeData.multiplier * stabMultiplier * critMultiplier));
    
    return {
        damage: damage,
        isCrit: isCrit,
        isSuperEffective: typeData.isWeakness,
        multiplier: typeData.multiplier * stabMultiplier * critMultiplier
    };
}

// ── function: applyStatusEffect ──
// Apply status effects
function applyStatusEffect(target, effectId) {
    const effect = STATUS_EFFECTS[effectId];
    if (!effect) return null;
    
    let effectKey = 'status-' + effectId;
    if (!target.effects[effectKey]) {
        target.effects[effectKey] = { ...effect, duration: effect.duration };
        showStatusNotification(target.name, effect.icon);
    }
    return target.effects[effectKey];
}

// ── function: healEntity ──
// Healers and status healers
function healEntity(entity, amount) {
    const healAmount = Math.min(amount, entity.maxHp - entity.hp);
    entity.hp = Math.min(entity.hp + healAmount, entity.maxHp);
    return healAmount;
}

// ── function: calculateCaptureRate ──
// Calculate capture success rate
function calculateCaptureRate(target, itemTier) {
    const tierMultipliers = { 1: 0.6, 2: 0.8, 3: 1.0 };
    const targetHealthFactor = target.hp / target.maxHp;
    
    // Base rate decreases as HP gets lower
    let baseRate = (1 - target.healthPercent) * 0.5;
    return Math.min(1.0, (baseRate + tierMultipliers[itemTier] - 0.4) * 1.2);
}

// ── function: spawnWildCreature ──
// Create a wild creature encounter
function spawnWildCreature(zoneId) {
    const zone = ZONES[zoneId];
    
    // Pick a creature based on zone rates
    const typeRoll = Math.random();
    let cumulative = 0;
    let selectedType = 'flux';
    
    for (const [type, rate] of Object.entries(zone.creatureRates)) {
        cumulative += rate;
        if (typeRoll < cumulative) {
            selectedType = type;
        }
    }
    
    // Find a creature with this affinity in the current zone
    const creaturesInZone = CREATURE_DATA.filter(c => {
        if (zone.bossId && c.id === zone.bossId) return true; // Bosses always available
        // Skip bosses for wild encounters unless it's that zone's boss
        if (c.hp > 50 && !zone.bossId) return false;
        return true;
    });
    
    // Filter by affinity and pick one
    const matching = creaturesInZone.filter(c => c.affinity === selectedType);
    const creature = matching[Math.floor(Math.random() * matching.length)];
    
    // Scale wild creature stats based on level range
    const levelRange = [15, 25];
    const level = Math.floor(Math.random() * (levelRange[1] - levelRange[0] + 1)) + levelRange[0];
    
    return calcStats(creature, level);
}

// ── function: showFloatingText ──
// Show floating damage text
function showFloatingText(x, y, text, type = 'damage') {
    const el = document.createElement('div');
    el.className = 'floating-text';
    el.style.left = x + 'px';
    el.style.top = (y - 20) + 'px';
    el.textContent = text;
    
    if (type === 'crit') el.classList.add('crit');
    if (type === 'heal') el.classList.add('heal');
    if (type === 'super-effective') el.classList.add('super-effective');
    
    document.getElementById('battle-canvas').appendChild(el);
    
    setTimeout(() => {
        el.remove();
    }, 1000);
}

// ── function: screenShake ──
// Screen shake effect
function screenShake() {
    const canvas = document.getElementById('battle-canvas');
    canvas.style.animation = 'shake 0.3s ease-in-out';
    setTimeout(() => {
        canvas.style.animation = '';
    }, 300);
}

// ── function: showTransformNotification ──
// Show transformation notification
function showTransformNotification(baseId) {
    const notif = document.querySelector('.transform-notification');
    if (notif) {
        notif.classList.add('visible');
        notif.querySelector('.transform-title').textContent = 
            `Evolution: ${getCreatureById(baseId).name} → ${getCreatureById(baseId + '-primal').name}`;
        
        setTimeout(() => {
            notif.classList.remove('visible');
        }, 4000);
    }
}

// ── function: getCreatureById ──
// Get creature by ID
function getCreatureById(id) {
    const base = CREATURE_DATA.find(c => c.id === id.split('-primal')[0]) || CREATURE_DATA[0];
    return calcStats(base, 1); // Return base stats
}

// ── function: checkStoryNode ──
function checkStoryNode() {
    const currentZoneIdx = GAME.zoneIndex;
    const node = STORY_NODES.find(n => n.zone === currentZoneIdx);
    
    if (node) {
        showStoryVignette(node);
    }
}

// ── function: showStoryVignette ──
function showStoryVignette(node) {
    // Clear previous vignette
    const oldVignette = document.querySelector('.story-vignette');
    if (oldVignette) oldVignette.remove();
    
    const vignette = document.getElementById('story-vignette');
    if (!vignette || !node) return;
    
    // Set color and show
    vignette.style.background = node.color;
    vignette.classList.add('active');
    
    // Create or get dialog container
    const dialog = ensureDialogContainer();
    Array.from(dialog.children).forEach(c => c.remove());
    
    // Create new title and text elements
    const titleEl = document.createElement('h3');
    titleEl.style.margin = '0 0 12px 0';
    titleEl.style.fontSize = '18px';
    titleEl.style.color = '#ffffff';
    
    const textEl = document.createElement('p');
    textEl.textContent = node.text;
    textEl.style.margin = '0';
    textEl.style.lineHeight = '1.6';
    
    dialog.appendChild(titleEl);
    dialog.appendChild(textEl);
    
    // Close after reading (after 8 seconds)
    setTimeout(() => {
        closeStoryVignette();
    }, 8000);
}

// ── function: closeStoryVignette ──
function closeStoryVignette() {
    const vignette = document.querySelector('.story-vignette');
    if (vignette) {
        vignette.classList.remove('active');
    }
}

// ── function: handleInput ──
/* Game Input Handling */
function handleInput(e) {
    if (GAME.gameState !== 'overworld') return;
    
    const keyMap = {
        'w': [-1, 0], 's': [1, 0], 'a': [0, -1], 'd': [0, 1],
        'ArrowUp': [-1, 0], 'ArrowDown': [1, 0], 'ArrowLeft': [0, -1], 'ArrowRight': [0, 1],
        ' ': () => GAME.toggleDebugMode()
    };
    
    const action = keyMap[e.key] || null;
    if (action) {
        e.preventDefault();
        const delta = typeof action === 'function' ? action() : action;
        
        const newX = GAME.position.x + delta[0];
        const newY = GAME.position.y + delta[1];
        
        // Boundary checks
        if (newX >= 0 && newX < ZONES.length && newY >= 0 && newY < ZONES.length) {
            GAME.position = { x: newX, y: newY };
            GAME.render();
            
            // Check for boss/anchor
            const zone = ZONES[newY];
            if (zone.bossId && !GAME.party.some(p => p.id === zone.bossId)) {
                handleBossEncounter(zone);
            }
        }
    }
}

// ── function: handleCanvasKey ──
function handleCanvasKey(e) {
    if (GAME.gameState !== 'overworld') return;
    
    const keyMap = {
        'w': [-1, 0], 's': [1, 0], 'a': [0, -1], 'd': [0, 1],
        'ArrowUp': [-1, 0], 'ArrowDown': [1, 0], 'ArrowLeft': [0, -1], 'ArrowRight': [0, 1],
        'e': () => { if (GAME.inventory.potion_small > 0) { GAME.usePotion('potion_small', 0); return [-1,-1]; } },
        'b': () => { if (GAME.party.length < 6 && GAME.party.every(p => p.hp >= p.maxHp)) { GAME.openPartyMenu(); } return [-1,-1]; }
    };
    
    const action = keyMap[e.key] || null;
    if (action) {
        e.preventDefault();
        const delta = typeof action === 'function' ? action() : action;
        
        if (!delta) return;
        
        const newX = GAME.position.x + delta[0];
        const newY = GAME.position.y + delta[1];
        
        if (newX >= 0 && newX < ZONES.length && newY >= 0 && newY < ZONES.length) {
            GAME.position = { x: newX, y: newY };
            GAME.render();
            
            const zone = ZONES[newY];
            if (zone.bossId && !GAME.party.some(p => p.id === zone.bossId)) {
                handleBossEncounter(zone);
            }
        }
    }
}

// ── function: toggleDebugMode ──
function toggleDebugMode() {
    GAME.debugMode = !GAME.debugMode || false;
    return [-1,-1];
}

// ── function: ensureDialogContainer ──
/* Story Vignette System - Fixed */

// Create or get vignette dialog container
function ensureDialogContainer() {
    let container = document.getElementById('dialog-container');
    if (!container) {
        const div = document.createElement('div');
        div.id = 'dialog-container';
        div.className = 'ui-layer';
        
        const contentDiv = document.createElement('div');
        contentDiv.style.padding = '20px';
        contentDiv.style.background = 'rgba(10,10,18,0.95)';
        contentDiv.style.borderRadius = '16px';
        contentDiv.style.marginBottom = '24px';
        contentDiv.style.maxWidth = '80%';
        contentDiv.style.color = 'var(--text-main)';
        
        const titleEl = document.createElement('h3');
        titleEl.style.margin = '0 0 12px 0';
        titleEl.style.fontSize = '18px';
        
        const textEl = document.createElement('p');
        textEl.style.margin = '0';
        
        contentDiv.appendChild(titleEl);
        contentDiv.appendChild(textEl);
        div.appendChild(contentDiv);
        
        return div;
    }
    return container;
}

// ── main ──
/* =====================================================
   NEW BINDING - CHRONICLE OF THE RESONANT  
   Main Game Initialization (v8)
   ===================================================== */

// Initialize abilities first
initAbilities();

/* =====================================================
   MAIN GAME OBJECT
   ===================================================== */

const GAME = {
    canvas: null,
    ctx: null,
    width: 800,
    height: 600,
    tileSize: 40,
    
    gameState: 'title',
    party: [],
    inventory: {},
    zoneIndex: 0,
    position: { x: 1, y: 1 },
    
    battleState: null,
    enemyIdx: null,
    activeEnemy: null,
    
    animations: [],
    particles: [],
    
    init() {
        this.canvas = document.getElementById('world-canvas');
        this.ctx = this.canvas.getContext('2d');
        
        this.resize();
        window.addEventListener('resize', () => this.resize());
        
        const startBtn = document.getElementById('btn-start-new');
        const continueBtn = document.getElementById('btn-continue');
        
        if (startBtn) {
            startBtn.onclick = () => this.startNewGame();
        }
        if (continueBtn) {
            continueBtn.onclick = () => {
                const saved = localStorage.getItem('newBindingSave');
                if (saved) {
                    try {
                        const saveData = JSON.parse(saved);
                        this.loadGame(saveData);
                    } catch(e) {
                        console.warn('Failed to load save:', e);
                    }
                } else {
                    this.startNewGame();
                }
            };
        }
        
        window.addEventListener('keydown', (e) => this.handleInput(e));
        if (this.canvas) {
            this.canvas.addEventListener('keydown', (e) => this.handleCanvasKey(e));
        }
    },
    
    resize() {
        const worldCanvas = document.getElementById('world-canvas');
        if (!worldCanvas) return;
        
        const dpr = window.devicePixelRatio || 1;
        this.width = worldCanvas.clientWidth;
        this.height = worldCanvas.clientHeight;
        
        worldCanvas.width = this.width * dpr;
        worldCanvas.height = this.height * dpr;
        worldCanvas.style.width = this.width + 'px';
        worldCanvas.style.height = this.height + 'px';
    },
    
    startNewGame() {
        if (document.getElementById('loading-screen')) {
            document.getElementById('loading-screen').style.display = 'none';
        }
        if (document.getElementById('title-screen')) {
            document.getElementById('title-screen').style.display = 'none';
        }
        
        const starters = ['lumivolt', 'petallia', 'obsidia'];
        const starterId = starters[Math.floor(Math.random() * starters.length)];
        const starterData = calcStats(CREATURE_DATA.find(c => c.id === starterId), 5);
        
        this.party = [starterData];
        this.inventory = {
            'potion_small': 5,
            'capture_basic': 2,
            'catalyst_basic': 0
        };
        
        this.zoneIndex = 0;
        this.position = { x: 1, y: 1 };
        
        showStoryVignette(STORY_NODES[0]);
        
        this.gameState = 'overworld';
        this.render();
    },
    
    saveGame() {
        const data = {
            party: this.party,
            inventory: this.inventory,
            zoneIndex: this.zoneIndex,
            position: this.position,
            battleState: null
        };
        
        try {
            localStorage.setItem('newBindingSave', JSON.stringify(data));
            showNotification('Game saved!');
        } catch(e) {
            console.warn('Failed to save game:', e);
        }
    },
    
    loadGame(savedData) {
        this.party = savedData.party;
        this.inventory = savedData.inventory;
        this.zoneIndex = savedData.zoneIndex;
        this.position = savedData.position;
        
        this.render();
        
        const zoneData = ZONES[this.zoneIndex];
        if (zoneData && zoneData.shrineId) {
            this.party.forEach(p => {
                p.hp = p.maxHp;
                delete p.effects;
            });
            showStoryVignette(STORY_NODES.find(n => n.zone === this.zoneIndex));
        } else if (STORY_NODES.find(n => n.zone === this.zoneIndex)) {
            showStoryVignette(STORY_NODES.find(n => n.zone === this.zoneIndex));
        }
        
        return this;
    },
    
    handleInput(e) {
        if (this.gameState !== 'overworld') return;
        
        const keyMap = {
            'w': [-1, 0], 's': [1, 0], 'a': [0, -1], 'd': [0, 1],
            'ArrowUp': [-1, 0], 'ArrowDown': [1, 0], 
            'ArrowLeft': [0, -1], 'ArrowRight': [0, 1]
        };
        
        const action = keyMap[e.key] || null;
        if (action) {
            e.preventDefault();
            
            const newX = this.position.x + action[0];
            const newY = this.position.y + action[1];
            
            const maxZonesX = ZONES.length;
            const maxZonesY = Math.ceil(240 / this.tileSize);
            
            if (newX >= 0 && newX < maxZonesX && newY >= 0 && newY < maxZonesY) {
                this.position = { x: newX, y: newY };
                this.render();
                
                const zone = ZONES[newY];
                if (zone && zone.bossId) {
                    handleBossEncounter(zone);
                }
            }
        }
    },
    
    handleCanvasKey(e) {
        if (this.gameState !== 'overworld') return;
        
        const keyMap = {
            'w': [-1, 0], 's': [1, 0], 'a': [0, -1], 'd': [0, 1],
            'ArrowUp': [-1, 0], 'ArrowDown': [1, 0], 
            'ArrowLeft': [0, -1], 'ArrowRight': [0, 1]
        };
        
        const action = keyMap[e.key] || null;
        if (action) {
            e.preventDefault();
            
            const newX = this.position.x + action[0];
            const newY = this.position.y + action[1];
            
            const maxZonesX = ZONES.length;
            const maxZonesY = Math.ceil(240 / this.tileSize);
            
            if (newX >= 0 && newX < maxZonesX && newY >= 0 && newY < maxZonesY) {
                this.position = { x: newX, y: newY };
                this.render();
                
                const zone = ZONES[newY];
                if (zone && zone.bossId) {
                    handleBossEncounter(zone);
                }
            }
        }
    },
    
    render() {
        if (!this.canvas || !this.ctx) return;
        
        const zoneData = ZONES[this.zoneIndex];
        if (zoneData) {
            this.ctx.fillStyle = zoneData.background;
            this.ctx.fillRect(0, 0, this.width, this.height);
        }
        
        this.ctx.strokeStyle = 'rgba(255,255,255,0.1)';
        this.ctx.lineWidth = 1;
        const tileSize = 40;
        for (let i = 0; i <= this.width / tileSize; i++) {
            this.ctx.beginPath();
            this.ctx.moveTo(i * tileSize, 0);
            this.ctx.lineTo(i * tileSize, this.height);
            this.ctx.stroke();
        }
        for (let i = 0; i <= this.height / tileSize; i++) {
            this.ctx.beginPath();
            this.ctx.moveTo(0, i * tileSize);
            this.ctx.lineTo(this.width, i * tileSize);
            this.ctx.stroke();
        }
        
        this.drawParticles();
        
        if (this.party.length > 0) {
            const player = this.party[0];
            const px = Math.floor(player.x * this.tileSize + this.tileSize / 2);
            const py = Math.floor(player.y * this.tileSize + this.tileSize / 2);
            
            this.drawEntityAt(px, py, player);
        }
        
        this.drawHUD();
    },
    
    drawParticles() {
        const zoneData = ZONES[this.zoneIndex];
        if (!zoneData || !zoneData.particleType) return;
        
        let particles = GAME.particles || [];
        
        if (Math.random() < 0.02 && particles.length < 30) {
            const angle = Math.random() * Math.PI * 2;
            const speed = Math.random() * 1 + 0.5;
            particles.push({
                x: GAME.width / 2,
                y: GAME.height / 2,
                vx: Math.cos(angle) * speed,
                vy: Math.sin(angle) * speed,
                life: 80 + Math.random() * 100,
                color: zoneData.color
            });
        }
        
        for (let i = particles.length - 1; i >= 0; i--) {
            const p = particles[i];
            p.x += p.vx;
            p.y += p.vy;
            p.life--;
            
            if (p.life <= 0) {
                particles.splice(i, 1);
            } else {
                this.ctx.globalAlpha = p.life / 200;
                this.ctx.fillStyle = p.color;
                this.ctx.fillRect(p.x, p.y, 3, 3);
                this.ctx.globalAlpha = 1;
            }
        }
        
        GAME.particles = particles;
    },
    
    drawEntityAt(x, y, creature) {
        const ctx = this.ctx;
        
        ctx.strokeStyle = 'rgba(255,255,255,0.4)';
        ctx.lineWidth = 2;
        ctx.beginPath();
        ctx.arc(x, y, 30, 0, Math.PI * 2);
        ctx.stroke();
        
        ctx.fillStyle = TYPE_COLORS[creature.affinity] || '#818cf8';
        ctx.globalAlpha = 0.9;
        ctx.beginPath();
        ctx.arc(x, y, 26, 0, Math.PI * 2);
        ctx.fill();
        
        ctx.strokeStyle = creature.affinity;
        ctx.lineWidth = 2;
        ctx.globalAlpha = 1;
        ctx.beginPath();
        ctx.arc(x, y, 24, 0, Math.PI * 2);
        ctx.stroke();
        
        ctx.fillStyle = 'white';
        ctx.font = 'bold 16px Arial';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(creature.affinity.charAt(0).toUpperCase(), x, y);
        
        if (creature.formPrefix) {
            ctx.font = 'italic 12px Arial';
            ctx.fillStyle = '#fbbf24';
            ctx.fillText(creature.formPrefix, x - 20, y + 50);
        }
    },
    
    drawHUD() {
        const zoneData = ZONES[this.zoneIndex];
        if (!zoneData) return;
        
        this.ctx.font = 'bold 18px Arial';
        this.ctx.fillStyle = 'rgba(255,255,255,0.9)';
        this.ctx.textAlign = 'left';
        this.ctx.fillText(zoneData.name, 16, 30);
        
        if (zoneData.bossId && !zoneData.bossDefeated) {
            const bossName = CREATURE_DATA.find(c => c.id === zoneData.bossId)?.name || 'Guardian';
            this.ctx.font = 'bold 24px Arial';
            this.ctx.fillStyle = 'rgba(255,100,100,0.9)';
            this.ctx.textAlign = 'center';
            this.ctx.fillText('⚠️ ' + bossName.toUpperCase(), GAME.width / 2, GAME.height / 2 + 30);
        }
        
        if (zoneData.shrineId) {
            const shrineX = GAME.width - 60;
            const shrineY = GAME.height - 50;
            this.ctx.font = 'bold 28px Arial';
            this.ctx.fillStyle = '#6ee7b7';
            this.ctx.textAlign = 'center';
            this.ctx.fillText('🛕', shrineX, shrineY);
        }
        
        if (this.party.length === 1 && this.party[0].level < 5) {
            this.ctx.font = 'italic 12px Arial';
            this.ctx.fillStyle = 'rgba(255,255,255,0.7)';
            this.ctx.fillText('Training...', GAME.width - 90, 30);
        }
    },
    
    // Battle methods (same as before for brevity)
    startBattle: function(enemyIdx, enemyData) {
        if (!this.battleState) {
            this.battleState = {
                playerTurn: true,
                phase: 'intro',
                messages: [],
                turnsRemaining: 20,
                defeated: false,
                animations: []
            };
        }
        
        this.gameState = 'battle';
        
        if (!enemyData) {
            const zoneData = ZONES[this.zoneIndex];
            enemyData = spawnWildCreature(this.zoneIndex);
        }
        
        const player = this.party[0] || this.party[this.battleState.playerPartyIdx || 0];
        if (player) {
            enemyData.level = Math.floor(player.level * (0.8 + Math.random() * 0.4));
        } else {
            enemyData.level = 15;
        }
        
        const zoneData = ZONES[this.zoneIndex];
        const isBoss = !!zoneData.bossId;
        
        this.drawBattleIntro(isBoss);
        
        setTimeout(() => {
            if (!isBoss) {
                this.offerSave();
            } else {
                this.startBattleTurns();
            }
        }, 1500);
    },
    
    drawBattleIntro: function(isBoss) {
        const canvas = document.getElementById('battle-canvas');
        if (!canvas) return;
        
        canvas.style.display = 'block';
        
        const title = isBoss ? '⚠️ CHALLENGE ⚠️' : 'WILD ENCOUNTER!';
        this.ctx.fillStyle = isBoss ? '#f87171' : '#fbbf24';
        this.ctx.font = 'bold 36px Arial';
        this.ctx.textAlign = 'center';
        this.ctx.fillText(title, canvas.width / 2, canvas.height / 2);
        
        const enemyName = isBoss ? CREATURE_DATA.find(c => c.id === ZONES[this.zoneIndex].bossId)?.name || 'Guardian' : 
            (CREATURE_DATA[this.enemyIdx]?.name || 'Wild Creature');
        
        this.ctx.font = 'bold 24px Arial';
        this.ctx.fillStyle = 'rgba(255,255,255,0.9)';
        this.ctx.fillText(enemyName, canvas.width / 2, canvas.height / 2 + 45);
    },
    
    offerSave: function() {
        const msg = document.getElementById('dialog-container');
        if (!msg) return;
        
        const container = document.createElement('div');
        container.style.padding = '16px';
        container.style.background = 'rgba(255,255,255,0.1)';
        container.style.borderRadius = '8px';
        container.style.marginTop = '12px';
        
        const p = document.createElement('p');
        p.textContent = 'A shrine appears! Would you like to save?';
        
        const btnContainer = document.createElement('div');
        btnContainer.style.display = 'flex';
        btnContainer.style.gap = '8px';
        btnContainer.style.justifyContent = 'flex-end';
        
        const saveBtn = document.createElement('button');
        saveBtn.className = 'btn btn-primary';
        saveBtn.textContent = 'Save';
        saveBtn.onclick = () => { this.saveGame(); msg.remove(); };
        
        const noBtn = document.createElement('button');
        noBtn.className = 'btn btn-secondary';
        noBtn.textContent = 'No Thanks';
        noBtn.onclick = () => { msg.remove(); };
        
        btnContainer.appendChild(saveBtn);
        btnContainer.appendChild(noBtn);
        container.appendChild(p);
        container.appendChild(btnContainer);
        msg.appendChild(container);
    },
    
    startBattleTurns: function() {
        this.battleState.phase = 'battle';
        this.renderBattle();
    },
    
    renderBattle: function() {
        const canvas = document.getElementById('battle-canvas');
        if (!canvas || this.battleState?.defeated) return;
        
        canvas.fillStyle = 'rgba(10,10,18,0.95)';
        canvas.fillRect(0, 0, this.width, this.height);
        
        const bs = this.battleState;
        const player = this.party[bs.playerPartyIdx || 0];
        const enemy = bs.activeEnemy;
        
        this.drawBattleUI(player, enemy);
        this.drawBattleEntities();
        this.drawMoveButtons(player);
        this.drawBattleMessages();
    },
    
    drawBattleUI: function(player, enemy) {
        const canvas = document.getElementById('battle-canvas');
        if (!canvas) return;
        
        const playerHpPct = player.hp / player.maxHp;
        const enemyHpPct = enemy.hp / enemy.maxHp;
        
        this.ctx.font = 'bold 20px Arial';
        this.ctx.fillStyle = '#e8e8ff';
        this.ctx.textAlign = 'left';
        this.ctx.fillText(player.name, 16, 35);
        
        const playerMaxHp = 40;
        this.drawHealthBar(16, 50, playerHpPct * playerMaxHp, playerMaxHp, '#e8e8ff');
        
        const enemyX = canvas.width - 220;
        this.ctx.fillText(enemy.name, enemyX + 16, 35);
        
        this.drawHealthBar(enemyX + 16, 50, enemyHpPct * playerMaxHp, playerMaxHp, '#f87171');
    },
    
    drawHealthBar: function(x, y, current, max, color) {
        const barWidth = 140;
        const barHeight = 20;
        
        this.ctx.fillStyle = 'rgba(0,0,0,0.5)';
        this.ctx.fillRect(x, y, barWidth, barHeight);
        
        const fillWidth = (current / max) * barWidth;
        this.ctx.fillStyle = color;
        this.ctx.fillRect(x, y, fillWidth, barHeight);
        
        this.ctx.strokeStyle = 'rgba(255,255,255,0.3)';
        this.ctx.lineWidth = 2;
        this.ctx.strokeRect(x, y, barWidth, barHeight);
    },
    
    drawBattleEntities: function() {
        const canvas = document.getElementById('battle-canvas');
        if (!canvas) return;
        
        const bs = this.battleState;
        const player = this.party[bs.playerPartyIdx || 0];
        const enemy = bs.activeEnemy;
        
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        
        this.drawEntity(enemy, centerX + 150, centerY - 20);
        this.drawEntity(player, centerX - 80, centerY - 20);
    },
    
    drawEntity: function(creature, x, y) {
        const canvas = document.getElementById('battle-canvas');
        if (!canvas) return;
        
        if (creature.formPrefix) {
            this.ctx.save();
            this.ctx.shadowColor = creature.affinity;
            this.ctx.shadowBlur = 20;
        }
        
        this.ctx.strokeStyle = 'rgba(255,255,255,0.3)';
        this.ctx.lineWidth = 3;
        this.ctx.beginPath();
        this.ctx.arc(x, y, 40, 0, Math.PI * 2);
        this.ctx.stroke();
        
        this.ctx.fillStyle = TYPE_COLORS[creature.affinity] || '#818cf8';
        this.ctx.globalAlpha = 0.9;
        this.ctx.beginPath();
        this.ctx.arc(x, y, 36, 0, Math.PI * 2);
        this.ctx.fill();
        
        this.ctx.strokeStyle = creature.affinity;
        this.ctx.lineWidth = 2;
        this.ctx.globalAlpha = 1;
        this.ctx.beginPath();
        this.ctx.arc(x, y, 35, 0, Math.PI * 2);
        this.ctx.stroke();
        
        this.ctx.fillStyle = 'white';
        this.ctx.font = 'bold 24px Arial';
        this.ctx.textAlign = 'center';
        this.ctx.textBaseline = 'middle';
        this.ctx.fillText(creature.affinity.charAt(0).toUpperCase(), x, y);
        
        if (creature.formPrefix) {
            this.ctx.font = 'italic 12px Arial';
            this.ctx.fillStyle = '#fbbf24';
            this.ctx.fillText(creature.formPrefix, x - 20, y + 55);
        }
        
        if (creature.formPrefix) {
            this.ctx.restore();
        }
    },
    
    drawMoveButtons: function(player) {
        const canvas = document.getElementById('battle-canvas');
        if (!canvas) return;
        
        const bottomY = canvas.height - 140;
        
        player.abilities.forEach((ability, idx) => {
            if (ability.currentPP <= 0) return;
            
            const btnX = 20 + idx * 165;
            const btnY = bottomY + 40;
            const btnWidth = 150;
            const btnHeight = 64;
            
            this.ctx.fillStyle = 'rgba(255,255,255,0.08)';
            this.ctx.beginPath();
            this.ctx.roundRect(btnX, btnY, btnWidth, btnHeight, 10);
            this.ctx.fill();
            
            const moveName = ability.name.substring(0, 14);
            this.ctx.fillStyle = '#e8e8ff';
            this.ctx.font = 'bold 14px Arial';
            this.ctx.fillText(moveName, btnX + 20, btnY + 35);
            
            const typeColor = TYPE_COLORS[ability.type] || '#818cf8';
            const typeText = ability.name.split(' ').pop().toUpperCase();
            this.ctx.font = 'italic 10px Arial';
            this.ctx.fillStyle = typeColor;
            this.ctx.fillText(typeText, btnX + 20, btnY + 50);
        });
    },
    
    executeMove: function(moveIdx) {
        const player = this.party[this.battleState.playerPartyIdx || 0];
        const ability = player.abilities[moveIdx];
        
        if (ability.currentPP <= 0) return;
        
        this.executeMoveEffect(ability, true);
    },
    
    executeMoveEffect: function(ability, isAttack) {
        const canvas = document.getElementById('battle-canvas');
        if (!canvas) return;
        
        const player = this.party[this.battleState.playerPartyIdx || 0];
        const enemy = this.battleState.activeEnemy;
        
        ability.currentPP--;
        
        this.battleState.messages.push('You used ' + ability.name + '!');
        
        if (isAttack) {
            const damageData = calculateDamage(player, enemy, ability);
            
            canvas.style.animation = 'shake 0.3s ease-in-out';
            setTimeout(() => canvas.style.animation = '', 300);
            
            showFloatingText(canvas.width / 2 + 250, canvas.height / 2 - 60, '-' + damageData.damage, damageData.isCrit ? 'crit' : '');
            
            if (damageData.isSuperEffective) {
                const critText = damageData.isCrit ? '(CRITICAL!)' : '';
                showFloatingText(canvas.width / 2 + 250, canvas.height / 2 - 40, 'SUPER EFFECTIVE' + critText, 'super-effective');
            }
            
            enemy.hp -= damageData.damage;
            if (enemy.hp < 0) enemy.hp = 0;
            
            const typeEffects = {
                flux: { burn: false, frost: false, shock: false },
                crysta: { burn: false, frost: true, shock: false },
                void: { burn: false, frost: false, shock: true },
                ember: { burn: true, frost: false, shock: false },
                storm: { burn: false, frost: false, shock: false },
                iron: { burn: false, frost: false, shock: false },
                spirit: { burn: false, frost: false, shock: false }
            };
            
            if (typeEffects[ability.type] && typeEffects[ability.type][ability.effect]) {
                applyStatusEffect(enemy, ability.effect);
            }
            
            if (enemy.hp <= 0) {
                this.battleState.phase = 'victory';
                showFloatingText(canvas.width / 2, canvas.height / 2 - 50, 'VICTORY!', 'super-effective');
                
                const expGain = Math.floor(enemy.level * 1.3) + 50;
                player.xp += expGain;
                if (player.xp >= player.xpToNextLevel) {
                    player.level++;
                    player.xp -= player.xpToNextLevel;
                    showFloatingText(canvas.width / 2, canvas.height / 2, 'LEVEL UP!', 'super-effective');
                    
                    const newStats = calcStats(CREATURE_DATA.find(c => c.id === player.id.split('-primal')[0]), player.level);
                    player.maxHp = newStats.maxHp;
                    player.atk = newStats.atk;
                    player.def = newStats.def;
                    player.spd = newStats.spd;
                }
                
                setTimeout(() => this.offerCapture(player, enemy, ability.type), 2000);
            } else {
                this.battleState.phase = 'enemy-turn';
                setTimeout(() => this.renderBattle(), 1000);
            }
        }
        
        this.renderBattleMessages();
    },
    
    offerCapture: function(player, enemy) {
        if (this.battleState.defeated || !this.inventory['capture_basic']) return;
        
        const zoneData = ZONES[this.zoneIndex];
        const isBoss = !!zoneData.bossId;
        
        if (isBoss) {
            setTimeout(() => this.renderBattleMessages(), 1500);
            return;
        }
        
        const msgContainer = document.getElementById('dialog-container');
        if (!msgContainer) return;
        
        const container = document.createElement('div');
        container.style.padding = '16px';
        container.style.background = 'rgba(255,255,255,0.08)';
        container.style.borderRadius = '8px';
        container.style.marginTop = '12px';
        
        const p = document.createElement('p');
        p.textContent = 'A wild ' + enemy.name + ' wants to befriend you! Press E to catch it with a Resonance Orb!';
        
        const btnContainer = document.createElement('div');
        btnContainer.style.display = 'flex';
        btnContainer.style.gap = '8px';
        btnContainer.style.justifyContent = 'flex-end';
        
        const captureBtn = document.createElement('button');
        captureBtn.className = 'btn btn-primary';
        captureBtn.textContent = 'Catch!';
        captureBtn.style.fontSize = '12px';
        captureBtn.onclick = () => this.attemptCapture(player, enemy);
        
        const cancelBtn = document.createElement('button');
        cancelBtn.className = 'btn btn-secondary';
        cancelBtn.textContent = 'Cancel';
        cancelBtn.onclick = () => { container.remove(); };
        
        btnContainer.appendChild(captureBtn);
        btnContainer.appendChild(cancelBtn);
        container.appendChild(p);
        container.appendChild(btnContainer);
        msgContainer.appendChild(container);
    },
    
    attemptCapture: function(player, enemy) {
        const captureRate = calculateCaptureRate(enemy, 1);
        
        if (Math.random() < captureRate) {
            showFloatingText(GAME.width / 2, GAME.height / 2, 'Captured ' + enemy.name + '!', 'super-effective');
            
            const capturedCreature = calcStats(CREATURE_DATA.find(c => c.id === enemy.id), Math.floor(enemy.level * 0.8));
            this.party.push(capturedCreature);
            
            if (this.battleState.phase === 'victory') {
                this.gameState = 'overworld';
                this.render();
                showStoryVignette(STORY_NODES[this.zoneIndex]);
            } else {
                setTimeout(() => this.renderBattle(), 2000);
            }
        } else {
            showFloatingText(GAME.width / 2, GAME.height / 2 + 40, 'Capture Failed!', '');
        }
        
        setTimeout(() => this.renderBattleMessages(), 1500);
    },
    
    renderBattleMessages: function() {
        const msgContainer = document.getElementById('dialog-container');
        if (!msgContainer) return;
        
        Array.from(msgContainer.children).forEach(c => c.remove());
        
        const bs = this.battleState || {};
        if (!bs.messages || bs.messages.length === 0) return;
        
        const msgBox = document.createElement('div');
        msgBox.style.padding = '16px';
        msgBox.style.background = 'rgba(255,255,255,0.05)';
        msgBox.style.borderRadius = '8px';
        msgBox.style.marginBottom = '12px';
        
        const text = document.createElement('p');
        text.textContent = bs.messages.join('\n');
        text.style.whiteSpace = 'pre-wrap';
        
        msgBox.appendChild(text);
        msgContainer.appendChild(msgBox);
    },
    
    drawBattleMessages: function() {
        const canvas = document.getElementById('battle-canvas');
        if (!canvas) return;
        
        const bs = this.battleState || {};
        const msgY = canvas.height - 140 + 80;
        
        this.ctx.font = 'bold 14px Arial';
        this.ctx.fillStyle = '#e8e8ff';
        this.ctx.textAlign = 'center';
        
        if (bs.phase === 'intro') {
            const enemyName = bs.activeEnemy?.name || 'Wild Creature';
            this.ctx.fillText('A wild ' + enemyName + ' appeared!', canvas.width / 2, msgY);
        } else if (bs.phase === 'victory' && bs.messages.length > 0) {
            this.ctx.fillText(bs.messages[bs.messages.length - 1] || 'VICTORY!', canvas.width / 2, msgY);
        } else if (bs.phase === 'enemy-turn') {
            this.ctx.fillText('Enemy is taking a turn...', canvas.width / 2, msgY);
        } else if (bs.messages && bs.messages.length > 0) {
            const lastMsg = bs.messages[bs.messages.length - 1];
            this.ctx.fillText(lastMsg || '', canvas.width / 2, msgY);
        }
    },
    
    // Inventory/Team screens
    openInventory: function() {
        this.showScreen('inventory');
    },
    
    showScreen: function(screenType) {
        const battleCanvas = document.getElementById('battle-canvas');
        if (battleCanvas) {
            battleCanvas.style.display = 'none';
        }
        
        const modal = document.createElement('div');
        modal.className = 'modal-overlay';
        modal.id = 'screen-modal';
        modal.onclick = () => this.closeScreen();
        
        const content = document.createElement('div');
        content.className = 'modal';
        
        if (screenType === 'inventory') {
            this.createInventoryContent(content);
        } else if (screenType === 'team') {
            this.createTeamContent(content);
        } else if (screenType === 'encyclopedia') {
            this.createEncyclopediaContent(content);
        } else if (screenType === 'shop') {
            this.createShopContent(content);
        }
        
        modal.appendChild(content);
        document.body.appendChild(modal);
    },
    
    createInventoryContent: function(container) {
        const title = document.createElement('h2');
        title.textContent = 'Bag';
        
        const backBtn = document.createElement('button');
        backBtn.className = 'back-btn';
        backBtn.textContent = 'Back';
        backBtn.onclick = () => this.closeScreen();
        
        const grid = document.createElement('div');
        grid.className = 'inventory-grid';
        
        const itemTypes = [
            { id: 'potion_small', name: 'Essence Vial', icon: '🧪' },
            { id: 'capture_basic', name: 'Resonance Orb', icon: '🔮' }
        ];
        
        itemTypes.forEach(item => {
            const count = this.inventory[item.id] || 0;
            
            for (let i = 0; i < Math.min(count, 5); i++) {
                const slot = document.createElement('div');
                slot.className = 'item-slot';
                slot.textContent = item.icon;
                slot.title = item.name + ' (' + count + ')';
                
                slot.onclick = () => this.useItem(item.id);
                grid.appendChild(slot);
            }
        });
        
        container.appendChild(title);
        container.appendChild(backBtn);
        container.appendChild(grid);
    },
    
    createTeamContent: function(container) {
        const title = document.createElement('h2');
        title.textContent = 'Team';
        
        const backBtn = document.createElement('button');
        backBtn.className = 'back-btn';
        backBtn.textContent = 'Back';
        backBtn.onclick = () => this.closeScreen();
        
        const grid = document.createElement('div');
        grid.style.display = 'grid';
        grid.style.gap = '8px';
        grid.style.padding = '16px';
        
        this.party.forEach((creature, idx) => {
            const member = document.createElement('div');
            member.className = 'team-member';
            
            if (idx === 0) member.classList.add('selected');
            
            const avatar = document.createElement('div');
            avatar.className = 'team-avatar';
            avatar.textContent = creature.affinity.charAt(0).toUpperCase();
            
            const info = document.createElement('div');
            info.className = 'team-info';
            
            const levelEl = document.createElement('span');
            levelEl.className = 'team-level';
            levelEl.textContent = 'Lv.' + creature.level;
            
            info.appendChild(levelEl);
            
            const nameEl = document.createElement('strong');
            nameEl.textContent = creature.name;
            nameEl.style.display = 'block';
            
            const affinityEl = document.createElement('span');
            affinityEl.className = 'team-affinity ' + creature.affinity;
            affinityEl.textContent = creature.affinity.toUpperCase();
            
            info.appendChild(nameEl);
            info.appendChild(affinityEl);
            
            member.appendChild(avatar);
            member.appendChild(info);
            
            if (idx === 0) {
                member.onclick = () => this.selectPartyMember(0);
            }
            
            grid.appendChild(member);
        });
        
        container.appendChild(title);
        container.appendChild(backBtn);
        container.appendChild(grid);
    },
    
    createEncyclopediaContent: function(container) {
        const title = document.createElement('h2');
        title.textContent = 'Creature Codex';
        
        const backBtn = document.createElement('button');
        backBtn.className = 'back-btn';
        backBtn.textContent = 'Back';
        backBtn.onclick = () => this.closeScreen();
        
        const grid = document.createElement('div');
        grid.style.display = 'grid';
        grid.style.gap = '8px';
        grid.style.padding = '16px';
        
        // Group creatures by zone
        for (let i = 0; i < CREATURE_DATA.length; i++) {
            const creature = CREATURE_DATA[i];
            
            if (i > 0 && !creature.isBoss) {
                grid.appendChild(backBtn);
                backBtn.style.marginTop = '16px';
                break;
            }
            
            const entry = document.createElement('div');
            entry.className = 'enc-entry';
            
            const header = document.createElement('div');
            header.className = 'enc-header';
            
            const avatar = document.createElement('div');
            avatar.style.width = '60px';
            avatar.style.height = '60px';
            avatar.style.borderRadius = '12px';
            avatar.style.background = TYPE_COLORS[creature.affinity] || '#818cf8';
            avatar.style.display = 'flex';
            avatar.style.alignItems = 'center';
            avatar.style.justifyContent = 'center';
            avatar.style.fontSize = '30px';
            avatar.textContent = creature.affinity.charAt(0).toUpperCase();
            
            const content = document.createElement('div');
            content.style.flex = '1';
            
            const nameEl = document.createElement('strong');
            nameEl.textContent = creature.name;
            nameEl.style.color = '#e8e8ff';
            nameEl.style.marginBottom = '4px';
            
            const affinityBadge = document.createElement('span');
            affinityBadge.style.display = 'inline-block';
            affinityBadge.style.background = TYPE_COLORS[creature.affinity];
            affinityBadge.style.color = '#1a1a2e';
            affinityBadge.style.padding = '2px 8px';
            affinityBadge.style.borderRadius = '4px';
            affinityBadge.style.fontSize = '11px';
            affinityBadge.style.textTransform = 'uppercase';
            affinityBadge.textContent = creature.affinity;
            
            const loreEl = document.createElement('p');
            loreEl.style.color = '#9ca3af';
            loreEl.style.marginTop = '8px';
            loreEl.textContent = creature.lore;
            
            content.appendChild(nameEl);
            content.appendChild(affinityBadge);
            content.appendChild(loreEl);
            
            header.appendChild(avatar);
            header.appendChild(content);
            entry.appendChild(header);
            
            // Add abilities
            const abilitiesDiv = document.createElement('div');
            abilitiesDiv.style.marginTop = '8px';
            abilitiesDiv.style.color = '#6ee7b7';
            abilitiesDiv.style.fontSize = '12px';
            
            creature.abilities.slice(0, 3).forEach((abi, idx) => {
                const abiv = document.createElement('div');
                abiv.textContent = abi.name;
                if (idx < 2) abiv.style.marginRight = '16px';
            });
            
            abilitiesDiv.appendChild(abiv);
            entry.appendChild(abilitiesDiv);
            
            grid.appendChild(entry);
        }
        
        container.appendChild(title);
        container.appendChild(backBtn);
        container.appendChild(grid);
    },
    
    createShopContent: function(container) {
        const title = document.createElement('h2');
        title.textContent = 'Trade Post';
        
        const backBtn = document.createElement('button');
        backBtn.className = 'back-btn';
        backBtn.textContent = 'Back';
        backBtn.onclick = () => this.closeScreen();
        
        const grid = document.createElement('div');
        grid.className = 'shop-grid';
        
        const shopItems = [
            { id: 'potion_large', name: 'Primal Elixir', icon: '🧪', price: 0, owned: true },
            { id: 'bandage', name: 'Field Bandage', icon: '💚', price: 100, owned: false },
            { id: 'capture_adv', name: 'Prism Orb', icon: '🔮', price: 250, owned: false }
        ];
        
        shopItems.forEach(item => {
            const slot = document.createElement('div');
            slot.className = 'shop-item';
            
            if (item.owned) {
                slot.classList.add('bought');
            }
            
            const icon = document.createElement('span');
            icon.style.fontSize = '32px';
            icon.textContent = item.icon;
            
            const nameEl = document.createElement('div');
            nameEl.style.marginTop = '8px';
            nameEl.textContent = item.name;
            if (item.owned) {
                nameEl.style.color = '#6ee7b7';
            } else {
                nameEl.style.color = '#e8e8ff';
            }
            
            slot.appendChild(icon);
            slot.appendChild(nameEl);
            grid.appendChild(slot);
        });
        
        container.appendChild(title);
        container.appendChild(backBtn);
        container.appendChild(grid);
    },
    
    useItem: function(itemId) {
        if (!this.inventory[itemId]) return;
        
        const item = ITEMS[itemId];
        
        if (item.effect === 'heal_25') {
            this.party[0].hp = Math.min(this.party[0].maxHp, this.party[0].hp + 25);
            showNotification('Healed 25 HP!');
        } else if (item.effect === 'heal_full') {
            this.party[0].hp = this.party[0].maxHp;
            showNotification('Fully healed!');
        }
        
        this.inventory[itemId]--;
        if (this.inventory[itemId] <= 0) {
            delete this.inventory[itemId];
        }
    },
    
    selectPartyMember: function(idx) {
        // Simplified - only one party member for now
    },
    
    closeScreen: function() {
        const modal = document.getElementById('screen-modal');
        if (modal) {
            modal.remove();
        }
        
        this.gameState = 'overworld';
        this.render();
    }
};

/* =====================================================
   HELPER FUNCTIONS FOR BOSS ENCOUNTER
   ===================================================== */

function handleBossEncounter(zoneData) {
    if (GAME.party.some(p => p.id === zoneData.bossId)) {
        showNotification('Boss already defeated! ' + zoneData.name);
        return;
    }
    
    const bossData = calcStats(CREATURE_DATA.find(c => c.id === zoneData.bossId), 20);
    const enemy = { ...bossData, hp: bossData.maxHp };
    
    GAME.startBattle(0, enemy);
}

/* =====================================================
   INITIALIZATION
   ===================================================== */

GAME.particles = [];
GAME.init();

function renderInitial() {
    const worldCanvas = document.getElementById('world-canvas');
    if (worldCanvas) {
        worldCanvas.style.display = 'block';
        
        const ctx = worldCanvas.getContext('2d');
        
        const zoneData = ZONES[GAME.zoneIndex];
        if (zoneData) {
            ctx.fillStyle = zoneData.background;
            ctx.fillRect(0, 0, GAME.width, GAME.height);
            
            ctx.strokeStyle = 'rgba(255,255,255,0.1)';
            ctx.lineWidth = 1;
            const tileSize = 40;
            for (let i = 0; i <= GAME.width / tileSize; i++) {
                ctx.beginPath();
                ctx.moveTo(i * tileSize, 0);
                ctx.lineTo(i * tileSize, GAME.height);
                ctx.stroke();
            }
            for (let i = 0; i <= GAME.height / tileSize; i++) {
                ctx.beginPath();
                ctx.moveTo(0, i * tileSize);
                ctx.lineTo(GAME.width, i * tileSize);
                ctx.stroke();
            }
        }
        
        if (GAME.party.length > 0) {
            const p = GAME.party[0];
            const centerX = GAME.width / 2;
            const centerY = GAME.height / 2;
            
            GAME.drawEntityAt(centerX, centerY, p);
        }
        
        GAME.drawHUD();
    }
}

function showTitleScreen() {
    const saved = localStorage.getItem('newBindingSave');
    
    if (document.getElementById('loading-screen')) {
        document.getElementById('loading-screen').style.display = 'none';
    }
    
    if (saved) {
        try {
            const saveData = JSON.parse(saved);
            GAME.loadGame(saveData);
        } catch(e) {
            console.warn('Failed to load save:', e);
        }
    } else if (GAME.party.length > 0) {
        GAME.loadGame(GAME);
    } else {
        GAME.startNewGame();
    }
}

renderInitial();
showTitleScreen();

/* =====================================================
   UI FUNCTIONS
   ===================================================== */

function showNotification(text) {
    const container = document.getElementById('notifications');
    if (!container) return;
    
    const notif = document.createElement('div');
    notif.className = 'notification';
    notif.textContent = text;
    
    container.appendChild(notif);
    
    setTimeout(() => {
        notif.style.animation = 'slideIn 0.3s ease-out reverse';
        setTimeout(() => notif.remove(), 300);
    }, 2500);
}

/* =====================================================
   MAIN GAME LOOP
   ===================================================== */

function gameLoop() {
    if (GAME.animations && GAME.animations.length > 0) {
        GAME.animations = GAME.animations.filter(a => a.life > 0);
    }
    
    requestAnimationFrame(gameLoop);
}

gameLoop();

/* =====================================================
   END OF INITIALIZATION
   ===================================================== */
</script>
</body>
</html>
<!-- agent-meta {"model":"qwen/qwen3.5-9b","provider":"lmstudio","persona":"composer","sessionId":"517b8e10-08d2-4882-bf78-3fbab74c5cd5","tokensIn":2338359,"tokensOut":96697,"tokensTotal":2435056,"turns":42,"toolCalls":41,"failedToolCalls":7,"timestamp":"2026-07-14T03:18:17.397Z"} -->