← Creature Collection results

Creature Collection

inkling · basic

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 57,108 bytes · SHA-256 0381f50a4a3d
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Veil of Aetheris</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@400;700&family=Orbitron:wght@400;700&family=Quicksand:wght@300;500;700&display=swap');

:root{
  --bg: #0b0c15;
  --glass: rgba(255,255,255,0.06);
  --glass-border: rgba(255,255,255,0.12);
  --text: #e8eaf6;
  --dim: #8b8fa3;
  --accent: #c7a4ff;
  --sol: #ffb347;
  --lun: #7ee787;
  --abyss: #9d4bff;
  --aether: #4fc3f7;
  --echo: #f093fb;
  --prism: #ffe082;
  --obsidian: #b0bec5;
  --danger: #ff6b6b;
  --heal: #69f0ae;
}

*{box-sizing:border-box;touch-action:manipulation;user-select:none;-webkit-user-select:none;}
html,body{margin:0;padding:0;width:100%;height:100%;overflow:hidden;background:var(--bg);color:var(--text);font-family:'Quicksand',sans-serif;}
canvas{display:block;}

#game{width:100%;height:100%;position:relative;overflow:hidden;}
.screen{position:absolute;inset:0;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;transition:opacity .6s ease;}
.screen.hide{opacity:0;pointer-events:none;}
.screen.show{opacity:1;pointer-events:auto;}

.glass{background:var(--glass);backdrop-filter:blur(14px) saturate(140%);-webkit-backdrop-filter:blur(14px) saturate(140%);border:1px solid var(--glass-border);border-radius:24px;box-shadow:0 20px 60px rgba(0,0,0,.6),inset 0 1px 0 rgba(255,255,255,.08);}
.glass-modal{background:rgba(11,12,21,.85);backdrop-filter:blur(18px);border:1px solid rgba(255,255,255,.15);border-radius:28px;padding:32px;max-width:92vw;width:640px;box-shadow:0 30px 80px rgba(0,0,0,.7);animation:fadeUp .5s ease;}

@keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.6}}
@keyframes shimmer{0%{background-position:-200% center}100%{background-position:200% center}}
@keyframes shake{0%,100%{transform:translate(0,0)}10%,30%,50%,70%,90%{transform:translate(-3px,2px)}20%,40%,60%,80%{transform:translate(3px,-2px)}}

.h1-title{font-family:'Cinzel Decorative',serif;font-weight:700;font-size:clamp(2.8rem,8vw,5.5rem);letter-spacing:.12em;text-align:center;background:linear-gradient(180deg,#fff 0%,#c7a4ff 60%,#ffe082 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;filter:drop-shadow(0 0 30px rgba(199,164,255,.35));margin-bottom:8px;animation:float 6s ease-in-out infinite;}
.subtitle{font-family:'Orbitron',sans-serif;font-size:1rem;color:var(--dim);letter-spacing:.25em;text-transform:uppercase;margin-bottom:48px;}

.btn{font-family:'Orbitron',sans-serif;padding:18px 36px;border-radius:50px;border:1px solid rgba(255,255,255,.2);background:rgba(255,255,255,.05);color:var(--text);font-size:1rem;letter-spacing:.1em;cursor:pointer;transition:all .2s ease;position:relative;overflow:hidden;}
.btn:hover{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.35);transform:translateY(-2px);box-shadow:0 10px 30px rgba(199,164,255,.15);}

.btn-primary{background:linear-gradient(135deg,rgba(199,164,255,.25),rgba(255,178,102,.15));border-color:rgba(199,164,255,.4);color:#fff;}
.btn-primary:hover{background:linear-gradient(135deg,rgba(199,164,255,.4),rgba(255,178,102,.25));border-color:rgba(199,164,255,.6);box-shadow:0 0 30px rgba(199,164,255,.3);}

.title-screen{background:radial-gradient(circle at 50% 30%,#15152a 0%,#0b0c15 70%);z-index:100;}
.title-screen .bg-stars{position:absolute;inset:0;pointer-events:none;background-image:radial-gradient(circle at 20% 30%,rgba(255,255,255,.12) 1px,transparent 1px),radial-gradient(circle at 80% 70%,rgba(255,255,255,.08) 1px,transparent 1px),radial-gradient(circle at 50% 50%,rgba(199,164,255,.08) 2px,transparent 2px);background-size:300px 300px,200px 200px,400px 400px;animation:drift 120s linear infinite;}
@keyframes drift{from{background-position:0 0,100px 50px,200px 100px}to{background-position:300px 300px,400px 350px,600px 400px}}

#titleCanvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;}
.title-content{position:relative;z-index:10;text-align:center;padding:20px;}
.nav-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px;margin-top:32px;max-width:420px;width:100%;margin-left:auto;margin-right:auto;}
.btn-small{padding:14px 18px;font-size:.9rem;border-radius:16px;white-space:nowrap;}

.screen-overlay{position:absolute;inset:0;background:radial-gradient(circle at center,rgba(199,164,255,.15) 0%,transparent 70%);pointer-events:none;opacity:.6;z-index:1;}

#overworld{z-index:10;}
#battle{z-index:20;}
#menu{z-index:30;}
#dialog{z-index:40;}
#shrine{z-index:35;}
#tutorial{z-index:50;}
#mapScreen{z-index:30;}
#bagScreen{z-index:30;}
#encyclopedia{z-index:30;}
#teamScreen{z-index:30;}
#saveScreen{z-index:30;}
#endScreen{z-index:100;}

#overworldCanvas{width:100%;height:100%;display:block;}
#battleCanvas{position:absolute;inset:0;width:100%;height:100%;display:block;}

.battle-ui{position:absolute;bottom:0;left:0;right:0;padding:24px;display:flex;flex-direction:column;gap:12px;z-index:5;}
.battle-info-top{display:flex;justify-content:space-between;align-items:center;padding:8px 20px;}
.enemy-row{display:flex;gap:20px;justify-content:center;align-items:center;}
.player-row{display:flex;gap:12px;justify-content:center;align-items:center;margin-top:8px;}
.health-bar{width:120px;height:14px;border-radius:7px;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.1);overflow:hidden;position:relative;}
.health-inner{height:100%;width:100%;background:linear-gradient(90deg,#69f0ae,#4fc3f7);transition:width .3s ease;box-shadow:0 0 10px rgba(69,240,174,.3);}

.ability-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;max-width:480px;margin:0 auto;}
.ability-btn{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.12);border-radius:16px;padding:14px;text-align:center;cursor:pointer;transition:all .2s;}
.ability-btn:hover:not(:disabled){background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.3);transform:scale(1.03);}
.ability-btn:disabled{opacity:.3;cursor:not-allowed;}
.ability-name{font-family:'Orbitron',sans-serif;font-size:.85rem;color:var(--text);margin-bottom:4px;}
.ability-meta{font-size:.7rem;color:var(--dim);}

.turn-indicator{font-family:'Orbitron',sans-serif;color:var(--prism);font-size:.9rem;letter-spacing:.15em;text-shadow:0 0 15px rgba(255,224,130,.4);}

.party-card{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:8px;display:flex;align-items:center;gap:8px;min-width:80px;}
.party-avatar{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--accent),var(--echo));display:flex;align-items:center;justify-content:center;font-size:.7rem;font-weight:bold;}
.party-stats{font-size:.65rem;color:var(--dim);line-height:1.2;}

.floating-text{position:absolute;font-family:'Orbitron',sans-serif;font-weight:bold;font-size:1.1rem;pointer-events:none;animation:floatUp .8s ease-out forwards;}
@keyframes floatUp{from{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-40px)}}

.zone-label{position:absolute;top:20px;left:20px;background:rgba(11,12,21,.7);backdrop-filter:blur(8px);padding:10px 22px;border-radius:50px;border:1px solid rgba(255,255,255,.1);font-family:'Orbitron',sans-serif;font-size:.85rem;color:var(--dim);letter-spacing:.15em;z-index:5;}

.modal-body{display:flex;flex-direction:column;gap:24px;}
.dialog-text{font-size:1.15rem;line-height:1.7;color:var(--text);text-align:center;}
.dialog-vignette{position:absolute;inset:0;background:radial-gradient(circle at 50% 60%,rgba(199,164,255,.4) 0%,rgba(11,12,21,.9) 80%);z-index:-1;border-radius:24px;}
.story-progress{display:flex;gap:8px;justify-content:center;margin-top:12px;}
.story-dot{width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,.15);transition:all .3s;}
.story-dot.active{background:var(--prism);box-shadow:0 0 10px var(--prism);}

.cutscene-box{position:absolute;inset:40px;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;z-index:25;padding:40px;}
.cutscene-box h2{font-family:'Cinzel Decorative',serif;color:var(--prism);font-size:2.2rem;margin-bottom:12px;text-shadow:0 0 30px rgba(255,224,130,.5);}
.cutscene-box p{font-size:1.2rem;max-width:600px;color:var(--text);line-height:1.8;}
.cutscene-vignette{position:absolute;inset:0;background:radial-gradient(circle at 50% 40%,rgba(255,178,102,.2) 0%,rgba(11,12,21,.85) 70%);z-index:-1;}

.collection-mini-game{display:flex;flex-direction:column;align-items:center;gap:20px;margin-top:8px;}
.tap-zone{width:240px;height:80px;background:linear-gradient(90deg,rgba(255,255,255,.05),rgba(255,255,255,.02));border:2px solid rgba(255,255,255,.15);border-radius:40px;position:relative;overflow:hidden;cursor:pointer;}
.tap-zone .marker{position:absolute;top:-4px;left:20%;width:40px;height:calc(100% + 8px);background:var(--prism);opacity:.8;border-radius:20px;animation:markerSlide 2s linear infinite;}
.tap-zone .hit-area{position:absolute;left:55%;width:30%;height:100%;background:rgba(255,255,255,.08);border-left:1px solid rgba(255,255,255,.2);border-right:1px solid rgba(255,255,255,.2);}
@keyframes markerSlide{from{left:-10%}to{left:110%}}

.enc-entry{display:grid;grid-template-columns:48px 1fr;gap:14px;padding:10px 0;border-bottom:1px solid rgba(255,255,255,.08);align-items:center;}
.enc-avatar{width:48px;height:48px;border-radius:50%;background:linear-gradient(135deg,rgba(255,255,255,.1),rgba(255,255,255,.05));display:flex;align-items:center;justify-content:center;font-size:1.2rem;}
.enc-info h3{font-family:'Orbitron',sans-serif;color:var(--text);margin:0 0 4px;}
.enc-info span{font-size:.75rem;color:var(--dim);}

.tutorial-overlay{position:absolute;inset:0;background:rgba(0,0,0,.75);backdrop-filter:blur(10px);z-index:60;display:flex;align-items:center;justify-content:center;}
.tut-card{max-width:520px;padding:32px;text-align:center;}
.tut-card h3{font-family:'Orbitron',sans-serif;color:var(--sol);margin-bottom:16px;letter-spacing:.1em;}
.tut-card p{color:var(--dim);margin-bottom:24px;line-height:1.6;}
.tut-arrow{font-size:3rem;color:var(--accent);animation:float 2s infinite;}
</style>
</head>
<body>
<div id="game">

<!-- Screens -->
<div id="titleScreen" class="screen show title-screen">
  <canvas id="titleCanvas"></canvas>
  <div class="bg-stars"></div>
  <div class="title-content">
    <h1 class="h1-title">VEIL OF AETHERIS</h1>
    <p class="subtitle">Bind the Fragments, Restore the Veil</p>
    <div style="margin-top:48px;">
      <button class="btn btn-primary" onclick="startNewGame()">NEW BINDING</button>
      <button class="btn" onclick="continueGame()" style="margin-left:16px;">CONTINUE</button>
    </div>
    <p style="margin-top:32px;font-size:.75rem;color:rgba(255,255,255,.3);">WASD / Arrows to move • Touch controls available</p>
  </div>
</div>

<div id="overworld" class="screen hide">
  <canvas id="overworldCanvas"></canvas>
  <div class="zone-label" id="zoneLabel">Bloomreach</div>
  <div class="nav-grid" style="position:absolute;bottom:24px;left:50%;transform:translateX(-50%);z-index:10;max-width:340px;">
    <button class="btn btn-small" onclick="openMenu('team')">TEAM</button>
    <button class="btn btn-small" onclick="openMenu('bag')">BAG</button>
    <button class="btn btn-small" onclick="openMenu('map')">MAP</button>
    <button class="btn btn-small" onclick="openMenu('save')">SAVE</button>
    <button class="btn btn-small" onclick="openMenu('encyclopedia')">CODEX</button>
    <button class="btn btn-small" onclick="openMenu('shrine')">REST</button>
  </div>
</div>

<div id="battle" class="screen hide">
  <canvas id="battleCanvas"></canvas>
  <div class="screen-overlay"></div>
  <div class="battle-ui" id="battleUI">
    <div class="battle-info-top">
      <div id="turnIndicator" class="turn-indicator">PLAYER TURN</div>
      <div id="enemyCount">ENEMY: 1</div>
    </div>
    <div class="enemy-row" id="enemyRow"></div>
    <div class="player-row" id="playerRow"></div>
    <div class="ability-grid" id="abilityGrid"></div>
  </div>
</div>

<div id="dialog" class="screen hide">
  <div class="glass-modal" style="position:relative;">
    <div class="dialog-vignette"></div>
    <h2 id="dialogTitle" style="font-family:'Cinzel Decorative',serif;color:var(--accent);margin-bottom:8px;">Binding Initiated</h2>
    <p class="dialog-text" id="dialogBody">A strange energy pulses in the air. A creature has appeared before you.</p>
    <div class="story-progress" id="storyDots">
      <span class="story-dot active"></span><span class="story-dot"></span><span class="story-dot"></span><span class="story-dot"></span>
    </div>
    <div style="margin-top:24px;display:flex;gap:12px;justify-content:center;">
      <button class="btn btn-primary" onclick="closeDialog()">CONTINUE</button>
    </div>
  </div>
</div>

<div id="shrine" class="screen hide">
  <div class="glass-modal" style="position:relative;">
    <h2 style="font-family:'Cinzel Decorative',serif;color:var(--heal);margin-bottom:8px;">SANCTUARY SHRINE</h2>
    <p style="color:var(--dim);margin-bottom:24px;">Rest here to restore vitality and record your progress.</p>
    <div style="display:flex;gap:12px;justify-content:center;">
      <button class="btn btn-primary" onclick="healParty()">REST & HEAL</button>
      <button class="btn" onclick="saveGame(true)">SAVE PROGRESS</button>
    </div>
  </div>
</div>

<div id="menu" class="screen hide">
  <div class="glass-modal" style="max-width:640px;width:92vw;padding:24px;">
    <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
      <h2 id="menuTitle" style="font-family:'Orbitron',sans-serif;color:var(--accent);margin:0;">MENU</h2>
      <button onclick="closeMenu()" style="background:none;border:none;color:var(--text);font-size:1.2rem;">✕</button>
    </div>
    <div id="menuContent"></div>
  </div>
</div>

<div id="mapScreen" class="screen hide" style="background:rgba(11,12,21,.9);">
  <div class="glass-modal" style="width:92vw;max-width:640px;">
    <h2 style="font-family:'Orbitron',sans-serif;color:var(--aether);margin-bottom:16px;">VEIL MAP</h2>
    <div id="mapContent" style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;"></div>
  </div>
</div>

<div id="bagScreen" class="screen hide">
  <div class="glass-modal" style="width:92vw;max-width:640px;">
    <h2 style="font-family:'Orbitron',sans-serif;color:var(--sol);margin-bottom:16px;">INVENTORY</h2>
    <div id="bagContent"></div>
  </div>
</div>

<div id="teamScreen" class="screen hide">
  <div class="glass-modal" style="width:92vw;max-width:640px;">
    <h2 style="font-family:'Orbitron',sans-serif;color:var(--echo);margin-bottom:16px;">PARTY COMPOSITION</h2>
    <div id="teamContent"></div>
  </div>
</div>

<div id="encyclopedia" class="screen hide">
  <div class="glass-modal" style="width:92vw;max-width:640px;">
    <h2 style="font-family:'Orbitron',sans-serif;color:var(--prism);margin-bottom:16px;">AETHER ENCYCLOPEDIA</h2>
    <div id="encContent"></div>
  </div>
</div>

<div id="saveScreen" class="screen hide">
  <div class="glass-modal" style="width:92vw;max-width:640px;">
    <h2 style="font-family:'Orbitron',sans-serif;color:var(--accent);margin-bottom:16px;">ARCHIVE SLOT</h2>
    <div style="display:flex;gap:12px;flex-wrap:wrap;">
      <button class="btn" onclick="loadSlot(0)">SLOT 1</button>
      <button class="btn" onclick="loadSlot(1)">SLOT 2</button>
      <button class="btn" onclick="loadSlot(2)">SLOT 3</button>
    </div>
    <div style="margin-top:16px;">
      <button class="btn btn-primary" onclick="saveGame(false)">OVERWRITE CURRENT</button>
    </div>
  </div>
</div>

<div id="tutorial" class="screen hide">
  <div class="tut-card glass-modal">
    <h3 id="tutTitle">WELCOME, BINDER</h3>
    <p id="tutBody">Use WASD or Arrow keys to move through the Veil. Approach creatures to challenge them.</p>
    <div class="tut-arrow">↓</div>
    <button class="btn btn-primary" onclick="nextTutorialStep()">BEGIN</button>
  </div>
</div>

<div id="endScreen" class="screen hide" style="background:radial-gradient(circle at 50% 40%,#1a1033 0%,#0b0c15 80%);">
  <div style="text-align:center;padding:40px;">
    <h1 class="h1-title" style="font-size:3rem;">VEIL RESTORED</h1>
    <h3 style="font-family:'Orbitron',sans-serif;color:var(--prism);letter-spacing:.2em;margin-bottom:24px;">THE BINDING IS COMPLETE</h3>
    <p style="max-width:480px;margin:0 auto;color:var(--dim);line-height:1.8;margin-bottom:32px;">You have bound the Fragments, defeated the Anchors, and restored harmony to the shattered realms. The Aetheris now breathes freely.</p>
    <button class="btn btn-primary" onclick="returnToTitle()">RETURN TO START</button>
  </div>
</div>

<div id="floatingContainer" style="position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:200;"></div>
</div>

<script>
/* ===================== GAME DATA ===================== */

const TYPES = [
  {name:'Solar',abbr:'SOL',color:'#ffb347',bg:'linear-gradient(135deg,#ffb347,#ff8c42)'},
  {name:'Lunar',abbr:'LUN',color:'#7ee787',bg:'linear-gradient(135deg,#7ee787,#4caf50)'},
  {name:'Void',abbr:'ABY',color:'#9d4bff',bg:'linear-gradient(135deg,#9d4bff,#7b1fa2)'},
  {name:'Aether',abbr:'AET',color:'#4fc3f7',bg:'linear-gradient(135deg,#4fc3f7,#0288d1)'},
  {name:'Echo',abbr:'ECH',color:'#f093fb',bg:'linear-gradient(135deg,#f093fb,#e91e8c)'},
  {name:'Prism',abbr:'PRI',color:'#ffe082',bg:'linear-gradient(135deg,#ffe082,#ff9800)'},
  {name:'Obsidian',abbr:'OBS',color:'#b0bec5',bg:'linear-gradient(135deg,#b0bec5,#607d8b)'}
];

const TYPE_CHART = {
  'Solar':['Lunar'],
  'Lunar':['Void'],
  'Void':['Solar'],
  'Aether':['Solar','Void'],
  'Echo':['Aether','Lunar'],
  'Prism':[], // beats all slightly
  'Obsidian':[] // resists all
};

const ABILITIES = [
  {name:'Solar Lance',type:'Solar',power:25,acc:95,pp:15,effect:'burn',desc:'A piercing beam that scorches the target.'},
  {name:'Moon Veil',type:'Lunar',power:20,acc:100,pp:20,effect:'frost',desc:'Shrouds the enemy in cold mist.'},
  {name:'Void Pulse',type:'Void',power:30,acc:90,pp:15,effect:'shock',desc:'Disrupts with chaotic energy.'},
  {name:'Aether Surge',type:'Aether',power:35,acc:85,pp:20,effect:'speed',desc:'Accelerates the user with wind.'},
  {name:'Echo Strike',type:'Echo',power:28,acc:90,pp:15,effect:'multi',desc:'Strikes twice with sonic force.'},
  {name:'Prism Shard',type:'Prism',power:40,acc:80,pp:25,effect:'crit',desc:'Shatters light into piercing fragments.'},
  {name:'Obsidian Shell',type:'Obsidian',power:15,acc:100,pp:25,effect:'barrier',desc:'Creates a defensive crystal barrier.'},
  {name:'Flame Burst',type:'Solar',power:22,acc:95,pp:12,effect:'burn',desc:'A sudden explosion of solar fire.'},
  {name:'Frost Nova',type:'Lunar',power:35,acc:85,pp:20,effect:'frost',desc:'Freezes the area around the target.'},
  {name:'Shadow Claw',type:'Void',power:32,acc:90,pp:18,effect:'burn',desc:'Tears with dark, burning talons.'},
  {name:'Wind Slash',type:'Aether',power:18,acc:100,pp:15,effect:'speed',desc:'Cuts swiftly, increasing velocity.'},
  {name:'Sonic Boom',type:'Echo',power:25,acc:95,pp:15,effect:'multi',desc:'Emits a shockwave that hits broadly.'},
  {name:'Crystal Rain',type:'Prism',power:30,acc:85,pp:20,effect:'heal',desc:'Heals the user with refracted light.'},
  {name:'Rock Bash',type:'Obsidian',power:38,acc:80,pp:25,effect:'barrier',desc:'Crushes with heavy stone, forming shields.'},
  {name:'Solar Flare',type:'Solar',power:45,acc:75,pp:30,effect:'burn',desc:'A blinding flash of solar energy.'},
  {name:'Lunar Eclipse',type:'Lunar',power:40,acc:80,pp:25,effect:'frost',desc:'Obscures vision and freezes blood.'},
  {name:'Void Rift',type:'Void',power:42,acc:75,pp:25,effect:'shock',desc:'Opens a rift that stuns the enemy.'},
  {name:'Aether Storm',type:'Aether',power:38,acc:85,pp:22,effect:'speed',desc:'Calls a storm that hastens allies.'},
  {name:'Echo Chamber',type:'Echo',power:28,acc:95,pp:15,effect:'multi',desc:'Bounces sound to strike multiple times.'},
  {name:'Prism Spiral',type:'Prism',power:35,acc:80,pp:22,effect:'crit',desc:'Spins light into a piercing vortex.'},
  {name:'Obsidian Wall',type:'Obsidian',power:20,acc:100,pp:20,effect:'barrier',desc:'Erects an impenetrable crystal wall.'},
  {name:'Ignition',type:'Solar',power:30,acc:90,pp:18,effect:'burn',desc:'Sets the ground ablaze.'},
  {name:'Glacial Spike',type:'Lunar',power:40,acc:80,pp:22,effect:'frost',desc:'Launches a massive ice spike.'},
  {name:'Abyss Grip',type:'Void',power:35,acc:85,pp:20,effect:'shock',desc:'Grasps with void energy, stuns target.'},
  {name:'Sky Shatter',type:'Aether',power:32,acc:90,pp:18,effect:'speed',desc:'Breaks air resistance for swift strikes.'},
  {name:'Vibration',type:'Echo',power:22,acc:95,pp:14,effect:'multi',desc:'Rapid vibrations deal repeated damage.'},
  {name:'Light Cascade',type:'Prism',power:25,acc:95,pp:15,effect:'heal',desc:'Showers with healing light.'},
  {name:'Stone Fist',type:'Obsidian',power:42,acc:75,pp:28,effect:'barrier',desc:'A massive stone blow, fortifying defense.'},
  {name:'Star Burn',type:'Solar',power:50,acc:70,pp:35,effect:'burn',desc:'Channels pure starlight to incinerate.'},
  {name:'Moonlight Dance',type:'Lunar',power:28,acc:95,pp:16,effect:'frost',desc:'A graceful dance that chills foes.'},
  {name:'Void Bite',type:'Void',power:45,acc:80,pp:25,effect:'shock',desc:'Bites with dark energy, stunning prey.'},
  {name:'Cyclone',type:'Aether',power:42,acc:80,pp:22,effect:'speed',desc:'Creates a cyclone to enhance speed.'},
  {name:'Resonance',type:'Echo',power:30,acc:90,pp:18,effect:'multi',desc:'Amplifies sound for additional hits.'},
  {name:'Spectral Blade',type:'Prism',power:28,acc:95,pp:18,effect:'crit',desc:'Slices with rainbow-edged light.'},
  {name:'Earth Tremor',type:'Obsidian',power:45,acc:75,pp:28,effect:'barrier',desc:'Shakes the ground, shielding user.'},
  {name:'Solar Guard',type:'Solar',power:15,acc:100,pp:15,effect:'barrier',desc:'Forms a solar shield around user.'},
  {name:'Frost Shield',type:'Lunar',power:15,acc:100,pp:15,effect:'barrier',desc:'Creates a frozen barrier.'},
  {name:'Shadow Step',type:'Void',power:30,acc:85,pp:18,effect:'speed',desc:'Moves so quickly it confuses the enemy.'},
  {name:'Wind Guard',type:'Aether',power:15,acc:100,pp:15,effect:'barrier',desc:'Wraps in protective winds.'},
  {name:'Echo Shield',type:'Echo',power:20,acc:95,pp:12,effect:'barrier',desc:'Reflects damage with sound waves.'},
  {name:'Crystal Armor',type:'Prism',power:15,acc:100,pp:15,effect:'barrier',desc:'Forms crystal plating on user.'},
  {name:'Stone Armor',type:'Obsidian',power:15,acc:100,pp:15,effect:'barrier',desc:'Hardens skin like stone.'}
];

const CREATURES = [
  {id:'sprigkin',name:'Sprigkin',type:'Solar',baseHP:60,baseATK:14,baseDEF:8,baseSPD:12,level:1,abilities:[0,7,1,23],lore:'A tiny seed-spirit that grows stronger with sunlight. It binds to caretakers with gentle warmth.',evolve:[20,24],item:'Sunseed'},
  {id:'mosshell',name:'Mosshell',type:'Void',baseHP:75,baseATK:10,baseDEF:16,baseSPD:6,level:1,abilities:[9,6,37,39],lore:'A creeping shell covered in bioluminescent moss. It absorbs negative energy and transforms it into growth.',evolve:[28,29],item:'Shadow Catalyst'},
  {id:'lumenfly',name:'Lumenfly',type:'Aether',baseHP:50,baseATK:12,baseDEF:6,baseSPD:18,level:1,abilities:[3,10,17,34],lore:'A delicate moth made of pure aether-wind. Its wings shimmer with the colors of unseen skies.',evolve:[25,26],item:'Aether Dust'},
  {id:'frostbite',name:'Frostbite',type:'Void',baseHP:65,baseATK:16,baseDEF:10,baseSPD:8,level:1,abilities:[2,8,36,24],lore:'A predator of frozen caves that breathes cold enough to freeze air into glass.',evolve:[30,31],item:'Frost Orb'},
  {id:'shiverscale',name:'Shiverscale',type:'Lunar',baseHP:70,baseATK:12,baseDEF:14,baseSPD:9,level:1,abilities:[1,26,38,40],lore:'Covered in shimmering lunar scales, it channels moonlight into freezing strikes.',evolve:[33,32],item:'Moon Pearl'},
  {id:'glacialis',name:'Glacialis',type:'Echo',baseHP:60,baseATK:14,baseDEF:9,baseSPD:13,level:1,abilities:[4,12,27,32],lore:'A phantom of frozen echoes, it mimics sounds to disorient prey before striking.',evolve:[34,27],item:'Echo Orb'},
  {id:'shadowmoth',name:'Shadowmoth',type:'Lunar',baseHP:55,baseATK:15,baseDEF:7,baseSPD:15,level:1,abilities:[1,9,11,43],lore:'Its wings are made of pure darkness. Where it flies, light dims and temperature drops.',evolve:[26,27],item:'Shadow Crystal'},
  {id:'voidling',name:'Voidling',type:'Void',baseHP:80,baseATK:11,baseDEF:18,baseSPD:5,level:1,abilities:[2,10,15,41],lore:'Born from the deepest abyss, it moves slowly but hits with crushing void-force.',evolve:[30,28],item:'Void Shard'},
  {id:'obscura',name:'Obscura',type:'Obsidian',baseHP:100,baseATK:8,baseDEF:22,baseSPD:4,level:1,abilities:[6,16,42,35],lore:'A walking crystal monolith. It protects the weak by absorbing harm into its black body.',evolve:[40,36],item:'Dark Catalyst'},
  {id:'aetherwing',name:'Aetherwing',type:'Aether',baseHP:55,baseATK:13,baseDEF:8,baseSPD:17,level:1,abilities:[3,17,33,20],lore:'A bird of luminous winds, it soars between realms leaving trails of stardust.',evolve:[35,37],item:'Star Shard'},
  {id:'echohawk',name:'Echohawk',type:'Echo',baseHP:65,baseATK:17,baseDEF:10,baseSPD:12,level:1,abilities:[4,11,21,28],lore:'A raptor that hunts with sound. Its cry can shatter glass and stun prey instantly.',evolve:[38,39],item:'Sonic Feather'},
  {id:'solarite',name:'Solarite',type:'Solar',baseHP:60,baseATK:20,baseDEF:8,baseSPD:10,level:1,abilities:[0,14,22,44],lore:'A being of pure solar core. Its presence warms the air and fuels growth in plants.',evolve:[45,46],item:'Solar Core'},
  {id:'prismira',name:'Prismira',type:'Prism',baseHP:55,baseATK:15,baseDEF:6,baseSPD:14,level:1,abilities:[5,12,19,30],lore:'A creature formed from refracted reality. It exists in multiple colors simultaneously.',evolve:[40,41],item:'Rainbow Prism'},
  {id:'luminox',name:'Luminox',type:'Prism',baseHP:70,baseATK:18,baseDEF:10,baseSPD:11,level:1,abilities:[5,13,18,43],lore:'A serpentine being of light. It uses its prism-body to bend energy and heal allies.',evolve:[42,43],item:'Light Gem'},
  {id:'vortex',name:'Vortex',type:'Prism',baseHP:50,baseATK:22,baseDEF:5,baseSPD:20,level:1,abilities:[5,17,12,31],lore:'Spinning faster than lightning, this creature distorts reality with each rotation.',evolve:[48,49],item:'Spiral Core'},
  {id:'cragbeast',name:'Cragbeast',type:'Obsidian',baseHP:110,baseATK:18,baseDEF:26,baseSPD:3,level:1,abilities:[6,15,35,46],lore:'A colossal rock-golem that walks through mountains. It defends territories from invaders.',evolve:[50,36],item:'Rock Catalyst'},
  {id:'shardling',name:'Shardling',type:'Obsidian',baseHP:90,baseATK:14,baseDEF:24,baseSPD:6,level:1,abilities:[6,39,42,47],lore:'Covered in razor-sharp crystal plates, it moves slowly but strikes with devastating force.',evolve:[42,36],item:'Crystal Shard'},
  {id:'ebonwhisper',name:'Ebonwhisper',type:'Lunar',baseHP:65,baseATK:16,baseDEF:11,baseSPD:14,level:1,abilities:[1,9,11,37],lore:'A shadowy whisperer that communicates in dreams. It binds to strong-willed partners.',evolve:[34,36],item:'Dark Seed'}
];

const ZONES = [
  {id:1,name:'Bloomreach',color:'#155c2e',bg:'linear-gradient(135deg,#0f3a1f,#1a4a2a)',encounters:[0,1,2],boss:{creature:3,type:'Aether',name:'Guardian of Light'},particles:'green',story:'In the Bloomreach, life begins anew. The air smells of nectar and sunlight. The first Anchor stirs here.'},
  {id:2,name:'Frostveil',color:'#0a3a5c',bg:'linear-gradient(135deg,#042640,#0a3a5c)',encounters:[3,4,5],boss:{creature:3,type:'Lunar',name:'Guardian of Frost'},particles:'blue',story:'The Frostveil is silent and cold. Even breath freezes here. The second Anchor sleeps beneath the ice.'},
  {id:3,name:'Shadowmere',color:'#2d1a5c',bg:'linear-gradient(135deg,#1a0f3d,#2d1a5c)',encounters:[6,7,8],boss:{creature:8,type:'Obsidian',name:'Guardian of Darkness'},particles:'purple',story:'Shadowmere is a place of whispers. Light is devoured. The third Anchor waits in the deep.'},
  {id:4,name:'Aether Sky',color:'#0a5c6e',bg:'linear-gradient(135deg,#042b38,#0a5c6e)',encounters:[9,10,11],boss:{creature:11,type:'Echo',name:'Guardian of Echoes'},particles:'cyan',story:'Above the clouds lies the Aether Sky, where winds carry memories. The fourth Anchor floats above.'},
  {id:5,name:'Prism Ridge',color:'#5c2a5c',bg:'linear-gradient(135deg,#3a155c,#5c2a5c)',encounters:[12,13,14],boss:{creature:14,type:'Prism',name:'Guardian of Light'},particles:'rainbow',story:'At Prism Ridge, reality fractures into colors. The fifth Anchor shines with impossible light.'},
  {id:6,name:'Obsid Vault',color:'#151515',bg:'linear-gradient(135deg,#0f0f12,#181820)',encounters:[15,16,17],boss:{creature:7,type:'Obsidian',name:'Guardian of Stone'},particles:'grey',story:'The final realm. Black crystal towers scrape the sky. The last Anchor must be broken here.'}
];

const ITEMS = [
  {id:'capsule_a',name:'Basic Capsule',tier:1,desc:'Standard collection orb.',type:'capsule'},
  {id:'capsule_b',name:'Shimmer Capsule',tier:2,desc:'Improved capture rate.',type:'capsule'},
  {id:'capsule_c',name:'Aether Capsule',tier:3,desc:'Highest capture potential.',type:'capsule'},
  {id:'heal_potion',name:'Vital Elixir',tier:1,desc:'Restores 40 HP to one creature.',use:'heal',value:40,type:'heal'},
  {id:'heal_major',name:'Greater Elixir',tier:2,desc:'Restores 100 HP.',value:100,type:'heal'},
  {id:'sunseed',name:'Sunseed',tier:1,desc:'Catalyst for Solar evolution.',use:'evolve',type:'catalyst'},
  {id:'moon_pearl',name:'Moon Pearl',tier:2,desc:'Catalyst for Lunar growth.',use:'evolve',type:'catalyst'},
  {id:'star_shard',name:'Star Shard',tier:2,desc:'Catalyst for Aether transformation.',use:'evolve',type:'catalyst'},
  {id:'shadow_crystal',name:'Shadow Crystal',tier:2,desc:'Catalyst for Void evolution.',use:'evolve',type:'catalyst'},
  {id:'frost_orb',name:'Frost Orb',tier:2,desc:'Catalyst for Void frost forms.',use:'evolve',type:'catalyst'}
];

const STARTERS = [
  {id:'sprigkin',name:'Sprigkin',type:'Solar'},
  {id:'mosshell',name:'Mosshell',type:'Void'},
  {id:'lumenfly',name:'Lumenfly',type:'Aether'}
];

/* ===================== STATE ===================== */

let state = {
  zone:1,
  zoneIndex:0,
  player:{x:10,y:5,zone:1},
  party:[],
  bag:[
    {item:'capsule_a',qty:3},
    {item:'capsule_b',qty:1},
    {item:'heal_potion',qty:2},
    {item:'sunseed',qty:1},
    {item:'moon_pearl',qty:0},
    {item:'star_shard',qty:0}
  ],
  inventory:[],
  captured:new Set(),
  anchorsDefeated:0,
  storyStage:0,
  battlesWon:0,
  tutorialStep:0,
  gold:100,
  lastSave:null,
  activeBattle:null,
  screen:'title',
  menuScreen:'main',
  playingMusic:true,
  particles:[]
};

function initState(){
  try{
    const s = localStorage.getItem('aetheris_save');
    if(s){
      const d = JSON.parse(s);
      if(d && d.party){
        state = {...state, ...d};
        state.screen = 'title';
        state.activeBattle = null;
      }
    }
  }catch(e){}
}

function saveGame(manual=true){
  const data = {...state};
  data.screen = manual ? 'title' : state.screen;
  data.activeBattle = null;
  try{localStorage.setItem('aetheris_save',JSON.stringify(data));}catch(e){}
  if(manual) showNotification('Progress Archived');
}

function loadSlot(slot){
  try{
    const k = 'aetheris_save_'+slot;
    const s = localStorage.getItem(k);
    if(s){
      const d = JSON.parse(s);
      if(d && d.party){
        state = {...state, ...d};
        state.screen = 'overworld';
        state.activeBattle = null;
        startOverworld();
      } else {showNotification('Slot Empty');}
    } else {showNotification('Slot Empty');}
  }catch(e){showNotification('Load Failed');}
}

/* ===================== AUDIO ===================== */

let audioCtx, masterGain, bgOsc, bgOsc2;

function initAudio(){
  if(!audioCtx){
    try{
      audioCtx = new (window.AudioContext||window.webkitAudioContext)();
      masterGain = audioCtx.createGain();
      masterGain.gain.value = 0.15;
      masterGain.connect(audioCtx.destination);
    }catch(e){console.warn('Audio init failed');}
  }
}

function playTone(freq,dur,vol=0.1,type='sine'){
  if(!audioCtx || !state.playingMusic) return;
  const osc = audioCtx.createOscillator();
  const g = audioCtx.createGain();
  osc.type = type;
  osc.frequency.setValueAtTime(freq,audioCtx.currentTime);
  g.gain.setValueAtTime(vol,audioCtx.currentTime);
  g.gain.exponentialRampToValueAtTime(0.001,audioCtx.currentTime+dur);
  osc.connect(g); g.connect(masterGain); osc.start(); osc.stop(audioCtx.currentTime+dur);
}

function startAmbient(){
  if(!audioCtx) return;
  stopAmbient();
  bgOsc = audioCtx.createOscillator();
  bgOsc2 = audioCtx.createOscillator();
  bgOsc.type = 'sine'; bgOsc2.type = 'triangle';
  bgOsc.frequency.setValueAtTime(110,audioCtx.currentTime);
  bgOsc2.frequency.setValueAtTime(82.4,audioCtx.currentTime);
  const filter = audioCtx.createBiquadFilter();
  filter.type = 'lowpass'; filter.frequency.value = 600;
  const g = audioCtx.createGain(); g.gain.value = 0.04;
  bgOsc.connect(filter); bgOsc2.connect(filter); filter.connect(g); g.connect(masterGain);
  bgOsc.start(); bgOsc2.start();
  setInterval(()=>{
    if(bgOsc && bgOsc2){
      bgOsc.frequency.setValueAtTime(110 + Math.random()*20, audioCtx.currentTime);
      bgOsc2.frequency.setValueAtTime(82 + Math.random()*10, audioCtx.currentTime);
    }
  },3000);
}

function stopAmbient(){
  if(bgOsc){try{bgOsc.stop();bgOsc.disconnect();}catch(e){}}
  if(bgOsc2){try{bgOsc2.stop();bgOsc2.disconnect();}catch(e){}}
  bgOsc=null; bgOsc2=null;
}

/* ===================== RENDERING ===================== */

const overworldCanvas = document.getElementById('overworldCanvas');
const owCtx = overworldCanvas.getContext('2d');
const battleCanvas = document.getElementById('battleCanvas');
const bCtx = battleCanvas.getContext('2d');

function resize(){
  overworldCanvas.width = window.innerWidth; overworldCanvas.height = window.innerHeight;
  battleCanvas.width = window.innerWidth; battleCanvas.height = window.innerHeight;
}
window.addEventListener('resize',resize); resize();

function drawOverworld(){
  const w = overworldCanvas.width; const h = overworldCanvas.height;
  const zone = ZONES[state.zone-1];
  owCtx.fillStyle = zone.bg; owCtx.fillRect(0,0,w,h);
  
  // Particles
  state.particles = state.particles.filter(p => p.y < h+20 && p.life > 0);
  if(Math.random()<.3) state.particles.push({x:Math.random()*w, y:-10, speed:1+Math.random()*2, size:2+Math.random()*4, life:1, color:zone.color});
  for(const p of state.particles){
    p.y += p.speed; p.life -= 0.005;
    owCtx.beginPath();
    owCtx.arc(p.x,p.y,p.size,p.life*Math.PI*2,p.life*Math.PI*2+Math.PI);
    owCtx.fillStyle = p.color + Math.round(p.life*255).toString(16).padStart(2,'0');
    owCtx.fill();
  }
  
  // Tile grid
  const tileSize = 60;
  const cols = Math.ceil(w/tileSize)+2;
  const rows = Math.ceil(h/tileSize)+2;
  owCtx.strokeStyle = 'rgba(255,255,255,.05)';
  owCtx.lineWidth = 1;
  for(let x=0;x<cols;x++) for(let y=0;y<rows;y++){
    owCtx.beginPath(); owCtx.rect(x*tileSize, y*tileSize, tileSize, tileSize); owCtx.stroke();
  }
  
  // Player
  const px = state.player.x * tileSize + tileSize/2;
  const py = state.player.y * tileSize + tileSize/2;
  owCtx.beginPath();
  owCtx.arc(px,py,14,0,Math.PI*2);
  owCtx.fillStyle = '#c7a4ff'; owCtx.fill();
  owCtx.beginPath();
  owCtx.arc(px,py,6,0,Math.PI*2);
  owCtx.fillStyle = '#fff'; owCtx.fill();
  
  // Enemies on overworld
  const zoneData = ZONES[state.zone-1];
  for(const enc of zoneData.encounters){
    const cx = ((enc+5)*tileSize + 30) % (Math.ceil(w/tileSize)*tileSize);
    const cy = ((enc+3)*tileSize + 30) % (Math.ceil(h/tileSize)*tileSize);
    owCtx.beginPath();
    owCtx.arc(cx,cy,10,0,Math.PI*2);
    owCtx.fillStyle = TYPES[enc%7].color; owCtx.fill();
    owCtx.strokeStyle = 'rgba(255,255,255,.3)'; owCtx.lineWidth=2; owCtx.stroke();
  }
}

let owAnim = null;
function startOverworld(){
  state.screen = 'overworld';
  showScreen('overworld');
  document.getElementById('zoneLabel').textContent = ZONES[state.zone-1].name;
  startAmbient();
  owAnim = requestAnimationFrame(overworldLoop);
}

function overworldLoop(){
  if(state.screen !== 'overworld') return;
  drawOverworld();
  owAnim = requestAnimationFrame(overworldLoop);
}

function movePlayer(dx,dy){
  const newX = state.player.x + dx;
  const newY = state.player.y + dy;
  if(newX>=0 && newX<30 && newY>=0 && newY<20){
    state.player.x = newX; state.player.y = newY;
    // Random encounter
    if(Math.random()<.01){
      const zoneData = ZONES[state.zone-1];
      const enc = zoneData.encounters[Math.floor(Math.random()*zoneData.encounters.length)];
      startBattle(enc);
    }
  }
}

document.addEventListener('keydown',(e)=>{
  if(state.screen==='overworld'){
    if(e.key==='ArrowUp'||e.key==='w') movePlayer(0,-1);
    if(e.key==='ArrowDown'||e.key==='s') movePlayer(0,1);
    if(e.key==='ArrowLeft'||e.key==='a') movePlayer(-1,0);
    if(e.key==='ArrowRight'||e.key==='d') movePlayer(1,0);
    if(e.key==='Escape') openMenu('main');
  }
});

/* Touch controls for mobile */
const touchBtnContainer = document.createElement('div');
touchBtnContainer.style.cssText = 'position:absolute;bottom:100px;left:50%;transform:translateX(-50%);display:grid;grid-template-columns:repeat(3,60px);grid-template-rows:repeat(3,60px);gap:4px;z-index:15;';
touchBtnContainer.innerHTML = `
  <button onclick="movePlayer(0,-1)" style="grid-column:2">▲</button>
  <button onclick="movePlayer(-1,0)" style="grid-column:1;grid-row:2">◀</button>
  <button onclick="movePlayer(1,0)" style="grid-column:3;grid-row:2">▶</button>
  <button onclick="movePlayer(0,1)" style="grid-column:2;grid-row:3">▼</button>
`;
document.getElementById('overworld').appendChild(touchBtnContainer);

/* ===================== BATTLE ===================== */

function startBattle(encounterIndex){
  state.screen = 'battle';
  showScreen('battle');
  state.activeBattle = {enemy:creatureCopy(CREATURES[encounterIndex]),turn:'player',turnOrder:0,turnCount:0};
  initBattleUI();
  requestAnimationFrame(battleLoop);
}

function creatureCopy(creature){
  const c = {...creature};
  c.hp = Math.floor(c.baseHP + c.level*10);
  c.atk = Math.floor(c.baseATK + c.level*2);
  c.def = Math.floor(c.baseDEF + c.level*2);
  c.spd = Math.floor(c.baseSPD + c.level*1.5);
  return c;
}

function initBattleUI(){
  const ui = document.getElementById('battleUI');
  document.getElementById('enemyRow').innerHTML = `<div class="party-card" style="border-color:${TYPES[state.activeBattle.enemy.type].color}">
    <div class="party-avatar" style="background:${TYPES[state.activeBattle.enemy.type].bg}">${state.activeBattle.enemy.name.slice(0,2)}</div>
    <div><div style="font-family:'Orbitron',sans-serif;font-size:.8rem;color:${TYPES[state.activeBattle.enemy.type].color}">${state.activeBattle.enemy.name}</div>
    <div class="health-bar"><div class="health-inner" id="enemyHP" style="width:100%"></div></div></div>
  </div>`;
  
  document.getElementById('playerRow').innerHTML = state.party.map((p,pidx)=>`
    <button class="party-card" onclick="switchCreature(${pidx})" style="border-color:${TYPES[p.type].color}">
      <div class="party-avatar" style="background:${TYPES[p.type].bg}">${p.name.slice(0,2)}</div>
      <div><div style="font-size:.7rem;font-family:'Orbitron';color:${TYPES[p.type].color}">${p.name}</div>
      <div class="health-bar"><div class="health-inner" style="width:${Math.round((p.hp/p.maxHP)*100)}%"></div></div></div>
    </button>
  `).join('');
  
  document.getElementById('abilityGrid').innerHTML = state.party[0].abilities.map((abIdx,i)=>`
    <button class="ability-btn" onclick="useAbility(${i},'player')" id="abBtn${i}">
      <div class="ability-name">${ABILITIES[abIdx].name}</div>
      <div class="ability-meta">${ABILITIES[abIdx].type} • Pwr ${ABILITIES[abIdx].power} • PP ${ABILITIES[abIdx].pp}</div>
    </button>
  `).join('');
}

function switchCreature(idx){
  if(state.activeBattle.turn !== 'player') return;
  // Swap active creature (for simplicity we just change active, but in this version only 1 active shown)
  // Actually we'll just make the party rotate visually
  state.party.push(state.party.splice(idx,1)[0]); // rotate
  initBattleUI();
}

function useAbility(abIdx,source){
  if(state.activeBattle.turn !== 'player') return;
  const ab = ABILITIES[state.party[0].abilities[abIdx]];
  const attacker = state.party[0];
  const defender = state.activeBattle.enemy;
  const result = calculateDamage(attacker, defender, ab);
  
  // Visual feedback
  const container = document.getElementById('floatingContainer');
  const text = document.createElement('div');
  text.className = 'floating-text';
  text.textContent = result.damage > 0 ? (result.crit ? 'CRITICAL!' : result.damage) : 'MISS';
  text.style.left = '50%'; text.style.top = '30%';
  text.style.color = result.crit ? '#ffe082' : (result.super ? TYPES[ab.type].color : '#fff');
  container.appendChild(text);
  setTimeout(()=>text.remove(),800);
  
  playTone(300,0.2,'triangle');
  if(result.crit) playTone(800,0.3,'sawtooth');
  
  // Apply damage and effects
  defender.hp -= result.damage;
  if(result.super) playTone(600,0.2);
  
  // Status effects (simple)
  if(ab.effect === 'burn') defender.burned = 3;
  else if(ab.effect === 'frost') defender.frozen = 3;
  else if(ab.effect === 'barrier') attacker.barrier = 1;
  else if(ab.effect === 'heal') attacker.hp = Math.min(attacker.maxHP, attacker.hp + ab.power);
  
  document.getElementById('enemyHP').style.width = Math.max(0, (defender.hp/defender.maxHP)*100)+'%';
  
  if(defender.hp <= 0){
    endBattle(true);
  } else {
    state.activeBattle.turn = 'enemy';
    setTimeout(enemyTurn, 1000);
  }
  initBattleUI();
}

function calculateDamage(attacker, defender, ab){
  const level = attacker.level;
  const atk = attacker.atk;
  const def = defender.def;
  const mult = Math.random()*0.3 + 0.85;
  let dmg = Math.floor((0.4*level*atk/def*mult) + Math.random()*5);
  
  let stab = 1;
  if(attacker.type === ab.type) stab = 1.25;
  
  let typeMult = 1;
  if(TYPE_CHART[ab.type] && TYPE_CHART[ab.type].includes(defender.type)) typeMult = 1.5;
  else if(TYPE_CHART[defender.type] && TYPE_CHART[defender.type].includes(ab.type)) typeMult = 0.7;
  
  if(ab.type === 'Prism') typeMult = 1.2; // beats all slightly
  if(defender.type === 'Obsidian' && ab.type !== 'Prism') typeMult = 0.6;
  if(ab.type === 'Obsidian' && defender.type === 'Prism') typeMult = 1.4; // weak to prism
  
  dmg = Math.floor(dmg * stab * typeMult);
  
  const crit = Math.random() < 0.1 ? true : false;
  if(crit) dmg = Math.floor(dmg * 1.5);
  
  const super = typeMult > 1;
  return {damage: Math.max(1, dmg), crit:crit, super:super};
}

function enemyTurn(){
  const enemy = state.activeBattle.enemy;
  const player = state.party[0];
  const ab = ABILITIES[enemy.abilities[Math.floor(Math.random()*enemy.abilities.length)]];
  const result = calculateDamage(enemy, player, ab);
  
  const container = document.getElementById('floatingContainer');
  const text = document.createElement('div');
  text.className = 'floating-text';
  text.textContent = result.damage > 0 ? (result.crit ? 'CRITICAL!' : result.damage) : 'MISS';
  text.style.left = '50%'; text.style.top = '60%';
  text.style.color = result.crit ? '#ff6b6b' : '#fff';
  container.appendChild(text);
  setTimeout(()=>text.remove(),800);
  
  playTone(150,0.2,'triangle');
  
  player.hp -= result.damage;
  document.getElementById('playerRow').innerHTML = state.party.map((p,pidx)=>`
    <div class="party-card" style="border-color:${TYPES[p.type].color}">
      <div class="party-avatar" style="background:${TYPES[p.type].bg}">${p.name.slice(0,2)}</div>
      <div><div style="font-size:.7rem;font-family:'Orbitron';color:${TYPES[p.type].color}">${p.name}</div><div class="health-bar"><div class="health-inner" style="width:${Math.round((p.hp/p.maxHP)*100)}%"></div></div></div>
    </div>
  `).join('');
  
  if(player.hp <= 0){
    player.hp = 1; // prevent death for now
    showNotification('Critical! Your creature barely survived.');
  }
  
  state.activeBattle.turn = 'player';
  document.getElementById('turnIndicator').textContent = 'PLAYER TURN';
  initBattleUI();
  
  setTimeout(()=>{
    if(state.screen === 'battle') initBattleUI();
  }, 800);
}

function endBattle(win){
  state.screen = 'dialog';
  showScreen('dialog');
  state.activeBattle = null;
  document.getElementById('dialogTitle').textContent = win ? 'Victory!' : 'Defeat';
  document.getElementById('dialogBody').textContent = win ? 'The creature has fallen. You earned experience and capture the fragment.' : 'You were defeated. Rest at a shrine to recover.';
  
  if(win){
    state.battlesWon++;
    // EXP
    const exp = 15 + state.activeBattle ? 20 : 10;
    state.party.forEach(c => { c.hp = Math.floor(c.hp + 10); });
    
    // Capture chance
    const hpPercent = (state.activeBattle ? state.activeBattle.enemy.hp : 0) / (state.activeBattle ? state.activeBattle.enemy.maxHP : 1);
    const chance = Math.max(0.1, 0.7 - hpPercent);
    if(Math.random() < chance){
      state.captured.add(state.activeBattle.enemy.id);
      document.getElementById('dialogBody').innerHTML += '<br><br><span style="color:var(--prism)">CAPTURED: ' + state.activeBattle.enemy.name + '</span>';
      playTone(500,0.5,'sine');
    }
    
    // Check zone anchor defeat for story progression
    if(state.battlesWon % 5 === 0 && state.storyStage < 4){
      state.storyStage++;
      showStoryCutscene();
    }
  }
}

function battleLoop(){
  if(state.screen !== 'battle'){
    // Draw battle canvas
    bCtx.fillStyle = '#0b0c15'; bCtx.fillRect(0,0,battleCanvas.width,battleCanvas.height);
    // Simple visual
    bCtx.fillStyle = 'rgba(255,255,255,.03)';
    for(let i=0;i<20;i++) bCtx.fillRect(Math.random()*battleCanvas.width, Math.random()*battleCanvas.height, 60, 2);
    if(state.screen === 'battle') requestAnimationFrame(battleLoop);
  }
}

/* ===================== STORY & DIALOG ===================== */

function showStoryCutscene(){
  const stage = state.storyStage;
  const zoneData = ZONES[state.zone-1];
  const box = document.createElement('div');
  box.className = 'cutscene-box';
  box.innerHTML = `
    <div class="cutscene-vignette"></div>
    <h2>STAGE ${stage}: ${zoneData.name}</h2>
    <p>${zoneData.story}</p>
  `;
  box.onclick = () => {box.remove(); state.screen='dialog'; showScreen('dialog'); document.getElementById('dialogBody').textContent='Continue your journey.';};
  document.getElementById('game').appendChild(box);
  playTone(400,1.5,'triangle');
}

function openMenu(type){
  state.screen = 'menu';
  state.menuScreen = type;
  showScreen('menu');
  document.getElementById('menuTitle').textContent = type.toUpperCase();
  document.getElementById('menuContent').innerHTML = renderMenu(type);
}

function closeMenu(){
  showScreen('overworld');
  state.screen = 'overworld';
  if(owAnim) cancelAnimationFrame(owAnim);
  startOverworld();
}

function renderMenu(type){
  if(type === 'team') return state.party.map((c,idx)=>`
    <div style="display:flex;gap:12px;align-items:center;padding:8px 0;border-bottom:1px solid rgba(255,255,255,.1);">
      <div style="width:40px;height:40px;border-radius:50%;background:${TYPES[c.type].bg};display:flex;align-items:center;justify-content:center;font-weight:bold;font-size:.8rem;">${c.name.slice(0,2)}</div>
      <div>
        <div style="font-family:'Orbitron';color:${TYPES[c.type].color}">${c.name} <span style="font-size:.7rem;color:var(--dim);">Lv ${c.level}</span></div>
        <div style="font-size:.75rem;color:var(--dim);">HP: ${Math.round(c.hp)}/${c.maxHP || c.baseHP} • ATK: ${c.atk}</div>
      </div>
    </div>
  `).join('');
  else if(type === 'bag') return state.bag.map(i=>`
    <div style="padding:8px 0;border-bottom:1px solid rgba(255,255,255,.08);display:flex;justify-content:space-between;">
      <div><strong style="font-family:'Orbitron';">${i.item ? ITEMS.find(x=>x.id===i.item)?.name || i.item : i}</strong> <span style="color:var(--dim);font-size:.75rem;">×${i.qty}</span></div>
    </div>
  `).join('');
  else return '<p>Menu content here.</p>';
}

/* ===================== ENCYCLOPEDIA ===================== */

function openEncyclopedia(){
  openMenu('encyclopedia');
  document.getElementById('menuContent').innerHTML = `<div id="encContent"></div>`;
  document.getElementById('encContent').innerHTML = CREATURES.map(c=>`
    <div class="enc-entry">
      <div class="enc-avatar" style="background:${TYPES[c.type].bg}">${c.name.slice(0,1)}</div>
      <div class="enc-info">
        <h3>${c.name}</h3>
        <span>${TYPES[c.type].name} • HP:${c.baseHP} ATK:${c.baseATK} DEF:${c.baseDEF} SPD:${c.baseSPD}</span>
        <p style="font-size:.85rem;color:var(--dim);margin-top:4px;">${c.lore}</p>
      </div>
    </div>
  `).join('');
  document.getElementById('menuTitle').textContent = 'ENCYCLOPEDIA';
}

/* ===================== CAPTURE MINI-GAME ===================== */

function captureMiniGame(creature){
  const modal = document.createElement('div');
  modal.className = 'glass-modal';
  modal.style.cssText = 'position:absolute;inset:40px;z-index:50;background:rgba(11,12,21,.95);display:flex;flex-direction:column;align-items:center;';
  modal.innerHTML = `
    <h2 style="font-family:'Orbitron';color:var(--prism);">CAPTURE ATTEMPT</h2>
    <p style="margin-bottom:16px;color:var(--dim);">Press the zone as the marker passes through.</p>
    <div class="collection-mini-game">
      <div class="tap-zone" id="tapZone" onclick="handleCaptureTap()">
        <div class="marker"></div>
        <div class="hit-area"></div>
      </div>
      <div id="captureResult" style="font-family:'Orbitron';font-size:1.2rem;color:var(--text);"></div>
    </div>
    <button onclick="this.parentElement.remove()" style="margin-top:20px;">CONTINUE</button>
  `;
  document.getElementById('game').appendChild(modal);
  window.captureActive = {start:Date.now(), hits:0, zoneWidth:.3};
}

function handleCaptureTap(){
  if(!window.captureActive) return;
  window.captureActive.hits++;
  const hitArea = .3;
  const rand = Math.random();
  const success = rand > .5;
  const el = document.getElementById('captureResult');
  if(success){
    el.textContent = 'SUCCESS!';
    el.style.color = '#69f0ae';
    playTone(800,0.3,'sine');
  } else {
    el.textContent = 'MISS!';
    el.style.color = '#ff6b6b';
    playTone(150,0.2,'triangle');
  }
  window.captureActive = null;
  setTimeout(()=>document.getElementById('game').lastChild.remove(), 1200);
}

/* ===================== MAP ===================== */

function renderMap(){
  const content = document.getElementById('mapContent');
  content.innerHTML = ZONES.map((z,i)=>`
    <button onclick="travelZone(${z.id})" style="padding:20px;border-radius:16px;background:${z.bg};border:1px solid rgba(255,255,255,.1);color:#fff;cursor:pointer;">
      <div style="font-family:'Orbitron';font-weight:bold;">${z.name}</div>
      <div style="font-size:.7rem;color:rgba(255,255,255,.7);">${i+1}/6 Zones</div>
    </button>
  `).join('');
}

function travelZone(z){
  if(z <= state.zone + 1 || z === state.zone){
    state.zone = z;
    startOverworld();
    closeMenu();
  } else {
    showNotification('Zone not yet unlocked');
  }
}

/* ===================== SHRINE & SAVE ===================== */

function openMenu(type){
  state.screen = 'menu';
  state.menuScreen = type;
  showScreen('menu');
  document.getElementById('menuTitle').textContent = type.toUpperCase();
  document.getElementById('menuContent').innerHTML = renderMenu(type);
  if(type==='map') renderMap();
  if(type==='encyclopedia') openEncyclopedia();
  if(type==='save'){
    document.getElementById('menuContent').innerHTML = `
      <div style="display:flex;gap:12px;flex-wrap:wrap;">
        <button class="btn" onclick="loadSlot(0)">SLOT 1</button>
        <button class="btn" onclick="loadSlot(1)">SLOT 2</button>
        <button class="btn" onclick="loadSlot(2)">SLOT 3</button>
      </div>
      <div style="margin-top:16px;"><button class="btn btn-primary" onclick="saveGame(false)">SAVE</button></div>
    `;
  }
}

function healParty(){
  state.party.forEach(c => { c.hp = c.maxHP || c.baseHP + c.level*10; });
  showNotification('Party fully restored');
  startOverworld();
  closeMenu();
}

/* ===================== UTILS ===================== */

function showNotification(msg){
  const el = document.createElement('div');
  el.style.cssText = 'position:absolute;top:20px;left:50%;transform:translateX(-50%);padding:12px 24px;background:rgba(11,12,21,.9);backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.2);border-radius:20px;color:var(--text);font-family:"Orbitron";z-index:300;animation:fadeUp .3s ease;';
  el.textContent = msg;
  document.getElementById('game').appendChild(el);
  setTimeout(()=>el.remove(), 2000);
}

function showScreen(id){
  document.querySelectorAll('.screen').forEach(s=>s.classList.remove('show')); document.querySelectorAll('.screen').forEach(s=>s.classList.add('hide'));
  document.getElementById(id).classList.remove('hide'); document.getElementById(id).classList.add('show');
}

function closeDialog(){
  showScreen('overworld');
  state.screen = 'overworld';
  if(owAnim) cancelAnimationFrame(owAnim);
  startOverworld();
}

function closeMenu(){
  showScreen('overworld');
  state.screen = 'overworld';
  if(owAnim) cancelAnimationFrame(owAnim);
  startOverworld();
}

function startTutorial(){
  state.tutorialStep = 0;
  document.getElementById('tutTitle').textContent = 'WELCOME, BINDER';
  document.getElementById('tutBody').textContent = 'Use WASD or Arrow keys to move through the Veil. Approach glowing orbs to challenge wild creatures.';
  showScreen('tutorial');
}

function nextTutorialStep(){
  state.tutorialStep++;
  if(state.tutorialStep >= 2){
    document.getElementById('tutorial').classList.add('hide');
    showScreen('overworld');
    startOverworld();
    if(state.party.length === 0){
      state.party.push(creatureCopy(CREATURES[0])); // Give starter
      showNotification('You received Sprigkin!');
    }
  } else {
    document.getElementById('tutTitle').textContent = 'COMBAT & BINDING';
    document.getElementById('tutBody').textContent = 'Select abilities to fight. After victory, try to capture the creature with your capsules.';
  }
}

/* ===================== GAME INIT ===================== */

function startNewGame(){
  state = {
    zone:1, zoneIndex:0, player:{x:10,y:5,zone:1}, party:[],
    bag:[{item:'capsule_a',qty:3},{item:'capsule_b',qty:1},{item:'heal_potion',qty:2},{item:'sunseed',qty:1}],
    inventory:[], captured:new Set(), anchorsDefeated:0, storyStage:0, battlesWon:0, tutorialStep:0,
    gold:100, lastSave:null, activeBattle:null, screen:'overworld', menuScreen:'main', playingMusic:true, particles:[]
  };
  state.party.push(creatureCopy(CREATURES[0]), creatureCopy(CREATURES[3]), creatureCopy(CREATURES[5]));
  saveGame(true);
  startOverworld();
  startTutorial();
  initAudio(); startAmbient();
}

function continueGame(){
  initState();
  initAudio(); startAmbient();
  if(state.screen === 'title' && state.party && state.party.length > 0){
    startOverworld();
  } else if(state.party && state.party.length === 0){
    startNewGame();
  }
}

function returnToTitle(){
  showScreen('titleScreen');
  state.screen = 'title';
  stopAmbient();
  initAudio();
}

document.addEventListener('DOMContentLoaded', ()=>{
  initState();
  resize();
  // Initialize title background animation
  const tCanvas = document.getElementById('titleCanvas');
  const tCtx = tCanvas.getContext('2d');
  tCanvas.width = window.innerWidth; tCanvas.height = window.innerHeight;
  function titleLoop(){
    tCtx.fillStyle = '#0b0c15'; tCtx.fillRect(0,0,tCanvas.width,tCanvas.height);
    for(let i=0;i<50;i++){
      tCtx.beginPath();
      tCtx.arc(Math.random()*tCanvas.width, Math.random()*tCanvas.height, 1+Math.random()*2, 0, Math.PI*2);
      tCtx.fillStyle = `rgba(199,164,255,${Math.random()*0.5})`;
      tCtx.fill();
    }
    requestAnimationFrame(titleLoop);
  }
  titleLoop();
  
  if(state.party && state.party.length > 0){
    document.querySelector('#titleScreen .btn').focus();
  }
});

</script>
</body>
</html>
<!-- agent-meta {"model":"thinkingmachines/inkling","provider":"openrouter","persona":"basic","sessionId":"8d9ba1f9-2001-4f07-8321-9dfd4cbffffe","tokensIn":1288,"tokensOut":19335,"tokensTotal":20623,"turns":1,"toolCalls":0,"failedToolCalls":0,"timestamp":"2026-07-18T20:53:39.177Z"} -->