← Creature Collection results

Creature Collection

Gemini 3.5 Flash · composer

Initial view of Creature Collection

Browser check: Artifact runtime error. This is a technical check, not a quality review.

Reviews & generation details

Reviews

Human review · Jul 17, 2026 · rubric 1

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

Visual design
3 / 5
UX / feel
3 / 5
Works
3 / 5
Prompt spirit
4 / 5
Delight
3 / 5

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 120,002 bytes · SHA-256 2c3e80828aee
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Aethera: Chronicles of the Resonated</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;600;800&amp;family=Playfair+Display:ital,wght@0,700;1,400&amp;display=swap">
<style>
/* ── css: layout_and_title ── */
:root {
  --bg-primary: #0a0a0f;
  --panel-glass: rgba(18, 18, 27, 0.85);
  --border-glass: rgba(255, 255, 255, 0.12);
  --text-main: #f0f0ff;
  --text-muted: #a0a0c0;
  --accent-cyan: #00f3ff;
  --accent-gold: #ffbe3b;
  --accent-pink: #ff3377;
  --card-light: rgba(255, 255, 255, 0.04);
  --success-color: #39e99a;
  
  --font-main: 'Plus Jakarta Sans', sans-serif;
  --font-serif: 'Playfair Display', Georgia, serif;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  background-color: var(--bg-primary);
  color: var(--text-main);
  font-family: var(--font-main);
  overflow: hidden;
  user-select: none;
}

#game-container {
  position: relative;
  width: 1280px;
  height: 720px;
  margin: 0 auto;
  border: 1px solid var(--border-glass);
  background: radial-gradient(circle at center, #1b1c2b 0%, #030409 100%);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 10px 40px rgba(0,0,0,0.8);
}

.screen {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.screen.hidden {
  opacity: 0;
  transform: scale(0.98);
  pointer-events: none;
}

/* Icons and generic Buttons */
.icon-btn {
  position: absolute;
  top: 15px;
  right: 15px;
  background: var(--panel-glass);
  border: 1px solid var(--border-glass);
  color: var(--text-main);
  border-radius: 50%;
  width: 44px;
  height: 44px;
  font-size: 20px;
  cursor: pointer;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
}

.icon-btn:hover {
  border-color: var(--accent-cyan);
  background: rgba(0, 243, 255, 0.15);
}

.btn {
  font-family: var(--font-main);
  font-weight: 600;
  border-radius: 6px;
  border: 1px solid var(--border-glass);
  cursor: pointer;
  padding: 12px 24px;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  letter-spacing: 0.5px;
  font-size: 14px;
  outline: none;
}

.btn-primary {
  background: linear-gradient(135deg, #1d90f4, #5732dc);
  color: #fff;
  border: none;
  box-shadow: 0 4px 15px rgba(29, 144, 244, 0.3);
}
.btn-primary:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(29, 144, 244, 0.45);
}

.btn-secondary {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-main);
}
.btn-secondary:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.12);
}

.btn-accent {
  background: linear-gradient(135deg, #dc3265, #ff3377);
  color: #fff;
  border: none;
}
.btn-accent:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(255, 51, 119, 0.4);
}

.btn-glass {
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(8px);
  color: var(--accent-cyan);
  border-borderColor: rgba(0,243,255,0.2);
}
.btn-glass:hover {
  background: rgba(0, 243, 255, 0.1);
  border-color: var(--accent-cyan);
}

.btn-sm {
  padding: 6px 14px;
  font-size: 12px;
}
.btn-full {
  width: 100%;
}
.btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Luminous Title Screen */
#title-screen {
  justify-content: center;
  align-items: center;
  gap: 40px;
}

.logo-container {
  text-align: center;
}

.subtitle-engraved {
  font-size: 13px;
  letter-spacing: 6px;
  color: var(--accent-gold);
  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
  font-weight: 800;
  margin-bottom: 8px;
}

.sparkle-title {
  font-family: var(--font-serif);
  font-size: 110px;
  font-weight: 700;
  letter-spacing: -2px;
  background: linear-gradient(to right, #00f3ff, #ff3377, #ffbe3b, #00f3ff);
  background-size: 300% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: shimmer 8s linear infinite;
  filter: drop-shadow(0 15px 30px rgba(0,0,0,0.9));
}

@keyframes shimmer {
  0% { background-position: 0% 50%; }
  100% { background-position: 300% 50%; }
}

.title-action-panel {
  display: flex;
  gap: 20px;
  background: var(--panel-glass);
  padding: 30px 40px;
  border-radius: 12px;
  border: 1px solid var(--border-glass);
  box-shadow: 0 12px 30px rgba(0,0,0,0.5);
}

.title-footer {
  position: absolute;
  bottom: 25px;
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: 2px;
}
/* ── css: overworld ── */
/* Overworld & Main Adventure Styling */
#adventure-screen {
  justify-content: flex-start;
  align-items: center;
}

.hud {
  width: 100%;
  height: 64px;
  background: rgba(10, 10, 15, 0.85);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-glass);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 40px;
  z-index: 10;
}

.hud-left {
  display: flex;
  flex-direction: column;
}

.hud-header {
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--accent-gold);
  font-weight: 800;
}

.zone-label-text {
  font-size: 18px;
  font-weight: 800;
  color: var(--text-main);
  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
}

.anchor-display {
  background: rgba(255, 51, 119, 0.12);
  border: 1px solid rgba(255, 51, 119, 0.3);
  color: #ff5599;
  padding: 6px 14px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1px;
}

.canvas-wrapper {
  position: relative;
  width: 800px;
  height: 480px;
  margin-top: 40px;
  border-radius: 8px;
  overflow: hidden;
  border: 2px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 20px 50px rgba(0,0,0,0.6);
}

#overworld-canvas {
  display: block;
  background: #0d0f1a;
  cursor: pointer;
}

/* On-Screen Field Arrow Pad for touch/accessibility */
#field-arrow-pad {
  position: absolute;
  bottom: 20px;
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
  pointer-events: none;
}

#field-arrow-pad .pad-row {
  display: flex;
  gap: 6px;
}

.pad-btn {
  pointer-events: auto;
  width: 44px;
  height: 44px;
  border-radius: 8px;
  background: rgba(18, 18, 27, 0.85);
  border: 1px solid var(--border-glass);
  color: white;
  font-size: 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.1s;
}
.pad-btn:active {
  background: var(--accent-cyan);
  color: black;
}

.tips-footer {
  margin-top: 20px;
  font-size: 12px;
  color: var(--text-muted);
}

.tips-footer b {
  color: var(--accent-cyan);
}
/* ── css: modals_and_tutorial ── */
/* Glassmorphism Modals & Overlay Layouts */
.modal-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(4, 4, 8, 0.7);
  backdrop-filter: blur(8px);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.25s ease;
}

.modal-overlay.hidden {
  opacity: 0;
  pointer-events: none;
}

.glass-modal {
  background: var(--panel-glass);
  border: 1px solid var(--border-glass);
  width: 520px;
  border-radius: 12px;
  padding: 40px;
  box-shadow: 0 20px 40px rgba(0,0,0,0.8);
  display: flex;
  flex-direction: column;
  gap: 25px;
}

.ritual-header {
  font-family: var(--font-serif);
  font-size: 26px;
  font-weight: 700;
  color: var(--accent-gold);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  padding-bottom: 10px;
}

.intro-text {
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-muted);
  max-height: 250px;
  overflow-y: auto;
}

/* Starter Grid inside Tutorial */
.starter-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 15px;
  margin-top: 15px;
}

.starter-card {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border-glass);
  border-radius: 8px;
  padding: 15px;
  cursor: pointer;
  text-align: center;
  transition: all 0.25s;
}

.starter-card:hover {
  transform: translateY(-4px);
  border-color: var(--accent-cyan);
  background: rgba(0, 243, 255, 0.06);
}

.starter-card.selected {
  border-color: var(--accent-gold);
  background: rgba(255, 190, 59, 0.1);
  box-shadow: 0 4px 15px rgba(255, 190, 59, 0.2);
}

.starter-icon {
  font-size: 36px;
  margin-bottom: 10px;
  animation: idle-float 3s ease-in-out infinite alternate;
}

.starter-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--text-main);
  margin-bottom: 4px;
}

.starter-affinity {
  font-size: 11px;
  color: var(--accent-cyan);
  text-transform: uppercase;
  font-weight: 800;
  letter-spacing: 1px;
}
/* ── css: combat_arena_css ── */
/* Combat Screen & Arena Layouts */
#combat-screen {
  background: black;
  overflow: hidden;
}

.combat-background {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle at 50% 80%, #151a30 0%, #030409 100%);
  z-index: 1;
}

.combat-fx-canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 3;
  pointer-events: none;
}

.combat-arena {
  flex: 1;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 40px 10%);
  height: calc(100% - 190px);
}

.arena-row {
  display: flex;
  width: 100%;
  position: relative;
}

.opponents-row {
  justify-content: flex-end;
  gap: 20px;
  align-items: flex-start;
  margin-top: 20px;
}

.allies-row {
  justify-content: flex-start;
  align-items: flex-end;
  margin-bottom: 20px;
}

/* Battle Pod (Creature node in fighting arena) */
.battle-pod {
  background: rgba(18, 19, 31, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 12px;
  backdrop-filter: blur(8px);
  width: 250px;
  padding: 15px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  position: relative;
  transition: all 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
  box-shadow: 0 10px 25px rgba(0,0,0,0.4);
}

.battle-pod.targetable {
  border-color: var(--accent-cyan);
  cursor: pointer;
  box-shadow: 0 0 15px rgba(0, 243, 255, 0.4);
}
.battle-pod.targetable:hover {
  transform: scale(1.05);
}

.pod-creature-panel {
  display: flex;
  align-items: center;
  gap: 12px;
}

.pod-avatar {
  font-size: 38px;
  transition: transform 0.2s;
}

.pod-details {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.pod-name {
  font-weight: 700;
  font-size: 14px;
}

.pod-lvl {
  font-size: 11px;
  color: var(--accent-gold);
  font-weight: 800;
}

.pod-type-badge {
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(255,255,255,0.08);
  width: fit-content;
  margin-top: 4px;
  text-transform: uppercase;
}

/* HP & Status Elements */
.pod-stats {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.hp-bar-outer {
  width: 100%;
  height: 8px;
  background: rgba(255,255,255,0.1);
  border-radius: 4px;
  overflow: hidden;
}

.hp-bar-inner {
  height: 100%;
  background: linear-gradient(to right, #10b981, #059669);
  width: 100%;
  transition: width 0.4s ease-out;
}

.hp-bar-inner.medium {
  background: linear-gradient(to right, #f59e0b, #d97706);
}

.hp-bar-inner.low {
  background: linear-gradient(to right, #ef4444, #dc2626);
}

.hp-text {
  font-size: 11px;
  text-align: right;
  color: var(--text-muted);
}

/* Status effect Badges */
.status-tray {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.status-tag {
  font-size: 9px;
  font-weight: 800;
  padding: 1px 4px;
  border-radius: 3px;
  letter-spacing: 0.5px;
  color: #fff;
  text-transform: uppercase;
}

.status-tag.burn { background: #ef4444; }
.status-tag.frost { background: #3b82f6; }
.status-tag.shock { background: #eab308; }
.status-tag.barrier { background: #a855f7; }
.status-tag.regen { background: #10b981; }

/* Arena Animations */
@keyframes idle-float {
  0% { transform: translateY(0); }
  100% { transform: translateY(-6px); }
}

.allies-row .battle-pod {
  animation: idle-float 4s ease-in-out infinite alternate;
}

.opponents-row .battle-pod {
  animation: idle-float 4s ease-in-out infinite alternate-reverse;
}
/* ── css: combat_hud_controls ── */
/* Combat HUD & Action Deck Panel */
.combat-hud {
  height: 190px;
  background: rgba(10, 10, 15, 0.95);
  border-top: 1px solid var(--border-glass);
  display: flex;
  flex-direction: column;
  z-index: 10;
}

.combat-status-log {
  padding: 12px 30px;
  font-size: 13px;
  color: var(--accent-gold);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  font-weight: 600;
  letter-spacing: 0.5px;
  min-height: 44px;
}

.combat-control-deck {
  flex: 1;
  display: flex;
  padding: 15px 30px;
  position: relative;
}

.combat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 15px;
  width: 100%;
}

.action-card {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border-glass);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: var(--text-main);
  cursor: pointer;
  transition: all 0.2s;
}

.action-card:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--accent-cyan);
  transform: translateY(-2px);
}

.action-card .action-icon {
  font-size: 24px;
}

.action-card .action-label {
  font-weight: 700;
  font-size: 13px;
}

/* Combat Context Panels */
.combat-context-panel {
  position: absolute;
  top: 15px;
  left: 30px;
  right: 30px;
  bottom: 15px;
  background: #0a0a0f;
  border-radius: 8px;
  display: flex;
  gap: 15px;
  align-items: center;
}

.combat-context-panel.hidden {
  display: none !important;
}

.context-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  flex: 1;
  overflow-y: auto;
  padding-right: 5px;
  height: 100%;
}

.context-grid-scroll {
  display: flex;
  gap: 12px;
  flex: 1;
  overflow-x: auto;
  align-items: center;
  height: 100%;
}

/* Custom button style inside inner combat actions */
.combat-act-btn {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border-glass);
  border-radius: 6px;
  padding: 10px;
  color: var(--text-main);
  text-align: left;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 4px;
  height: 100%;
  justify-content: space-between;
  transition: all 0.15s;
}

.combat-act-btn:hover:not(:disabled) {
  border-color: var(--accent-cyan);
  background: rgba(0, 243, 255, 0.05);
}

.combat-act-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.btn-title-row {
  display: flex;
  justify-content: space-between;
  width: 100%;
  font-weight: 700;
  font-size: 13px;
}

.btn-type-sub {
  font-size: 10px;
  color: var(--text-muted);
}

.btn-meta {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
}

.btn-pp {
  color: var(--accent-gold);
}
/* ── css: shrine_and_shop ── */
/* Sanctuary (Shrine) and Shop */
#shrine-screen {
  background: radial-gradient(circle at center, #1b0e2b 0%, #030109 100%);
  justify-content: center;
  align-items: center;
}

.text-center {
  text-align: center;
}

.glass-container {
  background: var(--panel-glass);
  border: 1px solid var(--border-glass);
  width: 600px;
  border-radius: 12px;
  padding: 40px;
  box-shadow: 0 15px 40px rgba(0,0,0,0.8);
  display: flex;
  flex-direction: column;
  gap: 20px;
  position: relative;
  overflow: hidden;
}

.shrine-aura {
  position: absolute;
  top: -50px;
  left: calc(50% - 100px);
  width: 200px;
  height: 200px;
  background: radial-gradient(circle, rgba(168, 85, 247, 0.25) 0%, rgba(0,0,0,0) 70%);
  filter: blur(10px);
  pointer-events: none;
  animation: idle-float 6s ease-in-out infinite alternate;
}

.shrine-title {
  font-family: var(--font-serif);
  font-size: 32px;
  color: var(--accent-gold);
  text-shadow: 0 0 10px rgba(255, 190, 59, 0.4);
}

.shrine-desc {
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.5;
}

.shrine-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

/* Shop panel drawer layout */
.shop-drawer-panel {
  border-top: 1px solid var(--border-glass);
  padding-top: 20px;
  margin-top: 15px;
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.flux-meter {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent-cyan);
}

.shop-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  max-height: 180px;
  overflow-y: auto;
}

.shop-card {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border-glass);
  border-radius: 6px;
  padding: 10px;
  text-align: left;
  display: flex;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
  transition: all 0.15s;
}

.shop-card:hover {
  background: rgba(0,243,255,0.06);
  border-color: var(--accent-cyan);
}

.shop-card-info {
  display: flex;
  flex-direction: column;
}

.shop-item-name {
  font-size: 12px;
  font-weight: 700;
}

.shop-item-cost {
  font-size: 11px;
  color: var(--accent-gold);
}
/* ── css: codex_and_tabs ── */
/* Pause Menu / Codex & Reservoir layout */
.glass-codex-container {
  background: var(--panel-glass);
  border: 1px solid var(--border-glass);
  width: 1040px;
  height: 600px;
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 25px 50px rgba(0,0,0,0.9);
}

.codex-tabs {
  display: flex;
  background: rgba(10, 10, 15, 0.9);
  border-bottom: 1px solid var(--border-glass);
}

.tab-btn {
  background: none;
  border: none;
  outline: none;
  color: var(--text-muted);
  padding: 16px 24px;
  font-family: var(--font-main);
  font-weight: 700;
  font-size: 13px;
  cursor: pointer;
  letter-spacing: 1px;
  transition: all 0.2s;
  border-bottom: 2px solid transparent;
}

.tab-btn:hover {
  color: var(--text-main);
}

.tab-btn.active {
  color: var(--accent-cyan);
  border-color: var(--accent-cyan);
  background: rgba(0,243,255,0.04);
}

.tab-btn-close {
  margin-left: auto;
  color: var(--accent-pink);
}

.tab-btn-close:hover {
  background: rgba(255, 51, 119, 0.1);
}

.codex-content {
  flex: 1;
  padding: 30px;
  overflow: hidden;
  position: relative;
}

.tab-panel {
  display: none;
  width: 100%;
  height: 100%;
}

.tab-panel.active {
  display: block;
}

/* Team Tab Styling */
.team-layout {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 30px;
  height: 100%;
}

.team-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
  padding-right: 5px;
}

.team-card {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border-glass);
  border-radius: 8px;
  padding: 12px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 12px;
  transition: all 0.15s;
}

.team-card:hover {
  background: rgba(255,255,255,0.06);
  border-color: var(--accent-cyan);
}

.team-card.selected {
  background: rgba(0,243,255,0.06);
  border-color: var(--accent-cyan);
  box-shadow: 0 4px 12px rgba(0,243,255,0.15);
}

.team-card-icon {
  font-size: 28px;
}

.team-card-info {
  display: flex;
  flex-direction: column;
}

.team-card-name {
  font-weight: 700;
  font-size: 13px;
}

.team-card-level {
  font-size: 11px;
  color: var(--accent-gold);
}

/* Detail view */
.team-detail-pane {
  background: rgba(0, 0, 0, 0.2);
  border: 1px solid var(--border-glass);
  border-radius: 8px;
  padding: 25px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.detail-header {
  display: flex;
  gap: 20px;
  align-items: center;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  padding-bottom: 15px;
}

.detail-avatar-large {
  font-size: 64px;
}

.detail-meta-text {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.detail-meta-title {
  font-family: var(--font-serif);
  font-size: 24px;
  font-weight: 700;
}

.detail-pills {
  display: flex;
  gap: 6px;
}

.detail-pill-type {
  font-size: 10px;
  font-weight: 800;
  padding: 2px 8px;
  border-radius: 4px;
  background: rgba(255,255,255,0.1);
  text-transform: uppercase;
}

.detail-grid-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 15px;
}

.detail-stat-box {
  background: rgba(255,255,255,0.02);
  border: 1px solid var(--border-glass);
  padding: 10px;
  border-radius: 6px;
  text-align: center;
}

.detail-stat-lbl {
  font-size: 10px;
  color: var(--text-muted);
  text-transform: uppercase;
  margin-bottom: 4px;
}

.detail-stat-val {
  font-size: 16px;
  font-weight: 700;
  color: var(--text-main);
}

.detail-lore-blurb {
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
  font-style: italic;
  background: rgba(255,255,255,0.01);
  padding: 10px;
  border-radius: 6px;
  border-left: 2px solid var(--accent-gold);
}

/* Abilities grid inside detail pane */
.detail-abilities {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.detail-abilities-title {
  font-size: 12px;
  font-weight: 800;
  color: var(--accent-cyan);
  letter-spacing: 1px;
}

.detail-ab-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.detail-ab-card {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border-glass);
  padding: 10px;
  border-radius: 6px;
  font-size: 12px;
}

.neutral-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--text-muted);
  text-align: center;
  font-size: 14px;
}
/* ── css: inventory_and_glossary ── */
/* Inventory and Glossary Styles */
.inv-split {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 25px;
  height: calc(100% - 40px);
}

.inv-list-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  overflow-y: auto;
  align-content: flex-start;
}

.inv-card {
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border-glass);
  border-radius: 8px;
  padding: 12px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  transition: all 0.15s;
}

.inv-card:hover {
  background: rgba(255,255,255,0.06);
  border-color: var(--accent-cyan);
}

.inv-label-group {
  display: flex;
  align-items: center;
  gap: 8px;
}

.inv-icon {
  font-size: 20px;
}

.inv-name {
  font-size: 13px;
  font-weight: 700;
}

.inv-qty {
  font-size: 11px;
  background: rgba(255, 190, 59, 0.12);
  color: var(--accent-gold);
  padding: 2px 6px;
  border-radius: 4px;
}

.inv-desc-pane {
  background: rgba(0,0,0,0.25);
  border: 1px solid var(--border-glass);
  border-radius: 8px;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.inv-desc-head {
  font-size: 16px;
  font-weight: 700;
  color: var(--accent-cyan);
}

.inv-desc-text {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.5;
}

/* Glossary panel layout */
.glossary-grid {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  gap: 12px;
  overflow-y: auto;
  height: 100%;
}

.gloss-card {
  aspect-ratio: 1;
  background: rgba(255,255,255,0.02);
  border: 3px solid rgba(255,255,255,0.04);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  cursor: help;
  transition: all 0.15s;
}

.gloss-card.unlocked {
  background: rgba(255,255,255,0.05);
  border-color: var(--accent-cyan);
}

.gloss-card.unlocked:hover {
  transform: scale(1.05);
}

.gloss-card.locked {
  opacity: 0.4;
}

.gloss-idx {
  position: absolute;
  top: 4px;
  left: 6px;
  font-size: 9px;
  color: var(--text-muted);
  font-weight: 800;
}

.gloss-avatar {
  font-size: 28px;
}

.gloss-name {
  font-size: 9px;
  text-align: center;
  overflow: hidden;
  text-transform: uppercase;
  font-weight: 800;
  width: 90%;
  margin-top: 4px;
}

/* Rhythm Harmonization Mini Game */
.resonance-ring-container {
  position: relative;
  width: 200px;
  height: 200px;
  margin: 20px auto;
  display: flex;
  align-items: center;
  justify-content: center;
}

.resonant-circle-target {
  position: absolute;
  width: 140px;
  height: 140px;
  border-radius: 50%;
  border: 4px dashed var(--accent-cyan);
  box-shadow: 0 0 10px rgba(0, 243, 255, 0.2);
}

.resonant-circle-pulse {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 3px solid var(--accent-pink);
  transform: scale(1);
}

.pulse-percentage {
  font-size: 32px;
  font-weight: 800;
  color: var(--text-main);
  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
  z-index: 2;
}

.btn-full-pulse {
  animation: pulse-border 1.5s infinite alternate;
}

@keyframes pulse-border {
  0% { transform: scale(1); box-shadow: 0 0 10px rgba(29, 144, 244, 0.3); }
  100% { transform: scale(1.02); box-shadow: 0 0 25px rgba(29, 144, 244, 0.6); }
}

/* Narrative Vignette layered overlays */
.vignette-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle, rgba(13,10,31,0.9) 0%, rgba(3,2,10,0.95) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
  transition: opacity 0.5s ease;
}

.vignette-layer.hidden {
  opacity: 0;
  pointer-events: none;
}

.vignette-dialog-bubble {
  max-width: 600px;
  background: var(--panel-glass);
  border: 1px solid var(--border-glass);
  padding: 40px;
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  box-shadow: 0 10px 40px rgba(0,0,0,0.9);
}

.vignette-title {
  font-weight: 800;
  color: var(--accent-gold);
  letter-spacing: 4px;
  font-size: 13px;
}

.vignette-body {
  font-family: var(--font-serif);
  font-size: 20px;
  line-height: 1.6;
  color: var(--text-main);
}
</style>
</head>
<body>
<div id="game-container">
  <!-- AUDIO / SOUND TOGGLE IN CORNER -->
  <button id="mute-btn" class="icon-btn" title="Toggle Music">🔊</button>

  <!-- TITLE SCREEN -->
  <div id="title-screen" class="screen active">
    <div class="logo-container">
      <div class="subtitle-engraved">CHRONICLES OF THE RESONATED</div>
      <h1 class="sparkle-title">AETHERA</h1>
    </div>
    <div class="title-action-panel">
      <button id="new-game-btn" class="btn btn-primary">Begin New Resonance</button>
      <button id="continue-btn" class="btn btn-secondary" disabled="">Resume Continuum</button>
    </div>
    <div class="title-footer">Crafted in the Silent Spire — Version 1.0</div>
  </div>

  <!-- INTRO INTRODUCTORY TUTORIAL MODAL OVERLAY -->
  <div id="intro-modal" class="modal-overlay hidden">
    <div class="glass-modal">
      <h2 id="intro-title" class="ritual-header">Awakening Sequence</h2>
      <div id="intro-body" class="intro-text"></div>
      <div id="starter-choice-container" class="starter-grid hidden"></div>
      <div class="modal-actions">
        <button id="intro-next-btn" class="btn btn-primary">Process</button>
      </div>
    </div>
  </div>

  <!-- MAIN ADVENTURE HUD & OVERWORLD SPLIT -->
  <div id="adventure-screen" class="screen hidden">
    <div class="hud">
      <div class="hud-left">
        <div class="hud-header">🛡️ CURRENT REGION</div>
        <div id="hud-zone-name" class="zone-label-text">Verdant Ascent</div>
      </div>
      <div class="hud-center">
        <div id="anchors-count" class="anchor-display">Anchors Unchained: 0/4</div>
      </div>
      <div class="hud-right">
        <button id="menu-btn" class="btn btn-sm btn-glass">📂 Codex &amp; Reservoir</button>
      </div>
    </div>

    <!-- CANVAS VIEWPORT -->
    <div class="canvas-wrapper">
      <canvas id="overworld-canvas" width="800" height="480"></canvas>
      <div id="field-arrow-pad">
        <button id="pad-up" class="pad-btn">▲</button>
        <div class="pad-row">
          <button id="pad-left" class="pad-btn">◀</button>
          <button id="pad-down" class="pad-btn">▼</button>
          <button id="pad-right" class="pad-btn">▶</button>
        </div>
      </div>
    </div>

    <div class="tips-footer">
      <span>Move with <b>WASD / Arrow Keys</b> or Tap/Click Canvas/Pad. Find wild battles, resting shrines, or the Zone Guardian.</span>
    </div>
  </div>

  <!-- COMBAT INTERFACE -->
  <div id="combat-screen" class="screen hidden">
    <div class="combat-background" id="combat-bg-grad"></div>
    <!-- Particle canvas for damage pops & sparks -->
    <canvas id="combat-particle-canvas" class="combat-fx-canvas" width="800" height="480"></canvas>

    <div class="combat-arena">
      <!-- Enemy Party Row (1 to 3 Targets) -->
      <div id="combat-opponents" class="arena-row opponents-row"></div>

      <!-- Player Active Ally Node -->
      <div id="combat-allies" class="arena-row allies-row">
        <!-- Main Battling Creature Display -->
        <div class="battle-pod" id="active-ally-pod"></div>
      </div>
    </div>

    <!-- COMBAT FOOTER / CONTROLS -->
    <div class="combat-hud">
      <div class="combat-status-log" id="combat-log">A feral presence blocks your path!</div>
      
      <div class="combat-control-deck">
        <!-- Main Controls Grid -->
        <div id="combat-main-actions" class="combat-grid">
          <button id="act-attack-btn" class="action-card">
            <span class="action-icon">⚔️</span>
            <span class="action-label">Abilities</span>
          </button>
          <button id="act-switch-btn" class="action-card">
            <span class="action-icon">🔄</span>
            <span class="action-label">Switch Ally</span>
          </button>
          <button id="act-collect-btn" class="action-card">
            <span class="action-icon">💎</span>
            <span class="action-label">Harmonize</span>
          </button>
          <button id="act-inventory-btn" class="action-card">
            <span class="action-icon">🎒</span>
            <span class="action-label">Sustain</span>
          </button>
        </div>

        <!-- Dynamic Context Panels -->
        <div id="combat-abilities-panel" class="combat-context-panel hidden">
          <div class="context-grid" id="abilities-grid"></div>
          <button id="combat-abilities-back" class="btn btn-sm btn-secondary btn-full">⬅ Back</button>
        </div>

        <div id="combat-allies-panel" class="combat-context-panel hidden">
          <div class="context-grid-scroll" id="switch-allies-grid"></div>
          <button id="combat-switch-back" class="btn btn-sm btn-secondary btn-full">⬅ Back</button>
        </div>

        <div id="combat-items-panel" class="combat-context-panel hidden">
          <div class="context-grid" id="combat-items-grid"></div>
          <button id="combat-items-back" class="btn btn-sm btn-secondary btn-full">⬅ Back</button>
        </div>
      </div>
    </div>
  </div>

  <!-- SHRINE / SANCTUARY INTERFACE -->
  <div id="shrine-screen" class="screen hidden">
    <div class="glass-container text-center">
      <div class="shrine-aura"></div>
      <h1 class="shrine-title">⛩️ Sanctuary Anchor</h1>
      <p class="shrine-desc">The localized field humming here cures essence fatigue and stores your cosmic progression index.</p>
      
      <div class="shrine-actions">
        <button id="shrine-heal" class="btn btn-primary">💖 Restore Celestial Essences</button>
        <button id="shrine-save" class="btn btn-secondary">💾 Sync Cosmic Progression INDEX</button>
        <button id="shrine-shop" class="btn btn-secondary">🏪 Exchange Flux (Shop)</button>
        <button id="shrine-exit" class="btn btn-accent btn-full">🛰️ Step Back to Cosmos</button>
      </div>

      <!-- SHOP INNER DRAWER -->
      <div id="shop-drawer" class="shop-drawer-panel hidden">
        <h3 class="ritual-header">Sanctuary Merchant Flux Exchange</h3>
        <div class="flux-meter">Flux Wallet: <span id="shop-flux-count">0</span> Aura-units</div>
        <div class="shop-grid" id="shop-items-grid"></div>
      </div>
    </div>
  </div>

  <!-- CODES / RESERVOIR DIALOGUE OVERLAY (MENU) -->
  <div id="menu-overlay" class="modal-overlay hidden">
    <div class="glass-codex-container">
      <div class="codex-tabs">
        <button class="tab-btn active" data-tab="tab-team">👥 RESONA-TEAM</button>
        <button class="tab-btn" data-tab="tab-inventory">🎒 CHRONO-PACK</button>
        <button class="tab-btn" data-tab="tab-encyclopedia">📖 GLOSSARY (27)</button>
        <button id="codex-close" class="tab-btn tab-btn-close">❌ EXIT</button>
      </div>

      <div class="codex-content">
        <!-- TEAM TAB -->
        <div id="tab-team" class="tab-panel active">
          <div class="team-layout">
            <div class="team-list" id="menu-team-list"></div>
            <div class="team-detail-pane" id="menu-team-detail">
              <p class="neutral-placeholder">Select a Resonated creature to examine cosmic alignment, traits, and prompt transformation catalyst.</p>
            </div>
          </div>
        </div>

        <!-- INVENTORY TAB -->
        <div id="tab-inventory" class="tab-panel">
          <div class="inv-header">🎒 Chrono-Pack Vault</div>
          <div class="inv-split">
            <div id="menu-inv-list" class="inv-list-grid"></div>
            <div id="menu-inv-desc" class="inv-desc-pane">
              <p class="neutral-placeholder">Hover/Click custom tools to read function, use transformation catalysts or restockers.</p>
            </div>
          </div>
        </div>

        <!-- ENCYCLOPEDIA TAB -->
        <div id="tab-encyclopedia" class="tab-panel">
          <div class="glossary-grid" id="menu-encyclopedia-grid"></div>
        </div>
      </div>
    </div>
  </div>

  <!-- MINI-GAME HARMONIZE OVERLAY -->
  <div id="harmonize-modal" class="modal-overlay hidden">
    <div class="glass-modal text-center">
      <h2 class="ritual-header">Rhythm Resonance Key</h2>
      <p id="harmonize-prompt" class="small-italic">Tap inside the circle when the expanding pulse exactly overlaps the outer ring!</p>
      
      <div class="resonance-ring-container">
        <div class="resonant-circle-target"></div>
        <div class="resonant-circle-pulse" id="resonant-pulse"></div>
        <div class="pulse-percentage" id="pulse-percent-disp">0%</div>
      </div>

      <button id="harmonize-tap-btn" class="btn btn-primary btn-full-pulse">💥 HARMONIZE!</button>
    </div>
  </div>

  <!-- FLOATING VIGNETTE DIALOG -->
  <div id="narrative-vignette" class="vignette-layer hidden">
    <div class="vignette-dialog-bubble">
      <div class="vignette-title" id="narr-vignette-title">CONVERGENCE EVENT</div>
      <div class="vignette-body" id="narr-vignette-body">...</div>
      <button id="narr-vignette-btn" class="btn btn-sm btn-accent">Synthesize</button>
    </div>
  </div>
</div>
<script>
'use strict';

// ── class: SynthEngine ──
// WEBAUDIO BACKEND INVENTED pad synthesizer
class SynthEngine {
  constructor() {
    this.ctx = null;
    this.muted = false;
    this.primaryOsc = null;
    this.secondaryOsc = null;
    this.gainNode = null;
  }

  init() {
    if (this.ctx) return;
    const AudioContextClass = window.AudioContext || window.webkitAudioContext;
    if (!AudioContextClass) return;
    this.ctx = new AudioContextClass();
    this.gainNode = this.ctx.createGain();
    this.gainNode.gain.setValueAtTime(0.04, this.ctx.currentTime);
    this.gainNode.connect(this.ctx.destination);
  }

  toggle() {
    this.muted = !this.muted;
    if (this.gainNode) {
      this.gainNode.gain.setValueAtTime(this.muted ? 0 : 0.04, this.ctx.currentTime);
    }
    return this.muted;
  }

  playAmbientLoop() {
    this.init();
    if (!this.ctx || this.primaryOsc) return;

    try {
      this.primaryOsc = this.ctx.createOscillator();
      this.primaryOsc.type = 'triangle';
      this.primaryOsc.frequency.setValueAtTime(110, this.ctx.currentTime); // A2

      this.secondaryOsc = this.ctx.createOscillator();
      this.secondaryOsc.type = 'sine';
      this.secondaryOsc.frequency.setValueAtTime(165, this.ctx.currentTime); // E3

      this.primaryOsc.connect(this.gainNode);
      this.secondaryOsc.connect(this.gainNode);

      this.primaryOsc.start();
      this.secondaryOsc.start();

      // Slow dynamic pitch modulation (LFO-like sweep)
      let t = 0;
      setInterval(() => {
        if (this.ctx && !this.muted) {
          t += 0.05;
          const shift = Math.sin(t) * 1.5;
          this.primaryOsc.frequency.setValueAtTime(110 + shift, this.ctx.currentTime);
          this.secondaryOsc.frequency.setValueAtTime(165 - shift, this.ctx.currentTime);
        }
      }, 100);
    } catch (e) {
      console.warn("Audio Context blocked or failed during boot: ", e);
    }
  }

  triggerBeep(freq, type = 'sine', duration = 0.1) {
    this.init();
    if (!this.ctx || this.muted) return;
    try {
      const osc = this.ctx.createOscillator();
      const gain = this.ctx.createGain();
      osc.type = type;
      osc.frequency.setValueAtTime(freq, this.ctx.currentTime);
      gain.gain.setValueAtTime(0.03, this.ctx.currentTime);
      gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + duration);
      osc.connect(gain);
      gain.connect(this.ctx.destination);
      osc.start();
      osc.stop(this.ctx.currentTime + duration);
    } catch(e) {}
  }

  triggerSuperEffectiveNoise() {
    this.triggerBeep(440, 'triangle', 0.1);
    setTimeout(() => this.triggerBeep(880, 'sine', 0.15), 80);
  }

  triggerCritNoise() {
    this.triggerBeep(220, 'sawtooth', 0.1);
    setTimeout(() => this.triggerBeep(660, 'sawtooth', 0.1), 50);
    setTimeout(() => this.triggerBeep(1320, 'sine', 0.2), 100);
  }
}

// ── const: AudioController ──
const AudioController = new SynthEngine();

// ── const: AFFINITIES ──
// SEVEN ORIGINAL COSMIC TYPES (AFFINITIES)
// Chart Rules:
// Pyre (A) > Bloom (B) > Hydro (C) > Pyre (A)
// Golem (D) > Pyre (A) & Hydro (C)
// Zephyr (E) > Golem (D) & Bloom (B)
// Nova (F) beats all others slightly (+30% bonus vs others)
// Epoch (G) resists everything (takes 0.5x) but is weak to Nova (F) (takes 2.0x)

const AFFINITIES = {
  PYRE: 'pyre',     // A
  BLOOM: 'bloom',   // B
  HYDRO: 'hydro',   // C
  GOLEM: 'golem',   // D
  ZEPHYR: 'zephyr', // E
  NOVA: 'nova',     // F
  EPOCH: 'epoch'    // G
};

// ── const: TYPE_COLORS ──
const TYPE_COLORS = {
  pyre: '#ef4444',
  bloom: '#10b981',
  hydro: '#06b6d4',
  golem: '#b45309',
  zephyr: '#a855f7',
  nova: '#f59e0b',
  epoch: '#ec4899'
};

// ── function: getDamageMultiplier ──
function getDamageMultiplier(attType, defType) {
  // Epoch resists everything except Nova
  if (defType === AFFINITIES.EPOCH) {
    if (attType === AFFINITIES.NOVA) return 2.0;
    return 0.5;
  }
  // Nova beats all other standard types slightly
  if (attType === AFFINITIES.NOVA) {
    return 1.3;
  }

  // Base Cycle
  if (attType === AFFINITIES.PYRE && defType === AFFINITIES.BLOOM) return 2.0;
  if (attType === AFFINITIES.BLOOM && defType === AFFINITIES.HYDRO) return 2.0;
  if (attType === AFFINITIES.HYDRO && defType === AFFINITIES.PYRE) return 2.0;

  // D > A & C
  if (attType === AFFINITIES.GOLEM && (defType === AFFINITIES.PYRE || defType === AFFINITIES.HYDRO)) return 2.0;

  // E > D & B
  if (attType === AFFINITIES.ZEPHYR && (defType === AFFINITIES.GOLEM || defType === AFFINITIES.BLOOM)) return 2.0;

  // If match or general resist cycle back:
  if (defType === AFFINITIES.PYRE && attType === AFFINITIES.BLOOM) return 0.5;
  if (defType === AFFINITIES.BLOOM && attType === AFFINITIES.PYRE) return 2.0;

  return 1.0;
}

// ── const: ZONE_DATABASES ──
// OVERWORLD DATABASE & CONFIGURATION
// 6 Distinct zones, customized with unique templates, background graphics on canvas, particle styles.
const ZONE_DATABASES = [
  {
    idx: 0,
    name: "Verdant Ascent",
    color: "#0f3a20",
    gradient: ["#021c0e", "#103f26"],
    particleColor: "#3bf283",
    encounterRate: 0.12,
    storyNode: "The spatial weave begins to unravel in the lower climbs. Secure the first Anchor.",
    guardianName: "Arch-Weaver Vael",
    guardianAvatar: "🕷️",
    unlocked: true
  },
  {
    idx: 1,
    name: "Submerged Siphon",
    color: "#083344",
    gradient: ["#02151e", "#0e3a4d"],
    particleColor: "#0ef1f9",
    encounterRate: 0.14,
    storyNode: "A liquid turbine keeps the lower chambers aligned. Quiet the guardian vortex.",
    guardianName: "Krakenoid Sovereign",
    guardianAvatar: "🐙",
    unlocked: false
  },
  {
    idx: 2,
    name: "Foundry Spire",
    color: "#450a0a",
    gradient: ["#210404", "#520c0c"],
    particleColor: "#f9620e",
    encounterRate: 0.15,
    storyNode: "Searing heat maintains structural integrity. Transcend the molten crucible.",
    guardianName: "Automaton Solderer",
    guardianAvatar: "🤖",
    unlocked: false
  },
  {
    idx: 3,
    name: "Basalt Monolith",
    color: "#271b12",
    gradient: ["#140d08", "#332215"],
    particleColor: "#eab308",
    encounterRate: 0.16,
    storyNode: "High-density matrix blockades. Forge onwards past the crushing pressure.",
    guardianName: "Goliath Monolith",
    guardianAvatar: "🗿",
    unlocked: false
  },
  {
    idx: 4,
    name: "Windswept Spire",
    color: "#2e1065",
    gradient: ["#170535", "#3c1285"],
    particleColor: "#a855f7",
    encounterRate: 0.18,
    storyNode: "Violent jetstreams swirl near the outer exosphere. Tame the gale.",
    guardianName: "Vortex Sphinx",
    guardianAvatar: "🧝",
    unlocked: false
  },
  {
    idx: 5,
    name: "Singularity Core",
    color: "#030712",
    gradient: ["#000000", "#111827"],
    particleColor: "#ec4899",
    encounterRate: 0.20,
    storyNode: "The final localized anchor holding Aethera together is spinning out of control.",
    guardianName: "Singularity Overlord",
    guardianAvatar: "👁️",
    unlocked: false
  }
];

// ── const: COMBAT_ABILITIES ──
// 43 UNIQUE ABILITIES FOR COMBAT
// Format: name, power, accuracy, affinity, cost, effect (burn, frost, shock, barrier, regen, heal, multiHit, priority, atkBoost, spdBoost, critBoost)
const COMBAT_ABILITIES = {
  // PYRE Type
  SearingSpike: { name: "Searing Spike", power: 45, accuracy: 0.95, affinity: 'pyre', cost: 10, effect: 'burn' },
  BlazeBurst: { name: "Blaze Burst", power: 65, accuracy: 0.90, affinity: 'pyre', cost: 15, effect: 'burn' },
  CinderSpark: { name: "Cinder Spark", power: 30, accuracy: 1.00, affinity: 'pyre', cost: 5, effect: 'priority' },
  PyreVolley: { name: "Pyre Volley", power: 25, accuracy: 0.85, affinity: 'pyre', cost: 12, effect: 'multiHit' },
  InfernoRift: { name: "Inferno Rift", power: 90, accuracy: 0.80, affinity: 'pyre', cost: 20, effect: 'atkBoost' },
  FlameVeil: { name: "Flame Veil", power: 0, accuracy: 1.00, affinity: 'pyre', cost: 8, effect: 'barrier' },

  // BLOOM Type
  IvyLash: { name: "Ivy Lash", power: 45, accuracy: 0.95, affinity: 'bloom', cost: 10, effect: 'spdBoost' },
  LeafTracer: { name: "Leaf Tracer", power: 30, accuracy: 1.00, affinity: 'bloom', cost: 6, effect: 'priority' },
  FloraRenew: { name: "Flora Renew", power: 0, accuracy: 1.00, affinity: 'bloom', cost: 12, effect: 'regen' },
  ThornEruption: { name: "Thorn Eruption", power: 75, accuracy: 0.85, affinity: 'bloom', cost: 18, effect: 'critBoost' },
  ForestHeal: { name: "Forest Heal", power: 40, accuracy: 1.00, affinity: 'bloom', cost: 15, effect: 'heal' },
  RootBnd: { name: "Root Bind", power: 55, accuracy: 0.90, affinity: 'bloom', cost: 10, effect: 'frost' },

  // HYDRO Type
  AquaJet: { name: "Aqua Jet", power: 35, accuracy: 1.00, affinity: 'hydro', cost: 5, effect: 'priority' },
  VortexSpout: { name: "Vortex Spout", power: 60, accuracy: 0.95, affinity: 'hydro', cost: 12, effect: 'frost' },
  VaporShield: { name: "Vapor Shield", power: 0, accuracy: 1.00, affinity: 'hydro', cost: 8, effect: 'barrier' },
  TidalCrash: { name: "Tidal Crash", power: 80, accuracy: 0.85, affinity: 'hydro', cost: 18, effect: 'spdBoost' },
  RadianSip: { name: "Radian Sip", power: 45, accuracy: 0.95, affinity: 'hydro', cost: 12, effect: 'heal' },
  MistGlow: { name: "Mist Glow", power: 0, accuracy: 1.00, affinity: 'hydro', cost: 10, effect: 'regen' },

  // GOLEM Type
  StoneCrush: { name: "Stone Crush", power: 50, accuracy: 0.95, affinity: 'golem', cost: 10, effect: 'none' },
  DustStorm: { name: "Dust Storm", power: 35, accuracy: 0.90, affinity: 'golem', cost: 8, effect: 'frost' },
  ClayShield: { name: "Clay Shield", power: 0, accuracy: 1.00, affinity: 'golem', cost: 12, effect: 'barrier' },
  MeteorTome: { name: "Meteor Tome", power: 85, accuracy: 0.80, affinity: 'golem', cost: 20, effect: 'atkBoost' },
  FossilClaw: { name: "Fossil Claw", power: 60, accuracy: 0.90, affinity: 'golem', cost: 12, effect: 'critBoost' },
  RockTumbler: { name: "Rock Tumbler", power: 30, accuracy: 0.85, affinity: 'golem', cost: 15, effect: 'multiHit' },

  // ZEPHYR Type
  GaleGusta: { name: "Gale Gusta", power: 45, accuracy: 0.95, affinity: 'zephyr', cost: 10, effect: 'spdBoost' },
  ZephyrClip: { name: "Zephyr Clip", power: 30, accuracy: 1.00, affinity: 'zephyr', cost: 6, effect: 'priority' },
  TornadoVane: { name: "Tornado Vane", power: 75, accuracy: 0.85, affinity: 'zephyr', cost: 16, effect: 'shock' },
  SonicCloud: { name: "Sonic Cloud", power: 0, accuracy: 1.00, affinity: 'zephyr', cost: 8, effect: 'barrier' },
  FeatherLick: { name: "Feather Lick", power: 40, accuracy: 0.95, affinity: 'zephyr', cost: 10, effect: 'regen' },
  AetherDrift: { name: "Aether Drift", power: 95, accuracy: 0.80, affinity: 'zephyr', cost: 22, effect: 'atkBoost' },

  // NOVA Type
  NovaPulse: { name: "Nova Pulse", power: 55, accuracy: 0.95, affinity: 'nova', cost: 12, effect: 'critBoost' },
  AuraBlast: { name: "Aura Blast", power: 75, accuracy: 0.90, affinity: 'nova', cost: 18, effect: 'shock' },
  GlitterBeam: { name: "Glitter Beam", power: 35, accuracy: 1.00, affinity: 'nova', cost: 8, effect: 'priority' },
  CosmicSweep: { name: "Cosmic Sweep", power: 25, accuracy: 0.85, affinity: 'nova', cost: 14, effect: 'multiHit' },
  Supernova: { name: "Supernova", power: 100, accuracy: 0.75, affinity: 'nova', cost: 25, effect: 'burn' },
  StarlightRefrain: { name: "Starlight Refrain", power: 50, accuracy: 1.00, affinity: 'nova', cost: 15, effect: 'heal' },

  // EPOCH Type
  StasisLock: { name: "Stasis Lock", power: 50, accuracy: 0.95, affinity: 'epoch', cost: 12, effect: 'frost' },
  TemporalStrike: { name: "Temporal Strike", power: 70, accuracy: 0.90, affinity: 'epoch', cost: 16, effect: 'shock' },
  ContinuumWarp: { name: "Continuum Warp", power: 0, accuracy: 1.00, affinity: 'epoch', cost: 15, effect: 'regen' },
  ParadoxScythe: { name: "Paradox Scythe", power: 90, accuracy: 0.85, affinity: 'epoch', cost: 24, effect: 'critBoost' },
  EpochArmor: { name: "Epoch Armor", power: 0, accuracy: 1.00, affinity: 'epoch', cost: 10, effect: 'barrier' },
  ChronosEcho: { name: "Chronos Echo", power: 30, accuracy: 0.85, affinity: 'epoch', cost: 15, effect: 'multiHit' },
  AeonHeal: { name: "Aeon Heal", power: 45, accuracy: 1.00, affinity: 'epoch', cost: 18, effect: 'heal' }
};

// ── const: CREATURES_DB ──
// CREATURES DATABASE
// 18 Base Forms + 9 Catalyst Transformed Forms = 27 total.
// Each has: key, name, avatar (emoji), affinity, stats, desc, learnset (level up abilities list), catalyst req.
const CREATURES_DB = [
  // Base Creatures (Indices 1 to 18)
  {
    id: 1, name: "Pyroshell", avatar: "🐢", affinity: 'pyre', baseStats: { hp: 55, atk: 52, def: 65, spd: 40 },
    lore: "Its shell maintains a continuous ember. Very sturdy but heavy.",
    learnset: [
      { lvl: 1, move: 'SearingSpike' },
      { lvl: 5, move: 'CinderSpark' },
      { lvl: 12, move: 'FlameVeil' },
      { lvl: 25, move: 'InfernoRift' }
    ],
    transformsTo: 19, transformLvl: 18, catalyst: "Cinder Dynamo"
  },
  {
    id: 2, name: "Sporepup", avatar: "🐕", affinity: 'bloom', baseStats: { hp: 48, atk: 56, def: 42, spd: 60 },
    lore: "Sniffs out ancient soil deposits to fuel the fungal spores on its back.",
    learnset: [
      { lvl: 1, move: 'IvyLash' },
      { lvl: 4, move: 'LeafTracer' },
      { lvl: 10, move: 'ForestHeal' },
      { lvl: 22, move: 'ThornEruption' }
    ],
    transformsTo: 20, transformLvl: 18, catalyst: "Spore Nucleus"
  },
  {
    id: 3, name: "Finlet", avatar: "🐬", affinity: 'hydro', baseStats: { hp: 50, atk: 48, def: 46, spd: 66 },
    lore: "Emits sub-aquatic frequencies that soothe nearby bio-nodes.",
    learnset: [
      { lvl: 1, move: 'AquaJet' },
      { lvl: 6, move: 'VortexSpout' },
      { lvl: 11, move: 'MistGlow' },
      { lvl: 24, move: 'TidalCrash' }
    ],
    transformsTo: 21, transformLvl: 18, catalyst: "Vapor Membrane"
  },
  {
    id: 4, name: "CrystaGrub", avatar: "🐛", affinity: 'golem', baseStats: { hp: 60, atk: 45, def: 58, spd: 35 },
    lore: "Feeds on dense geo-stratas to coat itself in flawless crystalline platelets.",
    learnset: [
      { lvl: 1, move: 'StoneCrush' },
      { lvl: 8, move: 'DustStorm' },
      { lvl: 15, move: 'ClayShield' },
      { lvl: 26, move: 'MeteorTome' }
    ],
    transformsTo: 22, transformLvl: 18, catalyst: "Geo Prism"
  },
  {
    id: 5, name: "ZephyrBeak", avatar: "🐦", affinity: 'zephyr', baseStats: { hp: 45, atk: 54, def: 38, spd: 72 },
    lore: "An avian vector that glides effortlessly through micro-currents.",
    learnset: [
      { lvl: 1, move: 'ZephyrClip' },
      { lvl: 5, move: 'GaleGusta' },
      { lvl: 14, move: 'TornadoVane' },
      { lvl: 28, move: 'AetherDrift' }
    ],
    transformsTo: 23, transformLvl: 20, catalyst: "Wind Core"
  },
  {
    id: 6, name: "Starlet", avatar: "⭐", affinity: 'nova', baseStats: { hp: 42, atk: 58, def: 40, spd: 68 },
    lore: "A fragment of fallen stellar light condensed into biological sentience.",
    learnset: [
      { lvl: 1, move: 'GlitterBeam' },
      { lvl: 7, move: 'NovaPulse' },
      { lvl: 16, move: 'CosmicSweep' },
      { lvl: 30, move: 'Supernova' }
    ],
    transformsTo: 24, transformLvl: 22, catalyst: "Cosmic Spark"
  },
  {
    id: 7, name: "RuneLizard", avatar: "🦎", affinity: 'epoch', baseStats: { hp: 64, atk: 50, def: 52, spd: 48 },
    lore: "Ancient inscriptions glow along its neck, shifting local timelines by milliseconds.",
    learnset: [
      { lvl: 1, move: 'StasisLock' },
      { lvl: 8, move: 'ContinuumWarp' },
      { lvl: 18, move: 'TemporalStrike' },
      { lvl: 28, move: 'ParadoxScythe' }
    ],
    transformsTo: 25, transformLvl: 24, catalyst: "Epoch Scroll"
  },
  {
    id: 8, name: "Emberscar", avatar: "🦊", affinity: 'pyre', baseStats: { hp: 46, atk: 62, def: 40, spd: 64 },
    lore: "Its sleek fur channels excess kinetic energy into small, controlled thermal flares.",
    learnset: [
      { lvl: 1, move: 'CinderSpark' },
      { lvl: 6, move: 'PyreVolley' },
      { lvl: 15, move: 'BlazeBurst' }
    ],
    transformsTo: 26, transformLvl: 20, catalyst: "Cinder Dynamo"
  },
  {
    id: 9, name: "VineCoyote", avatar: "🐺", affinity: 'bloom', baseStats: { hp: 52, atk: 58, def: 48, spd: 55 },
    lore: "Its vine-woven coat traps surrounding moisture, which it feeds back to forest seedlings.",
    learnset: [
      { lvl: 1, move: 'IvyLash' },
      { lvl: 7, move: 'FloraRenew' },
      { lvl: 16, move: 'RootBnd' }
    ],
    transformsTo: 27, transformLvl: 21, catalyst: "Spore Nucleus"
  },
  {
    id: 10, name: "CoralCrab", avatar: "🦀", affinity: 'hydro', baseStats: { hp: 58, atk: 50, def: 60, spd: 42 },
    lore: "The coral colony on its limb acts as a defensive sponge.",
    learnset: [
      { lvl: 1, move: 'RadianSip' },
      { lvl: 6, move: 'VaporShield' },
      { lvl: 14, move: 'VortexSpout' }
    ],
    transformsTo: null
  },
  {
    id: 11, name: "Gravelpelt", avatar: "🦡", affinity: 'golem', baseStats: { hp: 65, atk: 55, def: 55, spd: 38 },
    lore: "A sturdy dweller of cavern networks, constantly tunneling with razor-claws.",
    learnset: [
      { lvl: 1, move: 'StoneCrush' },
      { lvl: 8, move: 'FossilClaw' },
      { lvl: 16, move: 'RockTumbler' }
    ],
    transformsTo: null
  },
  {
    id: 12, name: "AirDolphin", avatar: "🐳", affinity: 'zephyr', baseStats: { hp: 70, atk: 48, def: 52, spd: 58 },
    lore: "Soars through condensation clouds, converting damp mists into propulsion drafts.",
    learnset: [
      { lvl: 1, move: 'GaleGusta' },
      { lvl: 7, move: 'SonicCloud' },
      { lvl: 15, move: 'FeatherLick' }
    ],
    transformsTo: null
  },
  {
    id: 13, name: "OrbWisp", avatar: "🔮", affinity: 'nova', baseStats: { hp: 40, atk: 65, def: 35, spd: 70 },
    lore: "Highly unstable radioactive core that pulses in response to cosmic activity.",
    learnset: [
      { lvl: 1, move: 'GlitterBeam' },
      { lvl: 8, move: 'AuraBlast' },
      { lvl: 17, move: 'NovaPulse' }
    ],
    transformsTo: null
  },
  {
    id: 14, name: "RiftGazer", avatar: "👻", affinity: 'epoch', baseStats: { hp: 50, atk: 52, def: 52, spd: 62 },
    lore: "Possesses secondary eyes that observe alternative parallel realities simultaneously.",
    learnset: [
      { lvl: 1, move: 'StasisLock' },
      { lvl: 7, move: 'EpochArmor' },
      { lvl: 16, move: 'ChronosEcho' }
    ],
    transformsTo: null
  },
  { id: 15, name: "KelpPony", avatar: "🐎", affinity: 'hydro', baseStats: { hp: 54, atk: 50, def: 44, spd: 62 }, lore: "A swift shore-runner that coats its hooves in high-tension aqua shields.", learnset: [{ lvl: 1, move: 'AquaJet' }], transformsTo: null },
  { id: 16, name: "GeodeSlug", avatar: "🐌", affinity: 'golem', baseStats: { hp: 72, atk: 40, def: 64, spd: 28 }, lore: "A slow sediment eater that leaves a mineral trail usable as focus fuel.", learnset: [{ lvl: 1, move: 'StoneCrush' }], transformsTo: null },
  { id: 17, name: "FlutterGlow", avatar: "🦋", affinity: 'nova', baseStats: { hp: 44, atk: 56, def: 38, spd: 68 }, lore: "Attracted to magnetic field disturbances. Glows like hot magnesium when threatened.", learnset: [{ lvl: 1, move: 'GlitterBeam' }], transformsTo: null },
  { id: 18, name: "AeonRex", avatar: "🦖", affinity: 'epoch', baseStats: { hp: 78, atk: 60, def: 58, spd: 40 }, lore: "An apex predator that bypasses kinetic barrier fields using temporal jaws.", learnset: [{ lvl: 1, move: 'StasisLock' }, { lvl: 12, move: 'TemporalStrike' }], transformsTo: null },

  // Transformed Forms (9 Total: Indices 19 to 27)
  {
    id: 19, name: "Pyroshell-Hyperion", avatar: "🌋", affinity: 'pyre', baseStats: { hp: 85, atk: 82, def: 95, spd: 55 },
    lore: "Superheated internal pressure has infused its shell with volatile magma engines.",
    learnset: [{ lvl: 1, move: 'BlazeBurst' }, { lvl: 20, move: 'FlameVeil' }, { lvl: 35, move: 'InfernoRift' }], transformsTo: null
  },
  {
    id: 20, name: "Sporepup-Mycelium", avatar: "🦁", affinity: 'bloom', baseStats: { hp: 78, atk: 86, def: 72, spd: 80 },
    lore: "Fully bonded with the host fungus, discharging toxic spore clusters on impact.",
    learnset: [{ lvl: 1, move: 'ForestHeal' }, { lvl: 20, move: 'ThornEruption' }, { lvl: 35, move: 'RootBnd' }], transformsTo: null
  },
  {
    id: 21, name: "Finlet-Vaporwing", avatar: "🐉", affinity: 'hydro', baseStats: { hp: 75, atk: 78, def: 76, spd: 96 },
    lore: "Propels itself through dense atmosphere on wings of super-heated moisture.",
    learnset: [{ lvl: 1, move: 'VortexSpout' }, { lvl: 20, move: 'TidalCrash' }, { lvl: 35, move: 'RadianSip' }], transformsTo: null
  },
  {
    id: 22, name: "CrystaGrub-Titan", avatar: "💎", affinity: 'golem', baseStats: { hp: 90, atk: 75, def: 92, spd: 45 },
    lore: "Its diamondoid carapace blocks direct kinetic impacts with sheer crystalline weight.",
    learnset: [{ lvl: 1, move: 'ClayShield' }, { lvl: 20, move: 'MeteorTome' }, { lvl: 35, move: 'FossilClaw' }], transformsTo: null
  },
  {
    id: 23, name: "ZephyrBeak-Tornado", avatar: "🦅", affinity: 'zephyr', baseStats: { hp: 75, atk: 84, def: 68, spd: 102 },
    lore: "An absolute master of thermal corridors. Forces vacuums at will.",
    learnset: [{ lvl: 1, move: 'GaleGusta' }, { lvl: 22, move: 'AetherDrift' }, { lvl: 36, move: 'TornadoVane' }], transformsTo: null
  },
  {
    id: 24, name: "Starlet-Omega", avatar: "🌟", affinity: 'nova', baseStats: { hp: 72, atk: 92, def: 65, spd: 94 },
    lore: "A fully collapsed biological star, emitting blinding waves of lethal spectrums.",
    learnset: [{ lvl: 1, move: 'AuraBlast' }, { lvl: 24, move: 'Supernova' }, { lvl: 38, move: 'StarlightRefrain' }], transformsTo: null
  },
  {
    id: 25, name: "RuneLizard-Epochal", avatar: "🐊", affinity: 'epoch', baseStats: { hp: 94, atk: 80, def: 82, spd: 68 },
    lore: "Its aura freezes surrounding time entirely. Moves with terrifying calculations.",
    learnset: [{ lvl: 1, move: 'ContinuumWarp' }, { lvl: 25, move: 'ParadoxScythe' }, { lvl: 38, move: 'AeonHeal' }], transformsTo: null
  },
  {
    id: 26, name: "Emberscar-Chimera", avatar: "🐅", affinity: 'pyre', baseStats: { hp: 76, atk: 88, def: 65, spd: 88 },
    lore: "Its claws hum with thermal kinetic energy, leaving trails of plasma wherever it runs.",
    learnset: [{ lvl: 1, move: 'BlazeBurst' }, { lvl: 22, move: 'InfernoRift' }], transformsTo: null
  },
  {
    id: 27, name: "VineCoyote-Sylvan", avatar: "🐺", affinity: 'bloom', baseStats: { hp: 82, atk: 84, def: 74, spd: 82 },
    lore: "The deep wooden limbs store memory clusters of Aethera before the great fracturing.",
    learnset: [{ lvl: 1, move: 'RootBnd' }, { lvl: 22, move: 'ThornEruption' }], transformsTo: null
  }
];

// ── const: INVENTORY_DB ──
// GAME INVENTORY DATABASE ITEMS
const INVENTORY_DB = {
  // Capture items
  'harm_1': { name: "Harmonizer Core", dName: "Harmonist Core", type: 'capture', rate: 1.0, cost: 100, desc: "Standard resonance tuner. 1.0x grab rate." },
  'harm_2': { name: "Aetherial Harness", dName: "Aether Harness", type: 'capture', rate: 1.5, cost: 250, desc: "Reinforced dimensional frame. 1.5x grab rate." },
  'harm_3': { name: "Cosmic Lattice", dName: "Cosmic Lattice", type: 'capture', rate: 2.2, cost: 600, desc: "Hyper-dimensional mesh. 2.2x grab rate." },

  // Restoratives
  'heal_1': { name: "Re-Flux Tonic", dName: "Re-Flux Tonic", type: 'heal', hp: 30, cost: 50, desc: "Repairs superficial matrix damage. Restores 30 HP." },
  'heal_2': { name: "Essence Restorer", dName: "Essence Restorer", type: 'heal', hp: 100, cost: 150, desc: "Synthesizes vital matter. Restores 100 HP." },
  'heal_full': { name: "Cosmic Elixir", dName: "Cosmic Elixir", type: 'heal', hp: 999, cost: 400, desc: "Repairs entire matrix. Fully heals a creature." },

  // Catalysts for transformations
  'cat_pyre': { name: "Cinder Dynamo", dName: "Cinder Dynamo", type: 'catalyst', cost: 300, desc: "Required to catalyze Pyre evolutions above Level 18." },
  'cat_bloom': { name: "Spore Nucleus", dName: "Spore Nucleus", type: 'catalyst', cost: 300, desc: "Required to catalyze Bloom evolutions above Level 18." },
  'cat_hydro': { name: "Vapor Membrane", dName: "Vapor Membrane", type: 'catalyst', cost: 300, desc: "Required to catalyze Hydro evolutions above Level 18." },
  'cat_golem': { name: "Geo Prism", dName: "Geo Prism", type: 'catalyst', cost: 300, desc: "Required to catalyze Golem evolutions above Level 18." },
  'cat_zephyr': { name: "Wind Core", dName: "Wind Core", type: 'catalyst', cost: 300, desc: "Required to catalyze Zephyr evolutions above Level 20." },
  'cat_nova': { name: "Cosmic Spark", dName: "Cosmic Spark", type: 'catalyst', cost: 350, desc: "Required to catalyze Nova evolutions above Level 22." },
  'cat_epoch': { name: "Epoch Scroll", dName: "Epoch Scroll", type: 'catalyst', cost: 350, desc: "Required to catalyze Epoch evolutions above Level 24." }
};

// ── const: MAP_COLS ──
// OVERWORLD MAP TILES AND PLAYER STATE ARCHITECTURE
const MAP_COLS = 20;

// ── const: MAP_ROWS ──
const MAP_ROWS = 12;

// ── const: TILE_SIZE ──
const TILE_SIZE = 40;

// ── const: BASE_MAP_TEMPLATE ──
// Grid tile types:
// 0 = Emptiness (Path)
// 1 = High resonance grass (Wild encounters)
// 2 = Monolith/Wall
// 3 = Rest Sanctuary (Shrine)
// 4 = Zone anchor gate (Boss/Guardian)
// 5 = Zone transit gate (Travel to next zone!)

const BASE_MAP_TEMPLATE = [
  [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2],
  [2,1,1,1,0,0,0,0,1,1,1,1,0,0,0,2,2,0,2,2],
  [2,1,0,0,0,2,2,0,0,1,1,0,0,2,0,0,0,0,0,2],
  [2,1,0,2,2,2,2,2,0,0,0,0,2,2,2,2,1,1,0,2],
  [2,0,0,0,0,0,0,2,2,0,2,2,2,0,0,2,1,1,0,2],
  [2,0,2,2,2,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2],
  [2,0,2,3,2,0,2,2,2,2,4,2,2,2,0,2,0,2,0,2],
  [2,0,2,0,2,0,2,1,1,1,1,1,1,2,0,2,0,2,0,2],
  [2,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,2,5,2],
  [2,1,1,0,2,2,2,2,2,2,2,2,2,2,2,1,1,2,0,2],
  [2,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,2],
  [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]
];

// ── class: OverworldEngine ──
class OverworldEngine {
  constructor(canvasId) {
    this.canvas = document.getElementById(canvasId);
    this.ctx = this.canvas.getContext('2d');
    this.particles = [];
  }

  // Draw region based on active custom zone gradients & particles
  render(playerPos, currentZone, anchorDefeated) {
    const zoneData = ZONE_DATABASES[currentZone];
    
    // Gradient Render
    const grad = this.ctx.createRadialGradient(400, 240, 50, 400, 240, 450);
    grad.addColorStop(0, zoneData.gradient[1]);
    grad.addColorStop(1, zoneData.gradient[0]);
    this.ctx.fillStyle = grad;
    this.ctx.fillRect(0, 0, 800, 480);

    // Draw ambient particles
    this.updateParticles(zoneData.particleColor);

    // Draw the tilemap
    for (let r = 0; r < MAP_ROWS; r++) {
      for (let c = 0; c < MAP_COLS; c++) {
        const tile = BASE_MAP_TEMPLATE[r][c];
        const x = c * TILE_SIZE;
        const y = r * TILE_SIZE;

        if (tile === 2) {
          // Monolith wall
          this.ctx.fillStyle = "rgba(255, 255, 255, 0.05)";
          this.ctx.fillRect(x + 2, y + 2, TILE_SIZE - 4, TILE_SIZE - 4);
          this.ctx.strokeStyle = "rgba(255, 255, 255, 0.15)";
          this.ctx.strokeRect(x + 2, y + 2, TILE_SIZE - 4, TILE_SIZE - 4);
        } else if (tile === 1) {
          // High resonance grass
          this.ctx.fillStyle = "rgba(0, 243, 255, 0.08)";
          this.ctx.fillRect(x + 4, y + 4, TILE_SIZE - 8, TILE_SIZE - 8);
          this.ctx.fillStyle = "rgba(0, 243, 255, 0.15)";
          this.ctx.fillRect(x + 10, y + 10, TILE_SIZE - 20, TILE_SIZE - 20);
        } else if (tile === 3) {
          // Shrine
          this.ctx.fillStyle = "#ec4899";
          this.ctx.beginPath();
          this.ctx.arc(x + 20, y + 20, 10, 0, Math.PI * 2);
          this.ctx.fill();
          this.ctx.strokeStyle = "#fff";
          this.ctx.lineWidth = 2;
          this.ctx.stroke();
          this.ctx.fillStyle = "#fff";
          this.ctx.font = "12px sans-serif";
          this.ctx.fillText("⛩️", x + 10, y + 24);
        } else if (tile === 4) {
          // Guardian Node
          this.ctx.fillStyle = anchorDefeated ? "rgba(16, 185, 129, 0.2)" : "rgba(239, 68, 68, 0.2)";
          this.ctx.fillRect(x + 1, y + 1, TILE_SIZE - 2, TILE_SIZE - 2);
          this.ctx.strokeStyle = anchorDefeated ? "#10b981" : "#ef4444";
          this.ctx.strokeRect(x + 1, y + 1, TILE_SIZE - 2, TILE_SIZE - 2);
          this.ctx.fillStyle = "#fff";
          this.ctx.font = "14px sans-serif";
          this.ctx.fillText(anchorDefeated ? "✅" : "⚠️", x + 12, y + 26);
        } else if (tile === 5) {
          // Zone travel gate
          this.ctx.fillStyle = "rgba(255, 190, 59, 0.1)";
          this.ctx.fillRect(x + 1, y + 1, TILE_SIZE - 2, TILE_SIZE - 2);
          this.ctx.strokeStyle = "#ffbe3b";
          this.ctx.strokeRect(x + 2, y + 2, TILE_SIZE - 4, TILE_SIZE - 4);
          this.ctx.fillStyle = "#ffbe3b";
          this.ctx.font = "12px Arial";
          this.ctx.fillText("🌀", x + 11, y + 25);
        }
      }
    }

    // Render Player
    const px = playerPos.x * TILE_SIZE;
    const py = playerPos.y * TILE_SIZE;
    this.ctx.fillStyle = "#00f3ff";
    this.ctx.shadowColor = "#00f3ff";
    this.ctx.shadowBlur = 10;
    this.ctx.beginPath();
    this.ctx.arc(px + 20, py + 20, 14, 0, Math.PI * 2);
    this.ctx.fill();
    this.ctx.shadowBlur = 0; // Reset

    // Small interior dot for player orientation
    this.ctx.fillStyle = "#ffffff";
    this.ctx.beginPath();
    this.ctx.arc(px + 20, py + 20, 5, 0, Math.PI * 2);
    this.ctx.fill();
  }

  updateParticles(color) {
    if (this.particles.length < 35) {
      this.particles.push({
        x: Math.random() * 800,
        y: Math.random() * 480,
        r: Math.random() * 2 + 1,
        vy: Math.random() * -0.5 - 0.2,
        alpha: Math.random() * 0.4 + 0.2
      });
    }

    this.particles.forEach((p, idx) => {
      p.y += p.vy;
      if (p.y < 0) {
        p.y = 480;
        p.x = Math.random() * 800;
      }
      this.ctx.fillStyle = color;
      this.ctx.globalAlpha = p.alpha;
      this.ctx.beginPath();
      this.ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
      this.ctx.fill();
    });
    this.ctx.globalAlpha = 1.0;
  }
}

// ── class: GameState ──
// DETAILED STATE MANAGEMENT SYSTEM (AUTO & MANUAL STORAGE INDEX SAVING)
class GameState {
  constructor() {
    this.playerPos = { x: 5, y: 5 };
    this.currentZone = 0;
    this.fluxWallet = 150; // Starting Currency
    this.team = []; // Up to 6 active creatures
    this.inventory = {
      'harm_1': 4,
      'harm_2': 1,
      'heal_1': 3,
      'cat_pyre': 1
    };
    this.unlockedEncylopedia = {}; // ID indexed booleans
    this.anchorsDefeated = [false, false, false, false, false, false]; // Zone indexed
    this.anchorsTargetCount = 4; // Final Boss victory requires 4 zone anchors beaten
    this.tutorialCompleted = false;
    this.finalGameCompleted = false;
  }

  addCreatureToTeam(modelId, level = 5) {
    if (this.team.length >= 6) return false;
    const dbData = CREATURES_DB.find(c => c.id === modelId);
    if (!dbData) return false;

    const stats = this.calcStatsForLvl(dbData.baseStats, level);
    const newCreature = {
      id: Date.now() + Math.random(),
      modelId: modelId,
      name: dbData.name,
      avatar: dbData.avatar,
      affinity: dbData.affinity,
      level: level,
      xp: 0,
      maxXp: level * 80,
      hp: stats.hp,
      maxHp: stats.hp,
      atk: stats.atk,
      def: stats.def,
      spd: stats.spd,
      status: null, // 'burn', 'frost', 'shock', 'barrier', 'regen'
      activeAbilities: []
    };

    // Populate active moves from learnset
    dbData.learnset.forEach(m => {
      if (level >= m.lvl && newCreature.activeAbilities.length < 4) {
        newCreature.activeAbilities.push({
          moveKey: m.move,
          pp: COMBAT_ABILITIES[m.move].cost * 2,
          maxPp: COMBAT_ABILITIES[m.move].cost * 2
        });
      }
    });

    this.team.push(newCreature);
    this.unlockedEncylopedia[modelId] = true;
    return true;
  }

  calcStatsForLvl(base, lvl) {
    return {
      hp: Math.floor(base.hp + (base.hp * 0.11 * lvl)),
      atk: Math.floor(base.atk + (base.atk * 0.09 * lvl)),
      def: Math.floor(base.def + (base.def * 0.08 * lvl)),
      spd: Math.floor(base.spd + (base.spd * 0.09 * lvl))
    };
  }

  levelUpCreature(creature) {
    if (creature.level >= 50) return;
    creature.level++;
    creature.xp = 0;
    creature.maxXp = creature.level * 80;
    const dbData = CREATURES_DB.find(c => c.id === creature.modelId);
    const stats = this.calcStatsForLvl(dbData.baseStats, creature.level);
    
    // Scale current HP proportionally
    const ratio = creature.hp / creature.maxHp;
    creature.maxHp = stats.hp;
    creature.hp = Math.max(1, Math.floor(stats.hp * ratio));
    creature.atk = stats.atk;
    creature.def = stats.def;
    creature.spd = stats.spd;

    // Check newly unlocked learnset capabilities
    dbData.learnset.forEach(m => {
      if (creature.level === m.lvl) {
        // Replace or Append logic
        if (creature.activeAbilities.length < 4) {
          creature.activeAbilities.push({
            moveKey: m.move,
            pp: COMBAT_ABILITIES[m.move].cost * 2,
            maxPp: COMBAT_ABILITIES[m.move].cost * 2
          });
        } else {
          // Replace first slot
          creature.activeAbilities[0] = {
            moveKey: m.move,
            pp: COMBAT_ABILITIES[m.move].cost * 2,
            maxPp: COMBAT_ABILITIES[m.move].cost * 2
          };
        }
      }
    });
  }

  triggerCatalystTransformation(creature, itemKey) {
    const dbData = CREATURES_DB.find(c => c.id === creature.modelId);
    if (!dbData || !dbData.transformsTo) return { success: false, msg: "This creature cannot be catalyzed further." };
    if (creature.level < dbData.transformLvl) return { success: false, msg: `Requires cosmic frequency level of ${dbData.transformLvl} or higher.` };
    const requiredCatalyst = dbData.catalyst;
    const itemData = INVENTORY_DB[itemKey];
    if (!itemData || itemData.name !== requiredCatalyst) {
      return { success: false, msg: `Requires transformation catalyst: ${requiredCatalyst}` };
    }

    // Deduct catalyst
    this.inventory[itemKey]--;
    if (this.inventory[itemKey] <= 0) delete this.inventory[itemKey];

    // Evolve creature in-place!
    const targetFormId = dbData.transformsTo;
    const evolvedDb = CREATURES_DB.find(c => c.id === targetFormId);
    creature.modelId = targetFormId;
    creature.name = evolvedDb.name;
    creature.avatar = evolvedDb.avatar;

    // Recalculate stats
    const stats = this.calcStatsForLvl(evolvedDb.baseStats, creature.level);
    creature.maxHp = stats.hp;
    creature.hp = stats.hp;
    creature.atk = stats.atk;
    creature.def = stats.def;
    creature.spd = stats.spd;

    this.unlockedEncylopedia[targetFormId] = true;
    return { success: true, msg: `${creature.name} has transformed successfully with ${itemData.name}!` };
  }

  serialize() {
    return JSON.stringify({
      playerPos: this.playerPos,
      currentZone: this.currentZone,
      fluxWallet: this.fluxWallet,
      team: this.team,
      inventory: this.inventory,
      unlockedEncylopedia: this.unlockedEncylopedia,
      anchorsDefeated: this.anchorsDefeated,
      tutorialCompleted: this.tutorialCompleted,
      finalGameCompleted: this.finalGameCompleted
    });
  }

  deserialize(dataString) {
    try {
      const parsed = JSON.parse(dataString);
      this.playerPos = parsed.playerPos;
      this.currentZone = parsed.currentZone;
      this.fluxWallet = parsed.fluxWallet;
      this.team = parsed.team;
      this.inventory = parsed.inventory;
      this.unlockedEncylopedia = parsed.unlockedEncylopedia;
      this.anchorsDefeated = parsed.anchorsDefeated;
      this.tutorialCompleted = parsed.tutorialCompleted;
      this.finalGameCompleted = parsed.finalGameCompleted;
      return true;
    } catch (e) {
      console.error("Error restoring stream indexes. Resetting.", e);
      return false;
    }
  }
}

// ── const: CurrentState ──
const CurrentState = new GameState();

// ── class: CombatEngine ──
// BATTLE SCENE COORDINATOR & COMBAT RULES
class CombatEngine {
  constructor() {
    this.isActive = false;
    this.allies = [];
    this.opponents = [];
    this.activeAllyIdx = 0;
    this.currentLog = "";
    this.combatType = "wild"; // 'wild' or 'guardian'
    this.selectedAction = null; // Stored target selection phase data
    this.resolveTurnCallback = null;
    this.floatingTexts = []; // [{x, y, text, color, life}]
  }

  initWildBattle(zoneIdx, playerTeam) {
    this.isActive = true;
    this.combatType = "wild";
    // Clone party to isolate battle temporary HP changes and status
    this.allies = playerTeam;
    this.activeAllyIdx = this.getFirstAbleAllyIdx();

    // Spawn 1 to 3 wild opponents scaled close to player average level
    const avgLvl = Math.max(3, Math.round(playerTeam.reduce((acc, c) => acc + c.level, 0) / playerTeam.length));
    const randomCount = Math.random() < 0.65 ? 1 : (Math.random() < 0.85 ? 2 : 3);
    this.opponents = [];

    const zoneAvail = this.getZoneSpawns(zoneIdx);

    for (let i = 0; i < randomCount; i++) {
      const template = zoneAvail[Math.floor(Math.random() * zoneAvail.length)];
      const spawnedLvl = Math.max(1, avgLvl + Math.floor(Math.random() * 3) - 1);
      const enemyStats = CurrentState.calcStatsForLvl(template.baseStats, spawnedLvl);
      
      const wildEnemy = {
        modelId: template.id,
        name: "Wild " + template.name,
        avatar: template.avatar,
        affinity: template.affinity,
        level: spawnedLvl,
        hp: enemyStats.hp,
        maxHp: enemyStats.hp,
        atk: enemyStats.atk,
        def: enemyStats.def,
        spd: enemyStats.spd,
        status: null,
        activeAbilities: template.learnset.map(m => m.move)
      };
      this.opponents.push(wildEnemy);
      CurrentState.unlockedEncylopedia[template.id] = true;
    }

    this.currentLog = `A primal presence emerges! Team size: ${this.opponents.length}`;
  }

  initGuardianBattle(zoneIdx, playerTeam) {
    this.isActive = true;
    this.combatType = "guardian";
    this.allies = playerTeam;
    this.activeAllyIdx = this.getFirstAbleAllyIdx();

    const zone = ZONE_DATABASES[zoneIdx];
    // Scale guardian to be a true challenge!
    const avgLvl = Math.max(8, Math.round(playerTeam.reduce((acc, c) => acc + c.level, 0) / playerTeam.length)) + 5;
    
    // Guardian behaves like a massive, highly optimized high-tier Golem/Epoch/Nova boss
    const bossModelId = 18; // AeonRex template
    const bossStats = CurrentState.calcStatsForLvl({ hp: 120, atk: 75, def: 85, spd: 45 }, avgLvl);

    const guardian = {
      modelId: bossModelId,
      name: zone.guardianName,
      avatar: zone.guardianAvatar,
      affinity: 'epoch',
      level: avgLvl,
      hp: bossStats.hp,
      maxHp: bossStats.hp,
      atk: bossStats.atk,
      def: bossStats.def,
      spd: bossStats.spd,
      status: null,
      activeAbilities: ['StasisLock', 'TemporalStrike', 'EpochArmor', 'CosmicSweep']
    };

    this.opponents = [guardian];
    this.currentLog = `GUARDIAN ALERT: ${zone.guardianName} blocks the spatial threshold!`;
  }

  getZoneSpawns(zoneIdx) {
    // Return creatures associated with this zone
    if (zoneIdx === 0) return CREATURES_DB.filter(c => c.affinity === 'pyre' || c.affinity === 'bloom');
    if (zoneIdx === 1) return CREATURES_DB.filter(c => c.affinity === 'hydro' || c.affinity === 'zephyr');
    if (zoneIdx === 2) return CREATURES_DB.filter(c => c.affinity === 'pyre' || c.affinity === 'golem');
    if (zoneIdx === 3) return CREATURES_DB.filter(c => c.affinity === 'golem' || c.affinity === 'epoch');
    if (zoneIdx === 4) return CREATURES_DB.filter(c => c.affinity === 'zephyr' || c.affinity === 'nova');
    return CREATURES_DB.filter(c => c.affinity === 'nova' || c.affinity === 'epoch');
  }

  getFirstAbleAllyIdx() {
    for (let i = 0; i < this.allies.length; i++) {
      if (this.allies[i].hp > 0) return i;
    }
    return 0;
  }

  getActiveAlly() {
    return this.allies[this.activeAllyIdx];
  }

  // CORE DAMAGE FORMULA IMPLEMENTATION
  // (0.4 × Level × ATK / DEF × Multiplier) + small random, floored sensibly. Critical hits apply. STAB = +25%
  calculateCombatDamage(attacker, defender, ability) {
    const isCrit = Math.random() < 0.12;
    const mult = getDamageMultiplier(ability.affinity, defender.affinity);
    
    // Base math
    let damage = (0.4 * attacker.level * (attacker.atk / Math.max(1, defender.def)) * mult);

    // Apply STAB (Same Type Attack Bonus)
    if (attacker.affinity === ability.affinity) {
      damage *= 1.25;
    }

    if (isCrit) {
      damage *= 1.5;
    }

    // Variance & absolute floor
    damage += Math.random() * 4;
    damage = Math.floor(damage);
    if (damage <= 0) damage = 1;

    return { amount: damage, crit: isCrit, scale: mult };
  }

  executeAbilityTurn(abilityKey, targetIdx) {
    const ally = this.getActiveAlly();
    const target = this.opponents[targetIdx];
    const ability = COMBAT_ABILITIES[abilityKey];

    if (!ally || !target || !ability) return;

    // Expend PP
    const abRef = ally.activeAbilities.find(a => a.moveKey === abilityKey);
    if (abRef) abRef.pp = Math.max(0, abRef.pp - 1);

    // Speed comparison sequence logic (SPD Order decides turn order)
    const allyFirst = ally.spd >= target.spd;
    let turnLog = "";

    if (allyFirst) {
      turnLog += this.processAttackStrike(ally, target, ability, "ally");
      if (target.hp > 0) {
        turnLog += "<br>" + this.processOpponentReply(target, ally);
      }
    } else {
      turnLog += this.processOpponentReply(target, ally);
      if (ally.hp > 0) {
        turnLog += "<br>" + this.processAttackStrike(ally, target, ability, "ally");
      }
    }

    // Process turn-end status tick (Burn damage, Regen effects)
    turnLog += this.processGlobalStatusTicks();

    this.currentLog = turnLog;
    this.triggerRenderCallback();
  }

  processAttackStrike(attacker, defender, ability, sourceSide) {
    if (attacker.hp <= 0) return "";

    // Status: Shock skips attack entirely
    if (attacker.status === 'shock' && Math.random() < 0.35) {
      return `❌ ${attacker.name}'s quantum state is shocked! Action bypassed.`;
    }

    // Check Accuracy
    if (Math.random() > ability.accuracy) {
      return `💨 ${attacker.name} used ${ability.name}... and missed!`;
    }

    // Check Barrier
    if (defender.status === 'barrier') {
      defender.status = null;
      return `🛡️ ${attacker.name}'s ${ability.name} was entirely deflected by ${defender.name}'s Barrier!`;
    }

    const res = this.calculateCombatDamage(attacker, defender, ability);
    defender.hp = Math.max(0, defender.hp - res.amount);

    let logs = `💥 ${attacker.name} used <b>${ability.name}</b> on ${defender.name} dealing <b>${res.amount}</b> matrix damage.`;
    
    if (res.crit) {
      logs += ` <b style="color:var(--accent-gold);">[CRITICAL COUPLING!]</b>`;
      AudioController.triggerCritNoise();
    }
    if (res.scale > 1.1) {
      logs += ` <b style="color:var(--accent-cyan);">[Resonance Cascade!]</b>`;
      AudioController.triggerSuperEffectiveNoise();
    } else {
      AudioController.triggerBeep(330, 'sine', 0.1);
    }

    // Apply secondary status effect chances
    if (ability.effect && ability.effect !== 'none' && defender.hp > 0) {
      if (Math.random() < 0.50) {
        if (['burn', 'frost', 'shock', 'barrier', 'regen'].includes(ability.effect)) {
          if (ability.effect === 'barrier' || ability.effect === 'regen') {
            attacker.status = ability.effect;
            logs += ` <b style="color:var(--success-color);">[Self-infused ${ability.effect.toUpperCase()}]</b>`;
          } else {
            defender.status = ability.effect;
            logs += ` <b>[Target afflicted with ${ability.effect.toUpperCase()}]</b>`;
          }
        } else if (ability.effect === 'heal') {
          const healPct = Math.floor(attacker.maxHp * 0.35);
          attacker.hp = Math.min(attacker.maxHp, attacker.hp + healPct);
          logs += ` <b>[Recovered ${healPct} HP]</b>`;
        } else if (ability.effect === 'atkBoost') {
          attacker.atk = Math.floor(attacker.atk * 1.25);
          logs += ` <b>[ATK amplified +25%]</b>`;
        } else if (ability.effect === 'spdBoost') {
          attacker.spd = Math.floor(attacker.spd * 1.25);
          logs += ` <b>[SPD amplified +25%]</b>`;
        }
      }
    }

    return logs;
  }

  processOpponentReply(opponent, ally) {
    if (opponent.hp <= 0) return "";
    
    // Dynamic mini AI chooses random available move
    const moves = opponent.activeAbilities;
    const moveKey = moves[Math.floor(Math.random() * moves.length)];
    const ability = COMBAT_ABILITIES[moveKey] || COMBAT_ABILITIES.SearingSpike;

    return this.processAttackStrike(opponent, ally, ability, "opponent");
  }

  processGlobalStatusTicks() {
    let tickLog = "";
    // Process status for active ally
    const ally = this.getActiveAlly();
    if (ally && ally.hp > 0) {
      if (ally.status === 'burn') {
        const burnDmg = Math.floor(ally.maxHp * 0.1);
        ally.hp = Math.max(1, ally.hp - burnDmg);
        tickLog += `<br>🔥 ${ally.name} taking burn decay (-${burnDmg} HP).`;
      } else if (ally.status === 'regen') {
        const regenVal = Math.floor(ally.maxHp * 0.12);
        ally.hp = Math.min(ally.maxHp, ally.hp + regenVal);
        tickLog += `<br>🌿 ${ally.name} revitalized by Regen capsule (+${regenVal} HP).`;
      }
    }

    // Process status for active opponent targets
    this.opponents.forEach(enemy => {
      if (enemy.hp > 0) {
        if (enemy.status === 'burn') {
          const decay = Math.floor(enemy.maxHp * 0.1);
          enemy.hp = Math.max(0, enemy.hp - decay);
          tickLog += `<br>🔥 ${enemy.name} decays from Burn flux (-${decay} HP).`;
        } else if (enemy.status === 'regen') {
          const boost = Math.floor(enemy.maxHp * 0.12);
          enemy.hp = Math.min(enemy.maxHp, enemy.hp + boost);
          tickLog += `<br>🌿 ${enemy.name} synthesized HP (+${boost} HP).`;
        }
      }
    });

    return tickLog;
  }

  switchActiveAlly(targetIdx) {
    if (this.allies[targetIdx].hp <= 0) return;
    const oldName = this.getActiveAlly().name;
    this.activeAllyIdx = targetIdx;
    const newAlly = this.getActiveAlly();

    this.currentLog = `🔄 Recalled ${oldName}. Synchronized Active Node to <b>${newAlly.name}</b>!`;
    
    // Opponent gets free counter response instantly!
    const activeEnemy = this.opponents.find(e => e.hp > 0);
    if (activeEnemy) {
      this.currentLog += "<br>" + this.processOpponentReply(activeEnemy, newAlly);
    }

    this.triggerRenderCallback();
  }

  setUpdateCallback(cb) {
    this.resolveTurnCallback = cb;
  }

  triggerRenderCallback() {
    if (this.resolveTurnCallback) this.resolveTurnCallback();
  }
}

// ── const: ActiveCombat ──
const ActiveCombat = new CombatEngine();

// ── class: HarmonizeCoreEngine ──
// RHYTHM INTEGRATION INTERFACE (MINI-GAME IMPLEMENTATION)
class HarmonizeCoreEngine {
  constructor() {
    this.active = false;
    this.scale = 1.0;
    this.increasing = true;
    this.successThreshold = 0.85; // Percent match target range
    this.pulseInterval = null;
    this.renderNode = null;
    this.displayPercentNode = null;
    this.targetCreatureIdx = 0;
    this.captureDeviceKey = 'harm_1';
    this.onCompleteCallback = null;
  }

  start(itemKey, oppIdx, onComplete) {
    this.active = true;
    this.scale = 0.1;
    this.increasing = true;
    this.captureDeviceKey = itemKey;
    this.targetCreatureIdx = oppIdx;
    this.onCompleteCallback = onComplete;

    this.renderNode = document.getElementById("resonant-pulse");
    this.displayPercentNode = document.getElementById("pulse-percent-disp");

    document.getElementById("harmonize-modal").classList.remove("hidden");

    if (this.pulseInterval) clearInterval(this.pulseInterval);
    this.pulseInterval = setInterval(() => {
      this.updatePulse();
    }, 15);
  }

  updatePulse() {
    if (!this.active) return;
    if (this.increasing) {
      this.scale += 0.015;
      if (this.scale >= 1.5) {
        this.scale = 1.5;
        this.increasing = false;
      }
    } else {
      this.scale -= 0.015;
      if (this.scale <= 0.1) {
        this.scale = 0.1;
        this.increasing = true;
      }
    }

    // Convert scale factors layout representation
    // Target is exactly dashed ring around scale = 1.0
    const accuracy = Math.max(0, 100 - Math.abs(1.0 - this.scale) * 100);
    this.displayPercentNode.textContent = `${Math.floor(accuracy)}%`;

    if (this.renderNode) {
      this.renderNode.style.width = `${this.scale * 140}px`;
      this.renderNode.style.height = `${this.scale * 140}px`;
    }
  }

  triggerTap() {
    if (!this.active) return;
    clearInterval(this.pulseInterval);
    this.active = false;
    document.getElementById("harmonize-modal").classList.add("hidden");

    const accuracy = Math.max(0, 1.0 - Math.abs(1.0 - this.scale));
    
    // Evaluate if grab targets sync sequence succeeds
    if (this.onCompleteCallback) {
      this.onCompleteCallback(accuracy);
    }
  }
}

// ── const: ActiveHarmonizer ──
const ActiveHarmonizer = new HarmonizeCoreEngine();

// ── const: UIRenderer ──
// STYLISH RENDERERS AND DATA SYNCHRONIZERS
const UIRenderer = {
  // Overworld UI Refresh
  updateHUD() {
    const zoneName = document.getElementById("hud-zone-name");
    const zone = ZONE_DATABASES[CurrentState.currentZone];
    if (zoneName && zone) {
      zoneName.textContent = `${zone.idx + 1}. ${zone.name}`;
      zoneName.style.color = zone.particleColor;
    }

    const anchorsCount = document.getElementById("anchors-count");
    if (anchorsCount) {
      const activeUnchained = CurrentState.anchorsDefeated.filter(x => x).length;
      anchorsCount.textContent = `Anchors Unchained: ${activeUnchained}/4`;
      if (activeUnchained >= 4) {
        anchorsCount.style.borderColor = "var(--success-color)";
        anchorsCount.style.color = "var(--success-color)";
      }
    }
  },

  // Refresh entire Pause Codex and state details
  refreshCodexMenu() {
    const listContainer = document.getElementById("menu-team-list");
    if (!listContainer) return;
    listContainer.innerHTML = "";

    CurrentState.team.forEach((ally, idx) => {
      const card = document.createElement("div");
      card.className = "team-card";
      card.innerHTML = `
        <div class="team-card-icon">${ally.avatar}</div>
        <div class="team-card-info">
          <div class="team-card-name">${ally.name}</div>
          <div class="team-card-level">Level ${ally.level}</div>
        </div>
      `;
      card.onclick = () => UIRenderer.showTeamMemberDetail(idx);
      listContainer.appendChild(card);
    });

    this.refreshInventoryTab();
    this.refreshGlossaryGrid();
  },

  showTeamMemberDetail(idx) {
    const ally = CurrentState.team[idx];
    const pane = document.getElementById("menu-team-detail");
    if (!pane || !ally) return;

    // Check transformation catalyst availability
    const origDb = CREATURES_DB.find(c => c.id === ally.modelId);
    let transformButtonHtml = "";

    if (origDb && origDb.transformsTo) {
      const hasReqItem = Object.keys(CurrentState.inventory).find(k => INVENTORY_DB[k].name === origDb.catalyst);
      transformButtonHtml = `
        <div style="border-top: 1px solid var(--border-glass); padding-top: 15px; margin-top: 5px;">
          <h4 style="font-size:12px; margin-bottom: 6px; color: var(--accent-gold);">MUTATION PATHWAY DETECTED</h4>
          <p style="font-size:11px; color: var(--text-muted); margin-bottom: 10px;">Requires leveling depth of ${origDb.transformLvl} + Catalyst [${origDb.catalyst}].</p>
          <button class="btn btn-sm btn-primary" onclick="triggerPartyEvolution(${idx}, '${hasReqItem || ''}')" ${(!hasReqItem || ally.level < origDb.transformLvl) ? 'disabled' : ''}>
            💥 Inject ${origDb.catalyst} Trigger
          </button>
        </div>
      `;
    }

    pane.innerHTML = `
      <div class="detail-header">
        <div class="detail-avatar-large">${ally.avatar}</div>
        <div class="detail-meta-text">
          <div class="detail-meta-title">${ally.name}</div>
          <div class="detail-pills">
            <span class="detail-pill-type" style="background:${TYPE_COLORS[ally.affinity]}">${ally.affinity}</span>
            <span class="detail-pill-type" style="color:var(--accent-gold)">Level ${ally.level}</span>
          </div>
        </div>
      </div>

      <div class="detail-grid-stats">
        <div class="detail-stat-box">
          <div class="detail-stat-lbl">HP Matrix</div>
          <div class="detail-stat-val">${ally.hp}/${ally.maxHp}</div>
        </div>
        <div class="detail-stat-box">
          <div class="detail-stat-lbl">Attack</div>
          <div class="detail-stat-val">${ally.atk}</div>
        </div>
        <div class="detail-stat-box">
          <div class="detail-stat-lbl">Defense</div>
          <div class="detail-stat-val">${ally.def}</div>
        </div>
        <div class="detail-stat-box">
          <div class="detail-stat-lbl">Speed</div>
          <div class="detail-stat-val">${ally.spd}</div>
        </div>
      </div>

      <div class="detail-lore-blurb">
        ${origDb ? origDb.lore : 'Deep quantum energy flows perfectly within its physical boundaries.'}
      </div>

      <div class="detail-abilities">
        <div class="detail-abilities-title">COSMIC ACTIONS EQUIP LIST</div>
        <div class="detail-ab-grid">
          ${ally.activeAbilities.map(ab => {
            const inf = COMBAT_ABILITIES[ab.moveKey];
            return `
              <div class="detail-ab-card">
                <b>⚡ ${inf.name}</b> (${inf.affinity})
                <div style="font-size:10px; color:var(--text-muted); margin-top: 3px;">Energy: ${ab.pp}/${ab.maxPp} | Pwr: ${inf.power}</div>
              </div>
            `;
          }).join('')}
        </div>
      </div>

      ${transformButtonHtml}
    `;
  },

  refreshInventoryTab() {
    const list = document.getElementById("menu-inv-list");
    if (!list) return;
    list.innerHTML = "";

    Object.keys(CurrentState.inventory).forEach(itemKey => {
      const qty = CurrentState.inventory[itemKey];
      const detail = INVENTORY_DB[itemKey];
      if (!detail || qty <= 0) return;

      const card = document.createElement("div");
      card.className = "inv-card";
      card.innerHTML = `
        <div class="inv-label-group">
          <span class="inv-icon">📦</span>
          <span class="inv-name">${detail.dName}</span>
        </div>
        <span class="inv-qty">x${qty}</span>
      `;
      card.onclick = () => {
        const descNode = document.getElementById("menu-inv-desc");
        let actionBtn = "";
        
        // Allow out of battle healing from inventory tab
        if (detail.type === 'heal') {
          actionBtn = `
            <button class="btn btn-sm btn-primary" onclick="triggerOutOfBattleHeal('${itemKey}')" style="margin-top:10px; width:100%;">
              Trigger restore on team party
            </button>
          `;
        }

        descNode.innerHTML = `
          <div class="inv-desc-head">${detail.name}</div>
          <div class="inv-desc-text">${detail.desc}</div>
          ${actionBtn}
        `;
      };
      list.appendChild(card);
    });
  },

  refreshGlossaryGrid() {
    const grid = document.getElementById("menu-encyclopedia-grid");
    if (!grid) return;
    grid.innerHTML = "";

    // 27 Total indexed database entries
    CREATURES_DB.forEach(c => {
      const isUnlocked = CurrentState.unlockedEncylopedia[c.id];
      const card = document.createElement("div");
      card.className = `gloss-card ${isUnlocked ? 'unlocked' : 'locked'}`;
      card.innerHTML = `
        <span class="gloss-idx">#${String(c.id).padStart(2, '0')}</span>
        <span class="gloss-avatar">${isUnlocked ? c.avatar : '❓'}</span>
        <span class="gloss-name">${isUnlocked ? c.name : 'Unknown Alignment'}</span>
      `;
      grid.appendChild(card);
    });
  },

  refreshCombatScreen() {
    const displayLog = document.getElementById("combat-log");
    if (displayLog) {
      displayLog.innerHTML = ActiveCombat.currentLog;
    }

    // Load active ally details card
    const activeAllyPod = document.getElementById("active-ally-pod");
    const ally = ActiveCombat.getActiveAlly();
    if (activeAllyPod && ally) {
      const activeAbilitiesStr = ally.activeAbilities.map(ab => ab.moveKey).join(",");
      const hpPct = Math.max(0, (ally.hp / ally.maxHp) * 100);
      let hpCls = "";
      if (hpPct < 25) hpCls = "low";
      else if (hpPct < 55) hpCls = "medium";

      activeAllyPod.innerHTML = `
        <div class="pod-creature-panel">
          <div class="pod-avatar">${ally.avatar}</div>
          <div class="pod-details">
            <div class="pod-name">${ally.name}</div>
            <div class="pod-lvl">LVL ${ally.level}</div>
            <div class="pod-type-badge" style="border:1px solid ${TYPE_COLORS[ally.affinity]}">${ally.affinity}</div>
          </div>
        </div>
        <div class="pod-stats">
          <div class="hp-bar-outer">
            <div class="hp-bar-inner ${hpCls}" style="width: ${hpPct}%"></div>
          </div>
          <div class="hp-text">${ally.hp} / ${ally.maxHp} HP</div>
        </div>
        <div class="status-tray" id="combat-ally-status-tray">
          ${ally.status ? `<span class="status-tag ${ally.status}">${ally.status}</span>` : ''}
        </div>
      `;
    }

    // Load Active Opponent list row
    const targetRow = document.getElementById("combat-opponents");
    if (targetRow) {
      targetRow.innerHTML = "";
      ActiveCombat.opponents.forEach((enemy, idx) => {
        if (enemy.hp <= 0) return; // Hide defeated opponents completely

        const enemyPct = Math.max(0, (enemy.hp / enemy.maxHp) * 100);
        let hpCls = "";
        if (enemyPct < 25) hpCls = "low";
        else if (enemyPct < 55) hpCls = "medium";

        const pod = document.createElement("div");
        pod.className = "battle-pod";
        
        // Add targetable styles depending on action select phase
        if (ActiveCombat.selectedAction) {
          pod.classList.add("targetable");
          pod.onclick = () => executeTriggeredCombatMove(idx);
        }

        pod.innerHTML = `
          <div class="pod-creature-panel">
            <div class="pod-avatar">${enemy.avatar}</div>
            <div class="pod-details">
              <div class="pod-name">${enemy.name}</div>
              <div class="pod-lvl">LVL ${enemy.level}</div>
              <div class="pod-type-badge" style="border:1px solid ${TYPE_COLORS[enemy.affinity]}">${enemy.affinity}</div>
            </div>
          </div>
          <div class="pod-stats">
            <div class="hp-bar-outer">
              <div class="hp-bar-inner ${hpCls}" style="width: ${enemyPct}%"></div>
            </div>
            <div class="hp-text">${enemy.hp} / ${enemy.maxHp} HP</div>
          </div>
          <div class="status-tray">
            ${enemy.status ? `<span class="status-tag ${enemy.status}">${enemy.status}</span>` : ''}
          </div>
        `;
        targetRow.appendChild(pod);
      });
    }

    // Refresh Dynamic panels in active combat deck
    this.refreshCombatAbilityPanel();
  },

  refreshCombatAbilityPanel() {
    const grid = document.getElementById("abilities-grid");
    if (!grid) return;
    grid.innerHTML = "";

    const ally = ActiveCombat.getActiveAlly();
    if (!ally) return;

    ally.activeAbilities.forEach(ab => {
      const details = COMBAT_ABILITIES[ab.moveKey];
      if (!details) return;

      const btn = document.createElement("button");
      btn.className = "combat-act-btn";
      btn.disabled = ab.pp <= 0;
      btn.innerHTML = `
        <div class="btn-title-row">
          <span>${details.name}</span>
          <span class="btn-type-sub" style="color:${TYPE_COLORS[details.affinity]}">${details.affinity}</span>
        </div>
        <div class="btn-meta">
          <span>Pwr: ${details.power}</span>
          <span class="btn-pp">PP: ${ab.pp}/${ab.maxPp}</span>
        </div>
      `;
      btn.onclick = () => triggerSelectCombatTarget(ab.moveKey);
      grid.appendChild(btn);
    });
  }
};

// ── const: OverworldController ──
// INTERACTIVE FIELD MOVEMENT, ENCOUNTERS, AND STORY VIGNETTES LOGIC
const OverworldController = {
  playerX: 5,
  playerY: 5,
  canvasInstance: null,

  init(overworldEngineInstance) {
    this.canvasInstance = overworldEngineInstance;
    this.refresh();
  },

  refresh() {
    this.playerX = CurrentState.playerPos.x;
    this.playerY = CurrentState.playerPos.y;
    this.canvasInstance.render(CurrentState.playerPos, CurrentState.currentZone, CurrentState.anchorsDefeated[CurrentState.currentZone]);
    UIRenderer.updateHUD();
  },

  tryMove(dx, dy) {
    const targetX = this.playerX + dx;
    const targetY = this.playerY + dy;

    // Bounds checking
    if (targetX < 0 || targetX >= MAP_COLS || targetY < 0 || targetY >= MAP_ROWS) return;

    // Solid Wall Collision check
    const tile = BASE_MAP_TEMPLATE[targetY][targetX];
    if (tile === 2) {
      AudioController.triggerBeep(180, 'triangle', 0.15); // Bonk Sound
      return; 
    }

    // Success movement
    this.playerX = targetX;
    this.playerY = targetY;
    CurrentState.playerPos.x = targetX;
    CurrentState.playerPos.y = targetY;

    AudioController.triggerBeep(520, 'sine', 0.05);

    // Re-render
    this.refresh();

    // Check interaction behaviors
    this.evaluateCurrentTile(tile);
  },

  evaluateCurrentTile(tile) {
    // 1 = High resonance grass (Chance of wild battle)
    if (tile === 1) {
      const zoneData = ZONE_DATABASES[CurrentState.currentZone];
      if (Math.random() < zoneData.encounterRate) {
        triggerBattleTransition("wild");
      }
    } 
    // 3 = Rest Sanctuary (Shrine)
    else if (tile === 3) {
      triggerShrineTransition();
    }
    // 4 = Zone Guardian (Boss)
    else if (tile === 4) {
      // Trigger boss dialog/battle if not already defeated
      if (!CurrentState.anchorsDefeated[CurrentState.currentZone]) {
        triggerNarrativeVignette(
          "THRESHOLD CHALLENGE",
          `Guardian ${ZONE_DATABASES[CurrentState.currentZone].guardianName} hums at high frequency. Defeat it to release this zone's dimensional anchor!`,
          () => triggerBattleTransition("guardian")
        );
      } else {
        triggerNarrativeVignette(
          "ANCHOR HARMONIZED",
          `You have already stabilized the spatial structure of this alignment. Move to the orange warp zone!`,
          null
        );
      }
    }
    // 5 = Zone transport gate warp
    else if (tile === 5) {
      // Can only travel to next zone if the previous zone's guardian was defeated!
      if (CurrentState.anchorsDefeated[CurrentState.currentZone]) {
        if (CurrentState.currentZone < 5) {
          CurrentState.currentZone++;
          CurrentState.playerPos = { x: 5, y: 5 }; // Restart positions
          ZONE_DATABASES[CurrentState.currentZone].unlocked = true;
          this.refresh();
          triggerNarrativeVignette(
            "ZONE SHIFT SUCCESS",
            `Successfully ascended to Region ${CurrentState.currentZone + 1}: ${ZONE_DATABASES[CurrentState.currentZone].name}. ${ZONE_DATABASES[CurrentState.currentZone].storyNode}`,
            null
          );
        } else {
          // POST-GAME FREEROAM / VICTORY CONDITIONS
          triggerNarrativeVignette(
            "COSMOS INTEGRITY COUPLING",
            "Perfect resonance accomplished! All regional anchors unchained! Post-game free roam unlocked (Harder high level wild spawns active). Enjoy exploring Aethera!",
            null
          );
        }
      } else {
        triggerNarrativeVignette(
          "ACCESS DECLINED",
          `The transmission barrier is locked. Deconstruct Guardian ${ZONE_DATABASES[CurrentState.currentZone].guardianName} (#4 anchor) first to cross.`,
          null
        );
      }
    }
  }
};

// ── function: triggerBattleTransition ──
// SYSTEM GLUE ACTIONS, WIRING TRANSITIONS, EVOLUTION & SHOP TRIGGER EVENTS
// Global state transition triggers

function triggerBattleTransition(type) {
  // Save position, switch active viewport screen
  document.getElementById("adventure-screen").classList.add("hidden");
  document.getElementById("combat-screen").classList.remove("hidden");

  // Reset context panels state-wise
  hideAllCombatContextPanels();

  if (type === "wild") {
    ActiveCombat.initWildBattle(CurrentState.currentZone, CurrentState.team);
  } else {
    ActiveCombat.initGuardianBattle(CurrentState.currentZone, CurrentState.team);
  }

  // Bind active combat engine updates to DOM updates
  ActiveCombat.setUpdateCallback(() => {
    UIRenderer.refreshCombatScreen();
    checkCombatDecisiveEnd();
  });

  UIRenderer.refreshCombatScreen();
  AudioController.triggerBeep(660, 'sawtooth', 0.25);
}

// ── function: hideAllCombatContextPanels ──
function hideAllCombatContextPanels() {
  document.getElementById("combat-abilities-panel").classList.add("hidden");
  document.getElementById("combat-allies-panel").classList.add("hidden");
  document.getElementById("combat-items-panel").classList.add("hidden");
  document.getElementById("combat-main-actions").classList.remove("hidden");
  ActiveCombat.selectedAction = null;
}

// ── function: triggerSelectCombatTarget ──
// BATTLE TURN EXECUTION CONTROL
function triggerSelectCombatTarget(abilityKey) {
  // Store dynamic choice and alert player to select a target in the opponent row
  ActiveCombat.selectedAction = { abilityKey: abilityKey };
  ActiveCombat.currentLog = `⚡ Select target enemy battle pod to release energy flow!`;
  UIRenderer.refreshCombatScreen();
}

// ── function: executeTriggeredCombatMove ──
function executeTriggeredCombatMove(enemyIdx) {
  if (!ActiveCombat.selectedAction) return;

  const abKey = ActiveCombat.selectedAction.abilityKey;
  ActiveCombat.executeAbilityTurn(abKey, enemyIdx);

  // Return to main options selection cards deck
  hideAllCombatContextPanels();
}

// ── function: checkCombatDecisiveEnd ──
function checkCombatDecisiveEnd() {
  // 1. Did all active allies crash?
  const allAlliesDown = CurrentState.team.every(a => a.hp <= 0);
  if (allAlliesDown) {
    ActiveCombat.currentLog = `🚨 All active essences collapsed. Auto-teleporting to last shrine baseline...`;
    UIRenderer.refreshCombatScreen();
    setTimeout(() => {
      exitCombatToField(false);
    }, 2800);
    return;
  }

  // 2. Did all opponents crash?
  const allEnemiesDown = ActiveCombat.opponents.every(e => e.hp <= 0);
  if (allEnemiesDown) {
    const totalExpReward = Math.max(30, Math.floor(100 * (ActiveCombat.opponents[0].level / ActiveCombat.getActiveAlly().level)));
    const fluxWon = Math.floor(25 * ActiveCombat.opponents.length + Math.random() * 20);

    CurrentState.fluxWallet += fluxWon;

    ActiveCombat.currentLog = `🏆 VICTORY! Acquired +${fluxWon} flux cores. Distributed +${totalExpReward} cosmic experience.`;
    
    // Distribute level EXP bounds
    CurrentState.team.forEach(ally => {
      if (ally.hp > 0) {
        ally.xp += totalExpReward;
        if (ally.xp >= ally.maxXp) {
          CurrentState.levelUpCreature(ally);
          ActiveCombat.currentLog += `<br>⭐ ${ally.name} reached Depth Level: ${ally.level}!`;
          AudioController.triggerCritNoise();
        }
      }
    });

    UIRenderer.refreshCombatScreen();

    // Check if this was a Guardian victory
    if (ActiveCombat.combatType === "guardian") {
      CurrentState.anchorsDefeated[CurrentState.currentZone] = true;
    }

    setTimeout(() => {
      exitCombatToField(true);
    }, 3000);
  }
}

// ── function: exitCombatToField ──
function exitCombatToField(victorySucess) {
  document.getElementById("combat-screen").classList.add("hidden");
  document.getElementById("adventure-screen").classList.remove("hidden");

  if (!victorySucess) {
    // Standard penalty - teleport back to safety start point
    CurrentState.playerPos = { x: 5, y: 5 };
    // Refill party health partially to avoid loop issues
    CurrentState.team.forEach(ally => {
      ally.hp = Math.max(ally.hp, Math.floor(ally.maxHp * 0.40));
    });
  }

  OverworldController.refresh();
}

// ── function: triggerShrineTransition ──
// REST SANCTUARY & SHOP INTEGRATION
function triggerShrineTransition() {
  document.getElementById("adventure-screen").classList.add("hidden");
  document.getElementById("shrine-screen").classList.remove("hidden");
  document.getElementById("shop-flux-count").textContent = CurrentState.fluxWallet;
  document.getElementById("shop-drawer").classList.add("hidden");
}

// ── function: exitShrineToField ──
function exitShrineToField() {
  document.getElementById("shrine-screen").classList.add("hidden");
  document.getElementById("adventure-screen").classList.remove("remove");
  document.getElementById("adventure-screen").classList.remove("hidden");
  OverworldController.refresh();
}

// ── function: triggerSanctuaryHealAll ──
function triggerSanctuaryHealAll() {
  CurrentState.team.forEach(ally => {
    ally.hp = ally.maxHp;
    ally.status = null;
    ally.activeAbilities.forEach(ab => ab.pp = ab.maxPp);
  });
  AudioController.triggerCritNoise();
  alert("✨ Celestial alignment completed! All creatures restored to peak state.");
}

// ── function: triggerShopExchange ──
function triggerShopExchange(itemKey) {
  const item = INVENTORY_DB[itemKey];
  if (!item) return;

  if (CurrentState.fluxWallet >= item.cost) {
    CurrentState.fluxWallet -= item.cost;
    CurrentState.inventory[itemKey] = (CurrentState.inventory[itemKey] || 0) + 1;
    document.getElementById("shop-flux-count").textContent = CurrentState.fluxWallet;
    AudioController.triggerBeep(880, 'sine', 0.15);
    alert(`Acquired 1x ${item.name}! Packed into Chrono-pack.`);
  } else {
    AudioController.triggerBeep(150, 'sawtooth', 0.2);
    alert("Insufficient flux cores for currency exchange.");
  }
}

// ── function: triggerItemCombatUse ──
// BATTLE ITEM INVENTORY ACTIONS
function triggerItemCombatUse(itemKey) {
  const item = INVENTORY_DB[itemKey];
  if (!item) return;

  // Defer to capture rhythm mini-game or restore healing directly
  if (item.type === 'capture') {
    if (ActiveCombat.combatType === "guardian") {
      ActiveCombat.currentLog = "⚠️ You cannot capture a Sentinel Anchor guardian!";
      UIRenderer.refreshCombatScreen();
      return;
    }

    // Spend item immediately
    CurrentState.inventory[itemKey]--;
    if (CurrentState.inventory[itemKey] <= 0) delete CurrentState.inventory[itemKey];

    hideAllCombatContextPanels();

    // Start Rhythm Harmonizer Ring sequence!
    ActiveHarmonizer.start(itemKey, 0, (accuracy) => {
      const target = ActiveCombat.opponents[0];
      const remainingPct = target.hp / target.maxHp;
      
      // Captured math success probability scales
      const successChance = (1.0 - remainingPct) * item.rate * accuracy;
      
      if (Math.random() < successChance || successChance > 0.8) {
        // Success alignment capture!
        const successAdd = CurrentState.addCreatureToTeam(target.modelId, target.level);
        if (successAdd) {
          ActiveCombat.currentLog = `🌌 HARMONIZED SUCCESSFULLY! ${target.name} joined your cosmic reservoir party.`;
        } else {
          ActiveCombat.currentLog = `🌌 HARMONIZED! (Reservoir storage packed, limited only to active team of 6).`;
        }
        UIRenderer.refreshCombatScreen();
        setTimeout(() => exitCombatToField(true), 2800);
      } else {
        ActiveCombat.currentLog = `❌ Resonance failed to synchronize. ${target.name} escaped the capture field.`;
        UIRenderer.refreshCombatScreen();
        // Opponent replies instantly!
        setTimeout(() => {
          ActiveCombat.currentLog += "<br>" + ActiveCombat.processOpponentReply(target, ActiveCombat.getActiveAlly());
          UIRenderer.refreshCombatScreen();
        }, 1200);
      }
    });

  } else if (item.type === 'heal') {
    // Fast restore active ally HP
    CurrentState.inventory[itemKey]--;
    if (CurrentState.inventory[itemKey] <= 0) delete CurrentState.inventory[itemKey];

    const activeAlly = ActiveCombat.getActiveAlly();
    activeAlly.hp = Math.min(activeAlly.maxHp, activeAlly.hp + item.hp);
    ActiveCombat.currentLog = `🧪 Used ${item.name}. Regenerated HP to active Node!`;

    hideAllCombatContextPanels();
    UIRenderer.refreshCombatScreen();
  }
}

// ── function: triggerPartyEvolution ──
// PARTY MUTATIONS (EVOLUTIONS)
function triggerPartyEvolution(idx, itemKey) {
  const ally = CurrentState.team[idx];
  const res = CurrentState.triggerCatalystTransformation(ally, itemKey);
  alert(res.msg);
  UIRenderer.refreshCodexMenu();
  UIRenderer.showTeamMemberDetail(idx);
}

// ── function: triggerOutOfBattleHeal ──
// OUT OF BATTLE RESTORE
function triggerOutOfBattleHeal(itemKey) {
  const item = INVENTORY_DB[itemKey];
  if (!item || item.type !== 'heal') return;

  const targetAlly = CurrentState.team.find(a => a.hp < a.maxHp);
  if (!targetAlly) {
    alert("Total party is already at maximum structural physical density!");
    return;
  }

  CurrentState.inventory[itemKey]--;
  if (CurrentState.inventory[itemKey] <= 0) delete CurrentState.inventory[itemKey];

  targetAlly.hp = Math.min(targetAlly.maxHp, targetAlly.hp + item.hp);
  AudioController.triggerBeep(990, 'sine', 0.2);
  UIRenderer.refreshCodexMenu();
  alert(`Applied healing capsule successfully onto ${targetAlly.name}! HP restored.`);
}

// ── function: triggerNarrativeVignette ──
// FLOATING STORY DIALOG DISPLAY SYSTEM
function triggerNarrativeVignette(title, text, cb) {
  const overlay = document.getElementById("narrative-vignette");
  const titleNode = document.getElementById("narr-vignette-title");
  const bodyNode = document.getElementById("narr-vignette-body");
  const btn = document.getElementById("narr-vignette-btn");

  if (overlay && titleNode && bodyNode) {
    titleNode.textContent = title;
    bodyNode.textContent = text;
    overlay.classList.remove("hidden");

    btn.onclick = () => {
      overlay.classList.add("hidden");
      if (cb) cb();
    };
  }
}

// ── main ──
// WIRE UP INTERFACES, BIND DOM ONCLICK EVENTS, EXECUTE SYSTEM ENGINE RUN!

// Title Screen Initializing and continue state loaders
const serializedSave = localStorage.getItem("aethera_save_v1");
const contBtn = document.getElementById("continue-btn");
if (serializedSave && contBtn) {
  contBtn.disabled = false;
}

// Sound click listener
document.getElementById("mute-btn").onclick = () => {
  const isMuted = AudioController.toggle();
  document.getElementById("mute-btn").textContent = isMuted ? "🔇" : "🔊";
};

// Title action bindings
document.getElementById("new-game-btn").onclick = () => {
  AudioController.playAmbientLoop();
  
  // Start custom tutorial flow
  document.getElementById("title-screen").classList.add("hidden");
  document.getElementById("intro-modal").classList.remove("hidden");

  // Render starter selection inside modal
  const starterGrid = document.getElementById("starter-choice-container");
  starterGrid.classList.remove("hidden");
  starterGrid.innerHTML = `
    <div class="starter-card selected" data-id="1">
      <div class="starter-icon">🐢</div>
      <div class="starter-name">Pyroshell</div>
      <div class="starter-affinity">Pyre</div>
    </div>
    <div class="starter-card" data-id="2">
      <div class="starter-icon">🐕</div>
      <div class="starter-name">Sporepup</div>
      <div class="starter-affinity">Bloom</div>
    </div>
    <div class="starter-card" data-id="3">
      <div class="starter-icon">🐬</div>
      <div class="starter-name">Finlet</div>
      <div class="starter-affinity">Hydro</div>
    </div>
  `;

  document.getElementById("intro-body").innerHTML = `
    Welcome Cosmic Resonator. Biological constructs known as the Resonated exist across the fractured domains of Aethera.<br><br>
    Choose your starting cosmic partner signature to begin balancing spatial anchors!
  `;

  // Bind starter clicks
  let selectedId = 1;
  const cards = document.querySelectorAll(".starter-card");
  cards.forEach(card => {
    card.onclick = () => {
      cards.forEach(c => c.classList.remove("selected"));
      card.classList.add("selected");
      selectedId = parseInt(card.getAttribute("data-id"));
    };
  });

  // Finish tutorial button
  document.getElementById("intro-next-btn").onclick = () => {
    // Add selected starter
    CurrentState.addCreatureToTeam(selectedId, 6);
    document.getElementById("intro-modal").classList.add("hidden");

    // Transition directly to field adventure
    document.getElementById("adventure-screen").classList.remove("hidden");
    
    // Boot canvas engine
    const world = new OverworldEngine("overworld-canvas");
    OverworldController.init(world);

    // Initial narrative dialogue guidance
    triggerNarrativeVignette(
      "ZONE MISSION ACTIVATED",
      ZONE_DATABASES[0].storyNode,
      null
    );
  };
};

document.getElementById("continue-btn").onclick = () => {
  const save = localStorage.getItem("aethera_save_v1");
  if (save) {
    CurrentState.deserialize(save);
    AudioController.playAmbientLoop();
    document.getElementById("title-screen").classList.add("hidden");
    document.getElementById("adventure-screen").classList.remove("hidden");

    const world = new OverworldEngine("overworld-canvas");
    OverworldController.init(world);
  }
};

// Keyboard listener for overworld navigation
window.onkeydown = (e) => {
  // Only receive moves when adventure overworld is fully visible and menu is hidden
  const screen = document.getElementById("adventure-screen");
  if (screen && !screen.classList.contains("hidden")) {
    const codex = document.getElementById("menu-overlay");
    if (codex && codex.classList.contains("hidden")) {
      if (e.key === "KeyW" || e.key === "ArrowUp") OverworldController.tryMove(0, -1);
      if (e.key === "KeyS" || e.key === "ArrowDown") OverworldController.tryMove(0, 1);
      if (e.key === "KeyA" || e.key === "ArrowLeft") OverworldController.tryMove(-1, 0);
      if (e.key === "KeyD" || e.key === "ArrowRight") OverworldController.tryMove(1, 0);
    }
  }
};

// On-screen field pad arrow buttons mapping
document.getElementById("pad-up").onclick = () => OverworldController.tryMove(0, -1);
document.getElementById("pad-down").onclick = () => OverworldController.tryMove(0, 1);
document.getElementById("pad-left").onclick = () => OverworldController.tryMove(-1, 0);
document.getElementById("pad-right").onclick = () => OverworldController.tryMove(1, 0);

// Codex Menu Overlay Button bindings
document.getElementById("menu-btn").onclick = () => {
  document.getElementById("menu-overlay").classList.remove("hidden");
  UIRenderer.refreshCodexMenu();
};

document.getElementById("codex-close").onclick = () => {
  document.getElementById("menu-overlay").classList.add("hidden");
};

// Menu overlay tab buttons
const tabBtns = document.querySelectorAll(".codex-tabs .tab-btn:not(#codex-close)");
tabBtns.forEach(btn => {
  btn.onclick = () => {
    tabBtns.forEach(b => b.classList.remove("active"));
    btn.classList.add("active");

    const targetTab = btn.getAttribute("data-tab");
    const panels = document.querySelectorAll(".codex-content .tab-panel");
    panels.forEach(p => p.classList.remove("active"));

    const targetPanel = document.getElementById(targetTab);
    if (targetPanel) {
      targetPanel.classList.add("active");
    }
  };
});

// COMBAT ACTION CONTROLLER BUTTONS WIRE-UPS
document.getElementById("act-attack-btn").onclick = () => {
  document.getElementById("combat-main-actions").classList.add("hidden");
  document.getElementById("combat-abilities-panel").classList.remove("hidden");
};

document.getElementById("combat-abilities-back").onclick = () => {
  hideAllCombatContextPanels();
  UIRenderer.refreshCombatScreen();
};

document.getElementById("act-switch-btn").onclick = () => {
  document.getElementById("combat-main-actions").classList.add("hidden");
  const panel = document.getElementById("combat-allies-panel");
  panel.classList.remove("hidden");

  // Load switch list grid of available reserves
  const grid = document.getElementById("switch-allies-grid");
  grid.innerHTML = "";

  CurrentState.team.forEach((ally, idx) => {
    const isCurrentActive = idx === ActiveCombat.activeAllyIdx;
    
    const card = document.createElement("button");
    card.className = "combat-act-btn";
    card.disabled = ally.hp <= 0 || isCurrentActive;
    card.style.minWidth = "160px";
    card.innerHTML = `
      <div class="btn-title-row">
        <span>${ally.avatar} ${ally.name}</span>
      </div>
      <div>
        <div style="font-size:10px;">Level: ${ally.level}</div>
        <div style="font-size:10px; color:var(--text-muted);">HP: ${ally.hp}/${ally.maxHp}</div>
      </div>
    `;
    card.onclick = () => {
      ActiveCombat.switchActiveAlly(idx);
      hideAllCombatContextPanels();
    };
    grid.appendChild(card);
  });
};

document.getElementById("combat-switch-back").onclick = () => {
  hideAllCombatContextPanels();
};

document.getElementById("act-collect-btn").onclick = () => {
  // Use first available Harmonizer item instantly
  const captureItemKey = Object.keys(CurrentState.inventory).find(k => INVENTORY_DB[k].type === 'capture');
  if (captureItemKey) {
    triggerItemCombatUse(captureItemKey);
  } else {
    alert("You have no Harmonizer tuning cores in inventory reservoir! Go buy some at regional Shrines.");
  }
};

document.getElementById("act-inventory-btn").onclick = () => {
  document.getElementById("combat-main-actions").classList.add("hidden");
  const panel = document.getElementById("combat-items-panel");
  panel.classList.remove("hidden");

  const grid = document.getElementById("combat-items-grid");
  grid.innerHTML = "";

  Object.keys(CurrentState.inventory).forEach(itemKey => {
    const item = INVENTORY_DB[itemKey];
    const qty = CurrentState.inventory[itemKey];
    if (qty <= 0) return;

    const btn = document.createElement("button");
    btn.className = "combat-act-btn";
    btn.innerHTML = `
      <div class="btn-title-row">
        <span>${item.dName}</span>
        <span style="color:var(--accent-gold);">x${qty}</span>
      </div>
      <div style="font-size:9px; color:var(--text-muted);">${item.desc}</div>
    `;
    btn.onclick = () => {
      triggerItemCombatUse(itemKey);
    };
    grid.appendChild(btn);
  });
};

document.getElementById("combat-items-back").onclick = () => {
  hideAllCombatContextPanels();
};

document.getElementById("harmonize-tap-btn").onclick = () => {
  ActiveHarmonizer.triggerTap();
};

// SHRINE CONTROL DECK
document.getElementById("shrine-heal").onclick = () => {
  triggerSanctuaryHealAll();
};

document.getElementById("shrine-save").onclick = () => {
  localStorage.setItem("aethera_save_v1", CurrentState.serialize());
  alert("💾 Spatial index progression successfully synchronized to local continuum state!");
};

document.getElementById("shrine-shop").onclick = () => {
  const drawer = document.getElementById("shop-drawer");
  drawer.classList.toggle("hidden");

  // Populate Shop Items
  const grid = document.getElementById("shop-items-grid");
  grid.innerHTML = "";

  Object.keys(INVENTORY_DB).forEach(key => {
    const item = INVENTORY_DB[key];
    const card = document.createElement("div");
    card.className = "shop-card";
    card.innerHTML = `
      <div class="shop-card-info">
        <span class="shop-item-name">${item.dName}</span>
        <span class="shop-item-cost">💸 ${item.cost} flux</span>
      </div>
      <div>🛒</div>
    `;
    card.onclick = () => triggerShopExchange(key);
    grid.appendChild(card);
  });
};

document.getElementById("shrine-exit").onclick = () => {
  exitShrineToField();
};

// Start ambient particles background loop execution
setInterval(() => {
  const adventure = document.getElementById("adventure-screen");
  if (adventure && !adventure.classList.contains("hidden")) {
    OverworldController.refresh();
  }
}, 50);
</script>
</body>
</html>
<!-- agent-meta {"model":"google/gemini-3.5-flash","provider":"openrouter","persona":"composer","sessionId":"a63724ef-3698-4a8e-a49b-35d0373eb2cf","tokensIn":648127,"tokensOut":41752,"tokensTotal":689879,"turns":27,"toolCalls":26,"failedToolCalls":0,"timestamp":"2026-07-14T05:11:32.278Z"} -->