Aethelgard
GPT 6 Luna · full

Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 68,773 bytes · SHA-256 e053f4e4f6bb
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="theme-color" content="#0b1020"><title>Aethelgard — Resonance Atlas</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Manrope:wght@400;500;600;700;800&family=Press+Start+2P&display=swap" rel="stylesheet">
<style>
:root{--bg:#0a0d19;--panel:rgba(21,27,48,.78);--line:rgba(163,177,222,.15);--muted:#929bb8;--ink:#f5f6ff;--mint:#75f0c5;--violet:#a78bfa;--gold:#f5c76b;--danger:#ff718a}*{box-sizing:border-box}body{margin:0;background:radial-gradient(ellipse at 22% -8%,#282351 0,transparent 38%),radial-gradient(ellipse at 92% 34%,#102844 0,transparent 35%),linear-gradient(145deg,#0a0d19,#10152a 68%,#0b1020);color:var(--ink);font-family:Manrope, sans-serif;min-height:100vh}body:before{content:"";position:fixed;inset:0;pointer-events:none;opacity:.24;background-image:radial-gradient(#a2b5f7 1px,transparent 1px);background-size:47px 47px;mask-image:linear-gradient(to bottom,black,transparent 90%)}button{font:inherit;color:inherit;cursor:pointer}.mono{font-family:'DM Mono',monospace}.pixel{font-family:'Press Start 2P',monospace}.app-shell{max-width:1220px;margin:auto;min-height:100vh;padding:26px 32px 40px;position:relative}.glass{background:linear-gradient(145deg,rgba(30,37,64,.8),rgba(15,20,38,.78));border:1px solid var(--line);box-shadow:0 20px 60px rgba(0,0,0,.22),inset 0 1px rgba(255,255,255,.035);backdrop-filter:blur(18px);border-radius:22px}.topbar{height:60px;display:flex;align-items:center;justify-content:space-between;margin-bottom:27px}.brand{display:flex;gap:13px;align-items:center}.brandmark{width:38px;height:38px;border-radius:13px;display:grid;place-items:center;background:linear-gradient(135deg,#9a7bff,#48d6d0);box-shadow:0 0 25px #7f69e640}.brandmark svg{width:23px}.brand-title{font-size:14px;font-weight:800;letter-spacing:.16em}.brand-sub{font-size:9px;color:#98a0bd;letter-spacing:.24em;text-transform:uppercase;margin-top:3px}.top-actions{display:flex;align-items:center;gap:10px}.resource{padding:9px 13px;border:1px solid var(--line);border-radius:12px;background:#ffffff08;font-size:12px;color:#f3d286;display:flex;align-items:center;gap:8px}.resource b{color:#fff;font-family:'DM Mono';font-size:13px}.icon-btn,.nav-btn{border:1px solid var(--line);background:#ffffff08;border-radius:11px;padding:9px 12px;color:#c6cbe0;transition:.2s}.icon-btn:hover,.nav-btn:hover{background:#ffffff13;transform:translateY(-1px)}.screen{display:none;animation:screenIn .36s ease both}.screen.active{display:block}@keyframes screenIn{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}.eyebrow{color:#9ea9cb;font:10px 'DM Mono';letter-spacing:.2em;text-transform:uppercase}.display{font-size:clamp(32px,5vw,55px);line-height:1.08;letter-spacing:-.055em;font-weight:800;margin:14px 0}.gradient-text{background:linear-gradient(95deg,#fff,#b8a5ff 58%,#80e7d1);-webkit-background-clip:text;background-clip:text;color:transparent}.lead{color:#a7aec8;line-height:1.7;font-size:14px;max-width:550px}.btn{border:0;border-radius:12px;padding:12px 17px;font-weight:800;font-size:12px;letter-spacing:.02em;transition:.2s;display:inline-flex;align-items:center;justify-content:center;gap:9px}.btn:hover{transform:translateY(-2px);filter:brightness(1.1)}.btn-primary{background:linear-gradient(110deg,#a28aff,#63d9d0);color:#101326;box-shadow:0 10px 27px #7668db38}.btn-quiet{background:#ffffff0b;border:1px solid var(--line);color:#d8ddf2}.btn-danger{background:#ff718a1a;border:1px solid #ff718a48;color:#ff9aab}.btn-small{padding:8px 11px;font-size:10px;border-radius:9px}.home-grid{display:grid;grid-template-columns:1.12fr .88fr;gap:20px;align-items:stretch}.hero-card{padding:42px 42px 35px;min-height:400px;position:relative;overflow:hidden}.hero-card:after{content:"";position:absolute;right:-75px;top:29px;width:370px;height:370px;border-radius:50%;background:radial-gradient(circle,#9178fa20,transparent 67%);pointer-events:none}.hero-art{position:absolute;right:36px;bottom:7px;width:230px;opacity:.88;filter:drop-shadow(0 10px 30px #8777ed44);z-index:0}.hero-copy{position:relative;z-index:1}.hero-copy .lead{margin:18px 0 25px}.hero-buttons{display:flex;gap:10px;flex-wrap:wrap}.home-aside{display:flex;flex-direction:column;gap:16px}.quote-card{padding:26px;flex:1;position:relative;overflow:hidden}.quote-card blockquote{margin:22px 0 20px;font-size:20px;line-height:1.45;font-weight:600;letter-spacing:-.03em}.quote-card .byline{color:#9ba5c4;font-size:11px}.quote-orb{position:absolute;right:-25px;top:-37px;color:#b5a4ff;opacity:.13;font-size:140px}.stat-row{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}.stat-tile{padding:15px;border-radius:15px;background:#ffffff07;border:1px solid var(--line)}.stat-tile strong{display:block;font:18px 'DM Mono';margin:7px 0 4px}.stat-tile span{color:#9aa4c2;font-size:9px;text-transform:uppercase;letter-spacing:.13em}.footer-note{font-size:10px;color:#68728f;margin-top:22px}.choose-head,.page-head{display:flex;justify-content:space-between;align-items:end;gap:20px;margin:16px 0 23px}.choose-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.starter-card{padding:22px;cursor:pointer;transition:.24s;position:relative;overflow:hidden}.starter-card:hover{transform:translateY(-5px);border-color:#9b88ef77;box-shadow:0 18px 42px #7e6fcd24}.starter-card .sprite{height:150px}.starter-card h3{font-size:20px;margin:3px 0 7px}.starter-card p{font-size:12px;color:#a3acc7;line-height:1.6}.type-pill{padding:5px 8px;border:1px solid currentColor;border-radius:99px;font:9px 'DM Mono';letter-spacing:.12em;text-transform:uppercase;display:inline-flex;align-items:center;gap:6px}.card-foot{display:flex;align-items:center;justify-content:space-between;margin-top:17px}.zone-layout{display:grid;grid-template-columns: minmax(0,1.45fr) minmax(275px,.65fr);gap:18px}.zone-main{padding:26px;min-height:535px;overflow:hidden;position:relative}.zone-heading{display:flex;justify-content:space-between;align-items:start;gap:12px}.zone-heading h2{font-size:29px;letter-spacing:-.05em;margin:6px 0}.zone-desc{font-size:12px;color:#9da7c2;max-width:480px;line-height:1.6}.zone-art{position:absolute;width:180px;height:140px;right:20px;top:8px;opacity:.7}.map-trail{position:relative;display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:145px;gap:0;margin:34px 0 0;isolation:isolate}.trail-svg{position:absolute;inset:0;width:100%;height:100%;z-index:-1;overflow:visible}.trail-svg path{fill:none;stroke:#8582c75a;stroke-width:2;stroke-dasharray:4 8}.node-wrap{display:flex;align-items:center;justify-content:center;position:relative}.node-wrap:nth-child(4n+2),.node-wrap:nth-child(4n+4){transform:translateY(46px)}.node{height:76px;width:76px;border-radius:25px;border:1px solid #ffffff26;background:linear-gradient(145deg,#292d4a,#161b32);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;transition:.22s;box-shadow:0 8px 28px #0005;position:relative}.node:hover:not(:disabled){transform:translateY(-5px) scale(1.04);border-color:#b6a1ff;box-shadow:0 0 26px #9985ff34}.node:disabled{opacity:.35;filter:grayscale(.7);cursor:not-allowed}.node.done{border-color:#65dfb68c;background:linear-gradient(145deg,#205847,#142d32)}.node .node-icon{font-size:23px}.node small{font:8px 'DM Mono';color:#c0c5dc}.node-flag{position:absolute;top:-6px;right:-5px;width:19px;height:19px;border-radius:50%;background:#82e6bd;color:#12231e;font-size:11px;display:grid;place-items:center}.zone-sidebar{display:flex;flex-direction:column;gap:15px}.side-card{padding:21px}.side-card h3{font-size:13px;margin:0 0 15px}.progress-line{height:6px;border-radius:9px;background:#ffffff12;overflow:hidden;margin:10px 0}.progress-line i{height:100%;display:block;background:linear-gradient(90deg,#9a82ff,#6de7cb);border-radius:9px}.party-mini{display:flex;flex-direction:column;gap:10px}.party-row{display:flex;align-items:center;gap:10px;padding:8px;border:1px solid #ffffff0d;border-radius:12px;background:#ffffff05}.mini-sprite{width:45px;height:41px;border-radius:10px;background:#ffffff08;display:grid;place-items:center}.mini-sprite svg{width:39px;height:39px}.party-row strong{font-size:11px;display:block}.party-row small{font-size:9px;color:#a4adc8}.mini-hp{height:4px;border-radius:5px;background:#ffffff13;margin-top:5px;width:100%;overflow:hidden}.mini-hp i{display:block;height:100%;background:#6ee3b3}.zone-nav{display:flex;gap:7px;flex-wrap:wrap;margin-top:17px}.zone-chip{padding:7px 9px;border-radius:9px;font-size:9px;border:1px solid var(--line);background:#ffffff05;color:#9ca6c1}.zone-chip.current{color:#fff;border-color:#a58cff88;background:#a58cff19}.zone-chip.locked{opacity:.4}.battle-layout{display:grid;grid-template-columns:minmax(0,1fr) 315px;gap:17px}.battle-stage{padding:25px;min-height:510px;position:relative;overflow:hidden;background:radial-gradient(ellipse at 76% 30%,#6d4b8528,transparent 50%),linear-gradient(145deg,#202541,#101629)}.battle-stage:before{content:"";position:absolute;left:-50px;right:-50px;bottom:116px;height:110px;border-radius:50%;background:#75d3bc0b;filter:blur(9px)}.battle-top{display:flex;justify-content:space-between;align-items:center;position:relative;z-index:2}.battle-label{font:9px 'DM Mono';letter-spacing:.17em;color:#a2aac3;text-transform:uppercase}.battle-arena{height:275px;position:relative;display:flex;align-items:center;justify-content:space-between;padding:0 7%;z-index:1}.fighter{width:43%;display:flex;flex-direction:column;align-items:center;position:relative}.fighter.enemy{transform:translateY(-22px)}.fighter.player{transform:translateY(25px)}.fighter .sprite{height:175px;width:185px;display:grid;place-items:center;filter:drop-shadow(0 19px 20px #0008);animation:bob 2.8s ease-in-out infinite}.fighter.enemy .sprite{transform:scaleX(-1);animation-delay:.5s}.fighter .sprite svg{width:100%;height:100%}@keyframes bob{50%{translate:0 -8px}}.fighter-info{width:min(100%,250px);padding:12px 14px;border-radius:14px;background:#101629df;border:1px solid #ffffff1b;backdrop-filter:blur(12px);position:relative;z-index:2}.fighter-name{display:flex;justify-content:space-between;align-items:center;font-size:12px;font-weight:800}.fighter-name small{font:9px 'DM Mono';color:#aeb5ce}.hp-track{height:7px;background:#ffffff16;border-radius:10px;margin:9px 0 5px;overflow:hidden}.hp-fill{height:100%;border-radius:10px;background:linear-gradient(90deg,#51dba8,#9ce77c);transition:width .5s,background .4s}.hp-meta{display:flex;justify-content:space-between;color:#aeb6ce;font:9px 'DM Mono'}.battle-log{height:55px;display:flex;align-items:center;padding:0 15px;color:#d1d6e9;font-size:12px;border-top:1px solid #ffffff12;margin-top:3px;line-height:1.45}.battle-controls{padding:19px}.battle-controls h3{font-size:13px;margin:0 0 13px}.move-list{display:grid;grid-template-columns:1fr 1fr;gap:9px}.move-btn{padding:11px;text-align:left;border-radius:12px;background:#ffffff08;border:1px solid var(--line);transition:.2s;min-height:67px}.move-btn:hover{background:#ffffff12;transform:translateY(-2px)}.move-btn strong{font-size:11px;display:block}.move-btn small{font:9px 'DM Mono';color:#9ca7c4;display:block;margin-top:7px}.battle-tools{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-top:12px}.tool-btn{background:#ffffff08;border:1px solid var(--line);border-radius:10px;padding:9px 5px;font-size:9px;color:#d7dcef}.tool-btn:hover{border-color:#a995ff}.status{display:inline-flex;font:8px 'DM Mono';text-transform:uppercase;letter-spacing:.08em;padding:3px 6px;border-radius:6px;background:#ffbd6d23;color:#ffd08d;margin-left:4px}.dex-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.dex-card{padding:16px;position:relative;overflow:hidden}.dex-card-top{display:flex;align-items:center;gap:12px}.dex-avatar{width:78px;height:78px;border-radius:19px;background:#ffffff08;display:grid;place-items:center}.dex-avatar svg{width:75px;height:75px}.dex-card h3{margin:0 0 6px;font-size:15px}.dex-card p{margin:10px 0 0;font-size:10px;line-height:1.6;color:#a6aec6}.dex-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;margin-top:13px}.dex-stats span{background:#ffffff08;border-radius:7px;padding:6px 3px;text-align:center;color:#9ea8c5;font:8px 'DM Mono'}.dex-stats b{display:block;color:#e8eaff;font-size:10px;margin-bottom:3px}.locked-mon{filter:grayscale(1);opacity:.45}.shop-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.shop-item{padding:20px}.shop-icon{height:52px;width:52px;border-radius:16px;background:#ffffff0b;display:grid;place-items:center;font-size:24px;margin-bottom:17px}.shop-item h3{font-size:14px;margin:0 0 7px}.shop-item p{font-size:11px;color:#9ea8c4;line-height:1.55;min-height:52px}.shop-buy{display:flex;align-items:center;justify-content:space-between;margin-top:15px}.team-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.team-card{padding:20px}.team-visual{height:130px;display:grid;place-items:center}.team-visual svg{width:125px;height:125px}.team-card h3{font-size:16px;margin:0}.team-card .team-data{font-size:10px;color:#aab2ca;margin:5px 0 13px}.team-stats{display:flex;gap:10px;color:#9ca6c1;font:9px 'DM Mono';margin:11px 0}.empty-slot{min-height:275px;border:1px dashed #ffffff21;background:#ffffff03;border-radius:20px;display:grid;place-items:center;color:#8f99b7;font-size:12px}.modal-back{position:fixed;inset:0;background:#050711c9;backdrop-filter:blur(9px);display:none;align-items:center;justify-content:center;padding:20px;z-index:20}.modal-back.show{display:flex}.modal{width:min(440px,100%);padding:27px}.modal h2{margin:8px 0 9px;font-size:22px}.modal p{font-size:12px;color:#a8b0c9;line-height:1.6}.modal-actions{display:flex;gap:9px;margin-top:20px}.toast{position:fixed;bottom:24px;left:50%;transform:translate(-50%,18px);opacity:0;transition:.3s;z-index:30;padding:12px 18px;border-radius:12px;background:#252c49;border:1px solid #ffffff24;box-shadow:0 10px 36px #0008;color:#edf0ff;font-size:12px;pointer-events:none}.toast.show{opacity:1;transform:translate(-50%,0)}.shake{animation:shake .38s}@keyframes shake{25%{transform:translateX(-8px)}50%{transform:translateX(7px)}75%{transform:translateX(-4px)}}.flash{animation:flash .35s}@keyframes flash{50%{filter:brightness(2.5) saturate(1.7)}}.particle{position:absolute;width:8px;height:8px;border-radius:50%;background:#9b8cff;box-shadow:0 0 16px currentColor;animation:drift 1.6s ease-out forwards;pointer-events:none}@keyframes drift{to{transform:translate(var(--dx),-90px) scale(.1);opacity:0}}.evo-banner{position:fixed;inset:0;z-index:40;display:none;place-items:center;background:#080a16db;backdrop-filter:blur(12px);text-align:center}.evo-banner.show{display:grid}.evo-inner{animation:screenIn .5s}.evo-inner svg{width:180px;height:180px;filter:drop-shadow(0 0 30px #9b82ff66)}.evo-inner h2{font-size:30px;margin:5px}.hidden{display:none!important}.muted{color:#96a0bd}.type-pyro{color:#ff997b}.type-hydro{color:#75cfff}.type-flora{color:#84e6a1}.type-zephyr{color:#c2adff}.type-earth{color:#e3bd85}.type-volt{color:#ffe47a}.type-cosmos{color:#d4b8ff}.hp-low{background:linear-gradient(90deg,#ff637e,#ffb36b)!important}@media(max-width:850px){.home-grid{grid-template-columns:1fr}.home-aside{display:grid;grid-template-columns:1fr 1fr}.zone-layout{grid-template-columns:1fr}.zone-sidebar{display:grid;grid-template-columns:1fr 1fr}.battle-layout{grid-template-columns:1fr}.battle-controls{order:2}.dex-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:600px){.app-shell{padding:16px 14px 30px}.topbar{margin-bottom:17px}.brand-title{font-size:11px}.resource{padding:8px;font-size:10px}.home-aside{display:flex}.hero-card{padding:29px 24px;min-height:390px}.hero-art{right:-24px;bottom:2px;width:150px;opacity:.24}.hero-copy .lead{max-width:100%;text-shadow:0 1px 10px #11152a}.hero-buttons{position:relative;z-index:2;max-width:100%}.hero-buttons .btn{padding:11px 13px;font-size:11px}.display{font-size:39px}.choose-grid,.shop-grid,.team-grid{grid-template-columns:1fr}.starter-card{display:grid;grid-template-columns:110px 1fr;column-gap:12px;padding:15px}.starter-card .sprite{grid-row:span 3;height:115px}.starter-card .sprite svg{width:110px}.starter-card .card-foot{grid-column:2}.zone-main{padding:18px 13px}.zone-art{width:100px;right:0}.zone-heading h2{font-size:23px}.zone-desc{max-width:70%}.map-trail{grid-template-columns:repeat(3,1fr);grid-auto-rows:116px;margin-top:20px}.node-wrap:nth-child(4n+2),.node-wrap:nth-child(4n+4){transform:translateY(25px)}.node{width:65px;height:65px;border-radius:21px}.trail-svg{opacity:.5}.zone-sidebar{grid-template-columns:1fr}.battle-stage{padding:16px;min-height:480px}.battle-arena{height:260px;padding:0 1%}.fighter .sprite{height:140px;width:140px}.fighter-info{padding:10px 9px}.fighter-name{font-size:10px}.battle-log{font-size:10px;padding:0 6px}.dex-grid{grid-template-columns:1fr 1fr;gap:8px}.dex-card{padding:11px}.dex-card-top{gap:8px}.dex-avatar{width:59px;height:59px}.dex-avatar svg{width:57px}.dex-card h3{font-size:12px}.page-head{align-items:start;flex-direction:column}.stat-row{gap:6px}.stat-tile{padding:11px}.stat-tile strong{font-size:15px}.top-actions{gap:5px}}
</style></head>
<body><main class="app-shell">
<header class="topbar"><div class="brand"><div class="brandmark"><svg viewBox="0 0 24 24" fill="none"><path d="M12 2 21 8v8l-9 6-9-6V8l9-6Z" stroke="#101326" stroke-width="1.8"/><path d="m12 6 5 6-5 6-5-6 5-6Z" fill="#101326"/></svg></div><div><div class="brand-title">AETHELGARD</div><div class="brand-sub">Resonance Atlas</div></div></div><div class="top-actions"><div class="resource" id="shardReadout">✧ <b>0</b> <span>SHARDS</span></div><button class="icon-btn" title="Sound" id="soundToggle">♫</button></div></header>
<section id="screen-menu" class="screen active"><div class="home-grid"><div class="glass hero-card"><div class="hero-copy"><div class="eyebrow">✦ A field guide to the living frequency</div><h1 class="display"><span class="gradient-text">Find your<br>frequency.</span></h1><p class="lead">Across Aethelgard, elemental spirits sing through stone, storm, and root. Tune your Aether Prism. Build your ensemble. Restore the world's great harmony.</p><div class="hero-buttons"><button class="btn btn-primary" id="continueBtn" onclick="continueGame()">Begin expedition <span>↗</span></button><button class="btn btn-quiet" onclick="returnScreen='menu';showScreen('dex')">Open the atlas <span>⌁</span></button><button class="btn btn-quiet" onclick="resetProgress()">Reset progress</button><button class="btn btn-quiet" onclick="showScreen('map')">Explore as guest</button></div><div class="footer-note">A turn-based journey through four resonant regions. Progress saves automatically on this device.</div></div><svg class="hero-art" viewBox="0 0 240 250" fill="none"><circle cx="123" cy="126" r="92" stroke="#9c8aff" opacity=".18"/><circle cx="123" cy="126" r="70" stroke="#7de5d0" opacity=".18" stroke-dasharray="3 7"/><path d="m121 31 57 40 10 70-49 63-68-8-37-58 22-69 65-38Z" fill="url(#crystal)" stroke="#c4b4ff" stroke-width="2"/><path d="m121 31 18 92-68 15 63 66-1-81 55-12-67-80Z" fill="#bba9ff" opacity=".17"/><circle cx="122" cy="124" r="17" fill="#a8fff0" opacity=".8"/><circle cx="122" cy="124" r="31" stroke="#a8fff0" opacity=".42"/><path d="M121 4v20m0 202v18M14 126h21m174 0h22" stroke="#9cebd9" opacity=".6"/><defs><linearGradient id="crystal" x1="48" y1="43" x2="192" y2="204"><stop stop-color="#987eff" stop-opacity=".32"/><stop offset="1" stop-color="#57e1ce" stop-opacity=".1"/></linearGradient></defs></svg></div><div class="home-aside"><div class="glass quote-card"><div class="eyebrow">FROM THE ARCHIVE · 01</div><div class="quote-orb">〝</div><blockquote>“Every creature is a note. A world is only ever one song away from healing.”</blockquote><div class="byline">— Liora Venn, First Conductor</div></div><div class="stat-row"><div class="stat-tile"><span>Resonants</span><strong>18</strong><span>known forms</span></div><div class="stat-tile"><span>Regions</span><strong>04</strong><span>to harmonize</span></div><div class="stat-tile"><span>Style</span><strong>∞</strong><span>your ensemble</span></div></div></div></div></section>
<section id="screen-choose" class="screen"><div class="choose-head"><div><div class="eyebrow">Your first resonance · 01 / 03</div><h1 class="display" style="font-size:38px">Choose your <span class="gradient-text">first note.</span></h1><p class="lead">A good ensemble starts with a voice you understand. Your first Resonant will grow alongside you.</p></div></div><div class="choose-grid" id="starterChoices"></div><div style="margin-top:18px"><button class="btn btn-quiet btn-small" onclick="showScreen('menu')">← Back</button></div></section>
<section id="screen-map" class="screen"><div class="page-head"><div><div class="eyebrow">Conductor field journal · <span id="rankLabel">Novice</span></div><h1 class="display" style="font-size:35px;margin:9px 0">The living <span class="gradient-text">world.</span></h1><p class="lead">Follow the old signal path. Each node holds a test, a secret, or a new voice waiting to be heard.</p></div><div class="flex gap-2 flex-wrap"><button class="nav-btn" onclick="showScreen('team')">◈ Ensemble</button><button class="nav-btn" onclick="showScreen('shop')">✧ Camp shop</button><button class="nav-btn" onclick="returnScreen='map';showScreen('dex')">⌁ Atlas</button></div></div><div class="zone-layout"><div class="glass zone-main"><div class="zone-heading"><div><div class="eyebrow" id="zoneKicker">REGION 01</div><h2 id="zoneName">Verdant Canopy</h2><div class="zone-desc" id="zoneDesc"></div></div><svg class="zone-art" viewBox="0 0 180 140" fill="none"><path d="M21 115 45 43l21 29 24-56 26 60 18-26 28 65H21Z" fill="#73d6b34d" stroke="#83e7c8" opacity=".8"/><path d="M88 16v99M46 45l42 36m27-4L88 92" stroke="#b2a4ff" opacity=".8"/><circle cx="88" cy="17" r="7" fill="#dfceff"/><circle cx="35" cy="105" r="3" fill="#f4ce78"/><circle cx="151" cy="84" r="4" fill="#70dff1"/></svg></div><div class="zone-nav" id="zoneNav"></div><div class="map-trail" id="mapTrail"><svg class="trail-svg" id="trailSvg" preserveAspectRatio="none"><path id="trailPath" d=""/></svg></div></div><aside class="zone-sidebar"><div class="glass side-card"><div class="flex justify-between items-center"><h3>Field status</h3><span class="eyebrow" id="zoneProgressText">0 / 0</span></div><div class="progress-line"><i id="zoneProgressBar" style="width:0%"></i></div><div class="muted text-xs" id="progressCaption">Complete the trail to tune this region.</div><div class="zone-nav" style="margin-top:14px"><span class="zone-chip">✦ Gatekeeper marks</span><span class="zone-chip">✧ Caches</span></div></div><div class="glass side-card"><div class="flex justify-between items-center mb-3"><h3 style="margin:0">Active ensemble</h3><button class="btn btn-quiet btn-small" onclick="showScreen('team')">Manage</button></div><div class="party-mini" id="partyMini"></div></div><div class="glass side-card"><h3>Conductor's note</h3><div class="muted text-xs leading-relaxed" id="fieldNote">A soft green pulse travels beneath the canopy. Follow it.</div><div class="zone-nav"><span class="zone-chip">⌖ Aether Prism attuned</span></div></div></aside></div></section>
<section id="screen-battle" class="screen"><div class="battle-layout"><div class="glass battle-stage" id="battleStage"><div class="battle-top"><button class="btn btn-quiet btn-small" onclick="confirmRetreat()">← Retreat</button><div class="battle-label" id="battleContext">WILD RESONANCE</div><div class="battle-label" id="battleRound">ROUND 01</div></div><div class="battle-arena"><div class="fighter enemy"><div class="fighter-info" id="enemyInfo"></div><div class="sprite" id="enemySprite"></div></div><div class="fighter player"><div class="sprite" id="playerSprite"></div><div class="fighter-info" id="playerInfo"></div></div></div><div class="battle-log" id="battleLog">The air begins to hum.</div></div><div class="glass battle-controls"><div class="flex justify-between items-center"><h3>Choose a resonance</h3><span class="eyebrow" id="turnHint">YOUR TURN</span></div><div class="move-list" id="moveList"></div><div class="battle-tools"><button class="tool-btn" onclick="openSwitch()">⇄<br>Exchange</button><button class="tool-btn" onclick="openBattleItems()">▧<br>Inventory</button><button class="tool-btn" onclick="tryCapture()" id="captureButton">◇<br>Prism</button></div><div class="muted text-xs mt-3" id="battleTip">Read the opposing element and tune your move.</div></div></div></section>
<section id="screen-dex" class="screen"><div class="page-head"><div><div class="eyebrow">Field encyclopedia · 18 known harmonics</div><h1 class="display" style="font-size:35px;margin:9px 0">The <span class="gradient-text">Resonant atlas.</span></h1><p class="lead">Six elemental lineages, three forms each. Recorded forms glow when welcomed into your ensemble.</p></div><button class="btn btn-quiet" onclick="backFromDex()">← Return</button></div><div class="dex-grid" id="dexGrid"></div></section>
<section id="screen-shop" class="screen"><div class="page-head"><div><div class="eyebrow">Outpost · The humming kettle</div><h1 class="display" style="font-size:35px;margin:9px 0">Camp <span class="gradient-text">provisions.</span></h1><p class="lead">A good Conductor plans for the silence between songs.</p></div><button class="btn btn-quiet" onclick="showScreen('map')">← Trail</button></div><div class="shop-grid" id="shopGrid"></div><div class="glass" style="padding:18px;margin-top:16px;display:flex;justify-content:space-between;align-items:center;gap:12px"><div><b style="font-size:12px">Rest at camp</b><div class="muted text-xs mt-1">Restore your full ensemble and clear lingering status.</div></div><button class="btn btn-primary btn-small" onclick="restAtCamp()">Rest · 18 shards</button></div></section>
<section id="screen-team" class="screen"><div class="page-head"><div><div class="eyebrow">Conductor's ensemble · up to 3 active</div><h1 class="display" style="font-size:35px;margin:9px 0">Your <span class="gradient-text">resonants.</span></h1><p class="lead">The first conscious member leads each encounter. Arrange your voices with care.</p></div><button class="btn btn-quiet" onclick="showScreen('map')">← Trail</button></div><div class="team-grid" id="teamGrid"></div></section>
<div class="modal-back" id="modalBack"><div class="glass modal" id="modalContent"></div></div><div class="toast" id="toast"></div><div class="evo-banner" id="evoBanner"><div class="evo-inner"><div class="eyebrow">A new harmony takes shape</div><div id="evoSprite"></div><h2 id="evoName"></h2><p class="muted">Your Resonant has evolved.</p><button class="btn btn-primary" onclick="closeEvolution()">Beautifully done</button></div></div>
<script>
'use strict';
const TYPES={Pyro:{color:'#ff947a',icon:'♨'},Hydro:{color:'#70cfff',icon:'◒'},Flora:{color:'#7be29b',icon:'❋'},Zephyr:{color:'#b5a0ff',icon:'〰'},Earth:{color:'#dbb780',icon:'⬟'},Volt:{color:'#ffe27a',icon:'ϟ'},Cosmos:{color:'#d8b6ff',icon:'✧'}};
const ADV={Pyro:{Flora:1.5,Hydro:.7},Hydro:{Pyro:1.5,Flora:.7},Flora:{Hydro:1.5,Pyro:.7},Zephyr:{Earth:1.5,Volt:.7},Earth:{Volt:1.5,Zephyr:.7},Volt:{Zephyr:1.5,Earth:.7},Cosmos:{}};
const MOVES={"Spark Nip":{power:35,accuracy:98,element:'Pyro',unlock:1},"Cinder Pounce":{power:52,accuracy:94,element:'Pyro',unlock:4},"Flare Howl":{power:72,accuracy:88,element:'Pyro',unlock:9},"Solar Rupture":{power:94,accuracy:84,element:'Pyro',unlock:15},"Bubble Pop":{power:36,accuracy:100,element:'Hydro',unlock:1},"Tidal Arc":{power:55,accuracy:94,element:'Hydro',unlock:4},"Riptide Lance":{power:74,accuracy:88,element:'Hydro',unlock:9},"Abyssal Crown":{power:96,accuracy:83,element:'Hydro',unlock:15},"Leaf Flick":{power:35,accuracy:100,element:'Flora',unlock:1},"Bramble Lash":{power:53,accuracy:94,element:'Flora',unlock:4},"Canopy Crush":{power:74,accuracy:88,element:'Flora',unlock:9},"Verdant Cataclysm":{power:94,accuracy:84,element:'Flora',unlock:15},"Gust Needle":{power:36,accuracy:100,element:'Zephyr',unlock:1},"Crosswind":{power:54,accuracy:95,element:'Zephyr',unlock:4},"Jetstream Dive":{power:73,accuracy:89,element:'Zephyr',unlock:9},"Skybreak Tempest":{power:93,accuracy:84,element:'Zephyr',unlock:15},"Stone Tap":{power:37,accuracy:98,element:'Earth',unlock:1},"Faultline":{power:56,accuracy:92,element:'Earth',unlock:4},"Obsidian Edge":{power:75,accuracy:87,element:'Earth',unlock:9},"Worldshaker":{power:96,accuracy:82,element:'Earth',unlock:15},"Static Prick":{power:36,accuracy:100,element:'Volt',unlock:1},"Arc Jolt":{power:54,accuracy:95,element:'Volt',unlock:4},"Thunder Drive":{power:74,accuracy:88,element:'Volt',unlock:9},"Stormcall":{power:95,accuracy:83,element:'Volt',unlock:15}};
const DB=[
{id:'emberkit',name:'Emberkit',type:'Pyro',tier:1,base:{hp:42,atk:13,def:9,spd:13},evolveAt:8,moves:['Spark Nip','Cinder Pounce','Flare Howl','Solar Rupture'],shape:'cat',hue:18,desc:'A curious cinder-spirit whose tail keeps the warmth of a vanished star.'},
{id:'pyrowolf',name:'Pyrowolf',type:'Pyro',tier:2,base:{hp:59,atk:19,def:13,spd:18},evolveAt:15,moves:['Spark Nip','Cinder Pounce','Flare Howl','Solar Rupture'],shape:'wolf',hue:10,desc:'A swift flame-runner. Its pawprints glow until morning.'},
{id:'ignisaur',name:'Ignisaur',type:'Pyro',tier:3,base:{hp:79,atk:25,def:18,spd:20},evolveAt:null,moves:['Spark Nip','Cinder Pounce','Flare Howl','Solar Rupture'],shape:'dragon',hue:5,desc:'An ancient furnace-heart, bright enough to guide lost caravans.'},
{id:'droplet',name:'Droplet',type:'Hydro',tier:1,base:{hp:46,atk:10,def:12,spd:11},evolveAt:8,moves:['Bubble Pop','Tidal Arc','Riptide Lance','Abyssal Crown'],shape:'otter',hue:196,desc:'A playful pool-spirit that carries tiny reflections of the moon.'},
{id:'geoxus',name:'Geoxus',type:'Hydro',tier:2,base:{hp:66,atk:15,def:18,spd:13},evolveAt:15,moves:['Bubble Pop','Tidal Arc','Riptide Lance','Abyssal Crown'],shape:'turtle',hue:202,desc:'A river guardian with a shell carved by centuries of current.'},
{id:'neptunian',name:'Neptunian',type:'Hydro',tier:3,base:{hp:83,atk:21,def:22,spd:19},evolveAt:null,moves:['Bubble Pop','Tidal Arc','Riptide Lance','Abyssal Crown'],shape:'serpent',hue:190,desc:'A luminous deepwater sovereign, bearing the hush of the abyss.'},
{id:'seedling',name:'Seedling',type:'Flora',tier:1,base:{hp:44,atk:11,def:12,spd:10},evolveAt:8,moves:['Leaf Flick','Bramble Lash','Canopy Crush','Verdant Cataclysm'],shape:'sprout',hue:126,desc:'A small green hum in a seed-shell, forever looking for sunlight.'},
{id:'thornback',name:'Thornback',type:'Flora',tier:2,base:{hp:67,atk:18,def:19,spd:10},evolveAt:15,moves:['Leaf Flick','Bramble Lash','Canopy Crush','Verdant Cataclysm'],shape:'stag',hue:112,desc:'A patient woodland sentinel with antlers of living briar.'},
{id:'bloomgoliath',name:'Bloomgoliath',type:'Flora',tier:3,base:{hp:91,atk:22,def:25,spd:12},evolveAt:null,moves:['Leaf Flick','Bramble Lash','Canopy Crush','Verdant Cataclysm'],shape:'golem',hue:137,desc:'An immense garden spirit; blossoms unfold in its slow footsteps.'},
{id:'sparky',name:'Sparky',type:'Volt',tier:1,base:{hp:39,atk:14,def:8,spd:17},evolveAt:8,moves:['Static Prick','Arc Jolt','Thunder Drive','Stormcall'],shape:'fox',hue:49,desc:'A bright little voltage, quick enough to outrun its own sparks.'},
{id:'boltsaber',name:'Boltsaber',type:'Volt',tier:2,base:{hp:58,atk:21,def:12,spd:21},evolveAt:15,moves:['Static Prick','Arc Jolt','Thunder Drive','Stormcall'],shape:'saber',hue:43,desc:'A blade-tailed duelist that cuts the air into clean blue arcs.'},
{id:'stormlord',name:'Stormlord',type:'Volt',tier:3,base:{hp:75,atk:28,def:17,spd:25},evolveAt:null,moves:['Static Prick','Arc Jolt','Thunder Drive','Stormcall'],shape:'lion',hue:52,desc:'A sovereign storm given form, crowned in crackling aurora.'},
{id:'gusty',name:'Gusty',type:'Zephyr',tier:1,base:{hp:40,atk:11,def:9,spd:18},evolveAt:8,moves:['Gust Needle','Crosswind','Jetstream Dive','Skybreak Tempest'],shape:'bird',hue:267,desc:'A feathered wisp that sleeps on the backs of clouds.'},
{id:'slipstream',name:'Slipstream',type:'Zephyr',tier:2,base:{hp:57,atk:17,def:13,spd:23},evolveAt:15,moves:['Gust Needle','Crosswind','Jetstream Dive','Skybreak Tempest'],shape:'manta',hue:252,desc:'A sky-swimmer whose wake leaves ribbons of violet light.'},
{id:'zephyrus',name:'Zephyrus',type:'Zephyr',tier:3,base:{hp:78,atk:22,def:17,spd:28},evolveAt:null,moves:['Gust Needle','Crosswind','Jetstream Dive','Skybreak Tempest'],shape:'griffin',hue:273,desc:'An exalted wind-spirit, carrying the first breath of every dawn.'},
{id:'pebble',name:'Pebble',type:'Earth',tier:1,base:{hp:49,atk:12,def:15,spd:7},evolveAt:8,moves:['Stone Tap','Faultline','Obsidian Edge','Worldshaker'],shape:'mole',hue:31,desc:'A shy pebblekin with a heart of warm, unpolished quartz.'},
{id:'obsidian',name:'Obsidian',type:'Earth',tier:2,base:{hp:70,atk:18,def:21,spd:8},evolveAt:15,moves:['Stone Tap','Faultline','Obsidian Edge','Worldshaker'],shape:'rhino',hue:20,desc:'A dark basalt bulwark, polished smooth by deep-earth rivers.'},
{id:'terrasunder',name:'Terrasunder',type:'Earth',tier:3,base:{hp:96,atk:24,def:27,spd:10},evolveAt:null,moves:['Stone Tap','Faultline','Obsidian Edge','Worldshaker'],shape:'titan',hue:28,desc:'A mountain awakened, its crystal core beating beneath continents.'}
];
const ZONES=[{name:'The Verdant Canopy',short:'Verdant Canopy',range:'Levels 1–5',desc:'A cathedral of leaves where old sunlight still sings in the moss. Flora and Zephyr harmonics drift along the canopy bridges.',note:'A soft green pulse travels beneath the canopy. Follow it.',levels:[2,3,4,5],nodes:[['wild','Tall grass'],['cache','Moss cache'],['trainer','Wandering Conductor'],['spring','Healing spring'],['wild','Whispering bough'],['trainer','Trail Conductor'],['wild','Moonlit clearing'],['boss','Canopy Warden']]},{name:'The Sunken Grotto',short:'Sunken Grotto',range:'Levels 5–10',desc:'A drowned city beneath a glass-clear lake. Hydro and Earth frequencies echo through the limestone vaults.',note:'The grotto answers in slow, tidal pulses. Keep your Prism steady.',levels:[5,6,8,9],nodes:[['wild','Reedwater bend'],['cache','Pearl cache'],['trainer','Diver Conductor'],['spring','Blue spring'],['wild','Silt garden'],['trainer','Echo Conductor'],['wild','The old aqueduct'],['boss','Grotto Keeper']]},{name:'The Obsidian Crags',short:'Obsidian Crags',range:'Levels 10–15',desc:'Volcanic shelves glow under a sky split by lightning. Pyro and Volt spirits race through the black glass.',note:'The crags are loud with power. Ground your signal before each encounter.',levels:[10,11,13,14],nodes:[['wild','Cinder shelf'],['cache','Glass cache'],['trainer','Forge Conductor'],['spring','Ashen spring'],['wild','Storm fissure'],['trainer','Ridge Conductor'],['wild','The black ascent'],['boss','Crag Sovereign']]},{name:'The Apex Pillar',short:'Apex Pillar',range:'Level 15+',desc:'At the world’s high axis, every elemental note converges. Climb the final gauntlet and restore Aethelgard’s first chord.',note:'There is no wind above the Pillar. Only the song you carried here.',levels:[15,16,17,18],nodes:[['wild','Threshold of sound'],['cache','Aether cache'],['trainer','Silent Conductor'],['spring','Stillpoint spring'],['wild','Choir of echoes'],['trainer','Last apprentice'],['wild','The tuning fork'],['boss','The First Resonance']]}];
const BOSSES=[{name:'Warden Sael',species:'thornback',level:6,dialog:'“The canopy does not yield. It listens. Show it what your heart is singing.”'},{name:'Keeper Neris',species:'geoxus',level:10,dialog:'“Every tide returns changed. Let the grotto hear how far you have come.”'},{name:'Sovereign Vark',species:'boltsaber',level:15,dialog:'“I have heard the mountain storm. I wonder if your small song can answer it.”'},{name:'The First Resonance',species:'zephyrus',level:19,dialog:'“You brought six voices through the silence. Now give them a reason to become one.”'}];
class Synth{constructor(){this.ctx=null;this.enabled=true}play(kind){if(!this.enabled)return;try{this.ctx ||= new (window.AudioContext||window.webkitAudioContext)();if(this.ctx.state==='suspended')this.ctx.resume();const c=this.ctx,now=c.currentTime;const patterns={hover:[660,.035,'sine'],select:[520,.08,'square'],physical:[150,.12,'triangle'],elemental:[740,.18,'sawtooth'],level:[880,.35,'square'],capture:[990,.5,'sine']};let [freq,dur,wave]=patterns[kind]||patterns.select;[0,1,2].slice(0,kind==='level'||kind==='capture'?3:1).forEach((_,i)=>{let o=c.createOscillator(),g=c.createGain();o.type=wave;o.frequency.setValueAtTime(freq*(i?1.25:1),now+i*.09);o.frequency.exponentialRampToValueAtTime(freq*.65,now+i*.09+dur);g.gain.setValueAtTime(.055,now+i*.09);g.gain.exponentialRampToValueAtTime(.001,now+i*.09+dur);o.connect(g);g.connect(c.destination);o.start(now+i*.09);o.stop(now+i*.09+dur)})}catch(e){}}}
const synth=new Synth();let state;let battle=null;let returnScreen='map';let toastTimer;let waiting=false;
function newProfile(){return {version:1,starterChosen:false,party:[],inventory:{harmonizer:5,hyper:1,elixir:2,cleanser:2},shards:55,unlockedZones:[0],activeZone:0,completed:{},cacheClaimed:{},bosses:[],seen:[],wins:0,trainerWins:0,created:Date.now()}}
function load(){try{let raw=localStorage.getItem('aethelgard-save');if(raw){let s=JSON.parse(raw);if(s&&s.version===1){state={...newProfile(),...s};state.inventory={...newProfile().inventory,...(s.inventory||{})};return}}}catch(e){}state=newProfile()}
function save(){if(!state)return;try{localStorage.setItem('aethelgard-save',JSON.stringify(state))}catch(e){}updateHeader()}
function updateHeader(){document.querySelector('#shardReadout b').textContent=state?.shards??0;document.getElementById('continueBtn').innerHTML=(state?.starterChosen?'Continue expedition':'Begin expedition')+' <span>↗</span>'}
function showScreen(name){if(name==='map'&&!state.starterChosen){toast('Choose your first Resonant before exploring.');renderStarters();name='choose'}document.querySelectorAll('.screen').forEach(s=>s.classList.remove('active'));let el=document.getElementById('screen-'+name);if(el){el.classList.add('active');if(name==='map')renderMap();if(name==='dex')renderDex();if(name==='shop')renderShop();if(name==='team')renderTeam()}window.scrollTo({top:0,behavior:'smooth'});synth.play('select')}
function continueGame(){if(!state.starterChosen){renderStarters();showScreen('choose')}else showScreen('map')}
function renderStarters(){let ids=['emberkit','droplet','seedling'];document.getElementById('starterChoices').innerHTML=ids.map(id=>{let d=species(id);return `<article class="glass starter-card" onclick="chooseStarter('${id}')"><div class="sprite">${sprite(d)}</div><div><span class="type-pill type-${d.type.toLowerCase()}">${TYPES[d.type].icon} ${d.type}</span><h3>${d.name}</h3><p>${d.desc}</p><div class="card-foot"><span class="muted text-xs">Balanced · level 3</span><span class="btn btn-primary btn-small">Choose →</span></div></div></article>`}).join('')}
function chooseStarter(id){let d=species(id);state.party=[makeMon(id,3)];state.starterChosen=true;state.seen.push(id);save();toast(`${d.name} joined your ensemble.`);showScreen('map')}
function species(id){return DB.find(x=>x.id===id)}function makeMon(id,level=1){let d=species(id),stats=calcStats(d,level);return {uid:Math.random().toString(36).slice(2,10),species:id,level,xp:0,hp:stats.hp,maxHp:stats.hp,status:null}}
function calcStats(d,l){let t=d.tier-1;return {hp:d.base.hp+Math.floor((l-1)*3.15)+t*2,atk:d.base.atk+Math.floor((l-1)*1.15)+t,def:d.base.def+Math.floor((l-1)*.92)+t,spd:d.base.spd+Math.floor((l-1)*.8)}}function stats(mon){return calcStats(species(mon.species),mon.level)}
function sprite(d){let c=TYPES[d.type].color,h=d.hue;let id='g'+d.id;let shapes={cat:'<path d="M48 91 35 60 57 72 66 51 81 69Q112 54 139 75L158 60 154 94Q168 110 155 132Q137 151 99 148Q61 150 47 131Q37 114 48 91Z"/><path d="M137 126q28-4 24 17-3 17-25 8" fill="none" stroke="currentColor" stroke-width="9" stroke-linecap="round"/>',wolf:'<path d="M39 88 26 53 57 69 71 45 86 69Q123 54 145 80L169 61 161 96Q174 119 151 139L105 151 61 141 42 118Z"/><path d="M143 130q22 4 19 20" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round"/>',dragon:'<path d="M34 102 19 64 58 76 77 42 94 71Q131 49 154 78L179 56 168 96 183 120 147 133 130 159 99 143 66 155 59 128 35 126Z"/><path d="m82 79 20 15-20 4m43-5 17 8-17 4" fill="none" stroke="currentColor" stroke-width="4"/>',otter:'<path d="M56 82Q59 55 95 58Q136 57 145 89L159 119Q152 147 111 151L70 143Q43 131 47 105Z"/><path d="M65 72 61 48 83 64m47 5 14-24 4 35"/>',turtle:'<path d="M38 108Q41 65 103 64Q154 67 157 109Q153 139 102 143Q49 142 38 108Z"/><path d="m59 86 42-19 39 23-8 34-43 11-36-20Zm43-18v66M60 89l74 35M140 91 61 121" fill="none" stroke="currentColor" stroke-width="4"/>',serpent:'<path d="M40 116Q48 76 91 91T147 82Q177 68 168 98Q159 122 127 112T77 131Q52 144 40 116Z"/><path d="m154 80 12-18 4 23"/>',sprout:'<path d="M65 125Q72 83 102 84Q134 85 140 125L130 151H71Z"/><path d="M101 91Q67 75 76 48Q104 47 107 81Q114 49 143 50Q146 79 109 94"/><path d="M75 114q25 17 52 0" fill="none" stroke="currentColor" stroke-width="4"/>',stag:'<path d="M58 96Q63 68 103 69Q146 69 149 103L139 137 67 137Z"/><path d="M75 82 55 39 58 73 35 57 62 91m67-9 19-43-3 34 23-16-26 35" fill="none" stroke="currentColor" stroke-width="7" stroke-linejoin="round"/>',golem:'<path d="m54 69 18-23 25 13 26-12 18 29 15 23-11 45-37 14-43-8-27-30Z"/><path d="m79 103 13 2m30-2 13-2M94 126q12 8 24-1" fill="none" stroke="currentColor" stroke-width="4"/>',fox:'<path d="m48 89-14-34 35 18 15-25 15 26q40-16 56 16l-7 44-46 19-43-20Z"/><path d="M139 130q32 1 29 22-3 14-24 8" fill="none" stroke="currentColor" stroke-width="9"/>',saber:'<path d="m43 91-13-37 35 17 19-24 18 27q41-13 58 17l-9 40-47 18-44-18Z"/><path d="m142 132 31 25-14-31 21-11-33 1" fill="none" stroke="currentColor" stroke-width="8"/>',lion:'<path d="M54 66 75 43l26 11 25-13 25 29 14 36-13 40-40 19-48-15-28-36Z"/><path d="M81 103h10m34 0h10m-39 22q13 11 26 0" fill="none" stroke="currentColor" stroke-width="4"/>',bird:'<path d="M39 115Q58 79 95 83Q118 49 154 64Q142 92 113 100Q91 130 39 115Z"/><path d="M92 91Q77 58 53 54Q61 83 80 98m55-26 25-7-16 19" fill="none" stroke="currentColor" stroke-width="7"/>',manta:'<path d="M27 105Q71 57 104 81Q137 54 174 89L148 105 162 142Q127 129 104 119Q80 141 45 144L61 115Z"/><path d="M101 84v42"/>',griffin:'<path d="M42 103Q54 69 91 77L110 57 130 76Q160 67 170 95L150 113 160 143 116 148 89 128 58 146Z"/><path d="m99 82-15-35 31 18m20 9 24-23-7 35" fill="none" stroke="currentColor" stroke-width="7"/>',mole:'<path d="M48 104Q48 67 97 68Q146 68 151 105L133 141 71 144Z"/><path d="m57 93-22 15 22 7m84-22 22 15-21 7"/><path d="M85 103h8m24 0h8" stroke="currentColor" stroke-width="5"/>',rhino:'<path d="M40 89Q54 61 110 69L145 84 163 91 151 110 155 140 94 146 60 133 41 115Z"/><path d="m150 87 24-19-7 30"/><path d="M68 131v18m61-18v18" stroke="currentColor" stroke-width="8"/>',titan:'<path d="m46 76 27-30 31 17 28-18 30 34 15 39-26 43-46 5-28-20-40 9-16-39Z"/><path d="m82 103 13 3m31-3 13-3m-44 26q14 10 28 0" fill="none" stroke="currentColor" stroke-width="5"/>'};let body=shapes[d.shape]||shapes.cat;return `<svg viewBox="0 0 200 190" xmlns="http://www.w3.org/2000/svg" aria-label="${d.name}" style="color:${c}"><defs><linearGradient id="${id}" x1=".1" y1="0" x2=".9" y2="1"><stop stop-color="${c}"/><stop offset="1" stop-color="hsl(${h} 68% 36%)"/></linearGradient><radialGradient id="gl${id}"><stop stop-color="${c}" stop-opacity=".38"/><stop offset="1" stop-color="${c}" stop-opacity="0"/></radialGradient></defs><ellipse cx="101" cy="162" rx="65" ry="18" fill="url(#gl${id})"/><g fill="url(#${id})" stroke="${c}" stroke-width="2.5" stroke-linejoin="round">${body}</g><path d="M55 86Q90 63 137 80" stroke="#fff" stroke-opacity=".25" stroke-width="4" fill="none"/><ellipse cx="85" cy="103" rx="5" ry="7" fill="#111529"/><ellipse cx="125" cy="102" rx="5" ry="7" fill="#111529"/><circle cx="86" cy="101" r="1.5" fill="white"/><circle cx="126" cy="100" r="1.5" fill="white"/><path d="M99 117q5 5 10 0" fill="none" stroke="#20233a" stroke-width="2" stroke-linecap="round"/><path d="M72 118q-10 7-16 1m75 1q9 6 16 0" fill="none" stroke="#fff" stroke-opacity=".23" stroke-width="2"/></svg>`}
function typeColor(t){return TYPES[t]?.color||'#fff'}function renderMap(){if(!state.starterChosen){continueGame();return}let zi=state.activeZone||0,z=ZONES[zi];document.getElementById('zoneKicker').textContent=`REGION 0${zi+1} · ${z.range.toUpperCase()}`;document.getElementById('zoneName').textContent=z.name;document.getElementById('zoneDesc').textContent=z.desc;document.getElementById('fieldNote').textContent=z.note;document.getElementById('rankLabel').textContent=zi===0?'Novice Conductor':zi===1?'Trail Conductor':zi===2?'Resonance Keeper':'Apex Conductor';document.getElementById('zoneNav').innerHTML=ZONES.map((x,i)=>`<button class="zone-chip ${i===zi?'current':''} ${state.unlockedZones.includes(i)?'':'locked'}" ${state.unlockedZones.includes(i)?`onclick="setZone(${i})"`:''}>0${i+1} · ${x.short}</button>`).join('');let doneCount=z.nodes.filter((_,i)=>state.completed[`${zi}-${i}`]).length;if(state.bosses.includes(zi)&&!state.completed[`${zi}-7`]){state.completed[`${zi}-7`]=true;doneCount++}document.getElementById('zoneProgressText').textContent=`${doneCount} / ${z.nodes.length}`;document.getElementById('zoneProgressBar').style.width=(doneCount/z.nodes.length*100)+'%';document.getElementById('progressCaption').textContent=state.bosses.includes(zi)?'Gatekeeper defeated — region harmonized.':`Complete ${z.nodes.length-doneCount} remaining trail ${z.nodes.length-doneCount===1?'node':'nodes'} to harmonize this region.`;let trail=document.getElementById('mapTrail');trail.innerHTML='<svg class="trail-svg" id="trailSvg" preserveAspectRatio="none"><path id="trailPath" d=""/></svg>'+z.nodes.map((n,i)=>{let completed=!!state.completed[`${zi}-${i}`],prev=i===0||!!state.completed[`${zi}-${i-1}`],unlocked=prev;let icons={wild:'✧',cache:'▣',trainer:'♙',spring:'⌁',boss:'♛'};return `<div class="node-wrap"><button class="node ${completed?'done':''}" ${unlocked?`onclick="enterNode(${zi},${i})"`:'disabled'} title="${n[1]}"><span class="node-icon">${icons[n[0]]}</span><small>${n[1]}</small>${completed?'<span class="node-flag">✓</span>':''}</button></div>`}).join('');drawTrail();document.getElementById('partyMini').innerHTML=state.party.map((m,i)=>{let d=species(m.species);return `<div class="party-row"><div class="mini-sprite">${sprite(d)}</div><div style="flex:1"><strong>${d.name} <span class="muted">Lv ${m.level}</span></strong><small>${d.type}${m.status?` · ${m.status}`:''}</small><div class="mini-hp"><i style="width:${m.hp/m.maxHp*100}%"></i></div></div></div>`}).join('')}
function drawTrail(){let path=document.getElementById('trailPath'),wrap=document.getElementById('mapTrail');if(!path||!wrap)return;let nodes=[...wrap.querySelectorAll('.node-wrap')],r=wrap.getBoundingClientRect();if(!r.width)return;let pts=nodes.map(e=>{let b=e.getBoundingClientRect();return `${b.left-r.left+b.width/2},${b.top-r.top+b.height/2}`});path.setAttribute('d','M'+pts.join(' L'))}window.addEventListener('resize',()=>{if(document.getElementById('screen-map').classList.contains('active'))drawTrail()});function setZone(i){state.activeZone=i;save();renderMap();synth.play('select')}
function enterNode(zi,i){if(zi!==state.activeZone)return;let [kind,label]=ZONES[zi].nodes[i],key=`${zi}-${i}`;if(kind==='cache'){if(state.cacheClaimed[key]){toast('This cache has already been tuned.');return}state.cacheClaimed[key]=true;state.completed[key]=true;let shards=22+zi*7;state.shards+=shards;let prize=zi>1?'hyper':'harmonizer';state.inventory[prize]=(state.inventory[prize]||0)+1;save();toast(`Cache opened · ${shards} shards and a Prism.`);renderMap();return}if(kind==='spring'){state.completed[key]=true;state.party.forEach(m=>{m.hp=m.maxHp;m.status=null});save();toast('The spring restores your entire ensemble.');renderMap();return}startBattle(zi,i,kind)}
function encounterFor(zi,i,kind){if(kind==='boss'){let b=BOSSES[zi];return {id:b.species,level:b.level,trainer:b.name,boss:true,dialog:b.dialog,reward:42+zi*25}}let level=ZONES[zi].levels[(i+zi)%ZONES[zi].levels.length];if(kind==='trainer'){let trainers=[['Mira of the Moss','gusty'],['Tovan Reed','pebble'],['Sela Tideglass','droplet'],['Orrin the Listener','seedling'],['Kestrel Vale','slipstream'],['Ari of the Forge','emberkit']];let t=trainers[(i+zi*2)%trainers.length];return {id:t[1],level:level+1,trainer:t[0],reward:20+zi*13}}let choices=zi===0?['seedling','gusty','emberkit','pebble']:zi===1?['droplet','pebble','seedling','gusty']:zi===2?['emberkit','sparky','boltsaber','droplet']:['ignisaur','neptunian','zephyrus','terrasunder','stormlord','bloomgoliath'];let id=choices[(i*3+zi+Math.floor(Math.random()*choices.length))%choices.length];return {id,level,trainer:null,reward:12+zi*9}}
function startBattle(zi,i,kind){let enc=encounterFor(zi,i,kind),key=`${zi}-${i}`;if(kind==='boss'&&enc.dialog){modal(`${enc.trainer} · Gatekeeper`,enc.dialog,`<button class="btn btn-primary" onclick="closeModal();launch(encounterFor(${zi},${i},'${kind}'),${zi},${i},'${key}','${kind}')">Begin the duel</button><button class="btn btn-quiet" onclick="closeModal()">Not yet</button>`,null,true)}else launch(enc,zi,i,key,kind)}function launch(enc,zi,i,key,kind){let lead=state.party.find(m=>m.hp>0);if(!lead){toast('Your ensemble needs a rest. Visit the camp shop.');showScreen('shop');return}battle={zone:zi,node:i,key,kind,enemy:makeMon(enc.id,enc.level),trainer:enc.trainer,boss:!!enc.boss,reward:enc.reward,dialog:enc.dialog,round:1,turnLock:false};state.seen.push(enc.id);save();renderBattle();showScreen('battle')}
function currentMon(){return state.party.find(m=>m.hp>0)}function availableMoves(mon){return species(mon.species).moves.map(n=>({name:n,...MOVES[n]})).filter(m=>m.unlock<=mon.level).slice(-4)}function renderBattle(){if(!battle)return;let p=currentMon(),e=battle.enemy,ed=species(e.species),pd=species(p.species);document.getElementById('battleContext').textContent=battle.boss?'GATEKEEPER DUEL':battle.trainer?'CONDUCTOR DUEL':'WILD RESONANCE';document.getElementById('battleRound').textContent=`ROUND ${String(battle.round).padStart(2,'0')}`;document.getElementById('enemySprite').innerHTML=sprite(ed);document.getElementById('playerSprite').innerHTML=sprite(pd);document.getElementById('enemyInfo').innerHTML=fighterInfo(ed,e,false);document.getElementById('playerInfo').innerHTML=fighterInfo(pd,p,true);document.getElementById('moveList').innerHTML=availableMoves(p).map(m=>`<button class="move-btn" onclick="playerMove('${m.name}')"><strong>${m.name}</strong><small><span style="color:${typeColor(m.element)}">${m.element}</span> · POW ${m.power} · ${m.accuracy}%</small></button>`).join('');document.getElementById('battleLog').textContent=battle.message||`${battle.trainer?battle.trainer+' challenges you.':'A wild '+ed.name+' emerged from the harmonic field.'}`;document.getElementById('captureButton').disabled=!!battle.trainer;document.getElementById('captureButton').style.opacity=battle.trainer?'.4':'1';document.getElementById('battleTip').textContent=battle.trainer?'Conductors protect their Resonants; capture is unavailable.':`Prisms in satchel: ${state.inventory.harmonizer||0} basic · ${state.inventory.hyper||0} hyper.`}
function fighterInfo(d,m,player){let pct=Math.max(0,m.hp/m.maxHp*100),xp=player?` · ${m.xp}/${xpNeed(m.level)} XP`:'';return `<div class="fighter-name"><span>${d.name}${m.status?` <i class="status">${m.status}</i>`:''}</span><small>LV ${m.level}</small></div><div class="hp-track"><div class="hp-fill ${pct<28?'hp-low':''}" style="width:${pct}%"></div></div><div class="hp-meta"><span>${m.hp} / ${m.maxHp} HP</span><span>${player?xp:d.type}</span></div>`}
function setBattleMessage(s){battle.message=s;document.getElementById('battleLog').textContent=s}
function playerMove(name){if(!battle||battle.turnLock)return;let p=currentMon(),e=battle.enemy,m=availableMoves(p).find(x=>x.name===name);if(!m)return;battle.turnLock=true;synth.play('select');if(Math.random()*100>m.accuracy){setBattleMessage(`${species(p.species).name}'s ${m.name} missed the frequency!`);setTimeout(enemyTurn,650);return}let mod=(ADV[m.element]?.[species(e.species).type]||1)*(species(p.species).type===m.element?1.18:1);let critical=Math.random()<.1;let dmg=damage(p,e,m,mod,critical);e.hp=Math.max(0,e.hp-dmg);let superText=mod>1.2?' It struck a resonant weakness!':mod<.9?' The hit was resisted.':'';setBattleMessage(`${species(p.species).name} used ${m.name} for ${dmg} damage.${critical?' A critical harmony!':''}${superText}`);synth.play(mod>1.2?'elemental':'physical');hitFX('enemy',critical);if(e.hp<=0){setTimeout(victory,620);return}setTimeout(enemyTurn,700)}
function damage(att,def,m,mod,crit){let a=stats(att),d=stats(def);return Math.max(1,Math.floor((((2*att.level/5+2)*m.power*(a.atk/d.def))/50+2)*mod*(.85+Math.random()*.15)*(crit?1.5:1)))}
function enemyTurn(){if(!battle)return;let p=currentMon(),e=battle.enemy;if(!p){defeat();return}if(e.status==='burn'){let tick=Math.max(2,Math.floor(e.maxHp*.06));e.hp=Math.max(0,e.hp-tick);setBattleMessage(`${species(e.species).name} loses ${tick} HP to the lingering burn.`);if(e.hp<=0){renderBattle();setTimeout(victory,400);return}}if(p.status==='burn'){let tick=Math.max(2,Math.floor(p.maxHp*.06));p.hp=Math.max(0,p.hp-tick);setBattleMessage(`${species(p.species).name} takes ${tick} burn damage.`);if(p.hp<=0){if(!currentMon()){renderBattle();setTimeout(defeat,400);return}p=currentMon();setBattleMessage(`${species(p.species).name} steps in to carry the melody.`)}}let moves=availableMoves(e),m=moves[Math.floor(Math.random()*moves.length)]||{name:'Resonant pulse',power:28,accuracy:95,element:species(e.species).type};if(Math.random()*100>m.accuracy){setBattleMessage(`${species(e.species).name}'s ${m.name} slipped past your guard.`)}else{let mod=(ADV[m.element]?.[species(p.species).type]||1)*(species(e.species).type===m.element?1.18:1),critical=Math.random()<.1,dmg=damage(e,p,m,mod,critical);p.hp=Math.max(0,p.hp-dmg);setBattleMessage(`${species(e.species).name} used ${m.name} for ${dmg} damage.${critical?' Critical hit!':''}${mod>1.2?' A super-effective strike!':''}`);synth.play(mod>1.2?'elemental':'physical');hitFX('player',critical);if(p.hp<=0){let next=currentMon();if(!next){renderBattle();setTimeout(defeat,500);return}setBattleMessage(`${species(p.species).name} falters. ${species(next.species).name} takes the lead.`)}}if(battle&&battle.kind!=='boss'&&Math.random()<.12&¤tMon()&&species(e.species).type==='Pyro')currentMon().status='burn';battle.round++;battle.turnLock=false;save();renderBattle()}
function hitFX(side,critical){let el=document.getElementById('battleStage');if(critical)el.classList.add('shake');let target=document.getElementById(side==='player'?'playerSprite':'enemySprite');target.classList.add('flash');for(let i=0;i<5;i++){let p=document.createElement('i');p.className='particle';p.style.left=(30+Math.random()*40)+'%';p.style.top=(35+Math.random()*30)+'%';p.style.setProperty('--dx',`${Math.random()*90-45}px`);p.style.color=side==='player'?'#ff8b82':'#8fe9ce';el.append(p);setTimeout(()=>p.remove(),1700)}setTimeout(()=>{el.classList.remove('shake');target.classList.remove('flash')},450)}
function xpNeed(level){return level*level*9}function awardXP(amount){let messages=[];state.party.forEach(mon=>{if(mon.hp<=0)return;mon.xp+=amount;while(mon.xp>=xpNeed(mon.level)&&mon.level<25){mon.xp-=xpNeed(mon.level);mon.level++;let old=mon.maxHp,st=stats(mon);mon.maxHp=st.hp;mon.hp=Math.min(mon.maxHp,mon.hp+(mon.maxHp-old));messages.push(`${species(mon.species).name} reached level ${mon.level}!`);synth.play('level');let oldd=species(mon.species);if(oldd.evolveAt&&mon.level>=oldd.evolveAt){let next=DB.find(x=>x.type===oldd.type&&x.tier===oldd.tier+1);if(next){mon.species=next.id;let ns=stats(mon);mon.maxHp=ns.hp;mon.hp=Math.min(ns.hp,mon.hp+8);messages.push(`Evolution: ${oldd.name} → ${next.name}!`);showEvolution(next)}}}});save();return messages}
function victory(){if(!battle)return;let b=battle,e=species(b.enemy.species);state.wins++;if(b.trainer)state.trainerWins++;let shards=b.reward;state.shards+=shards;let xp=12+b.enemy.level*7+(b.boss?22:0);let msgs=awardXP(xp);if(b.kind==='boss'){state.bosses.push(b.zone);if(b.zone<3&&!state.unlockedZones.includes(b.zone+1))state.unlockedZones.push(b.zone+1);state.completed[b.key]=true}else if(b.kind==='trainer'||b.kind==='wild')state.completed[b.key]=true;save();setBattleMessage(`${e.name} harmonizes and withdraws. +${xp} XP · +${shards} shards.`);battle.turnLock=true;setTimeout(()=>{battle=null;showScreen('map');toast(msgs.length?msgs.join(' '):`Victory · +${shards} Aether Shards`);renderMap();if(b.kind==='boss'&&b.zone===3)setTimeout(()=>modal('Aethelgard remembers','The First Resonance joins your song. Across the four regions, lost frequencies bloom again. Your journey is complete — and the world still holds more Resonants to discover.','Continue exploring',closeModal),500)},750)}
function defeat(){if(!battle)return;state.shards=Math.max(0,state.shards-8);state.party.forEach(m=>{m.hp=Math.max(1,Math.floor(m.maxHp*.25));m.status=null});save();battle=null;showScreen('map');toast('Your ensemble retreated to camp. 8 shards lost; the spring restored your strength.');renderMap()}
function tryCapture(){if(!battle||battle.turnLock||battle.trainer)return;if(state.party.length>=3){toast('Your active ensemble is full. Make room before capturing another Resonant.');return}let tier=state.inventory.hyper>0?'hyper':'harmonizer';if(!state.inventory[tier]){toast('No Aether Prisms remain. Visit the Camp Shop.');return}state.inventory[tier]--;battle.turnLock=true;let e=battle.enemy,remaining=e.hp/e.maxHp,rate=(tier==='hyper'?.57:.34)+(1-remaining)*.55+(e.status?.16:0);rate=Math.min(.94,rate);if(Math.random()<rate){let id=e.species,d=species(id);if(state.party.length<3){let mon=makeMon(id,e.level);mon.hp=e.hp;state.party.push(mon)}else{state.party.push(makeMon(id,e.level))}state.seen.push(id);state.shards+=Math.floor(battle.reward*.5);synth.play('capture');save();setBattleMessage(`${d.name} settles safely inside your Aether Prism!`);setTimeout(()=>{battle=null;showScreen('map');toast(`${d.name} joined your ensemble · +${Math.floor(0)} shards`);renderMap()},800)}else{setBattleMessage(`The Prism glows, then scatters. ${species(e.species).name} resists the harmony!`);synth.play('elemental');setTimeout(enemyTurn,700)}renderBattle()}
function openSwitch(){if(!battle||battle.turnLock)return;let options=state.party.map((m,i)=>({m,i})).filter(x=>x.m.hp>0&&x.m!==currentMon());if(!options.length){toast('No conscious Resonant can exchange.');return}modal('Exchange Resonant','Choose a conscious voice to lead the next turn.',options.map(x=>`<button class="btn btn-quiet" onclick="switchTo(${x.i})">${species(x.m.species).name} · Lv ${x.m.level} (${x.m.hp} HP)</button>`).join('')+'<button class="btn btn-quiet" onclick="closeModal()">Cancel</button>',null,null,true)}function switchTo(i){closeModal();let m=state.party.splice(i,1)[0];state.party.unshift(m);save();setBattleMessage(`${species(m.species).name} steps forward. The wild Resonant takes its turn!`);battle.turnLock=true;renderBattle();setTimeout(enemyTurn,650)}
function openBattleItems(){if(!battle||battle.turnLock)return;let items=[['elixir','Core Restorer','Restore 50% of one Resonant’s max HP'],['cleanser','Resonance Elixir','Clear all status effects']];modal('Ensemble inventory','Choose an item to use. Using an item gives the opponent a turn.',items.map(([id,n,d])=>`<button class="btn btn-quiet" onclick="useBattleItem('${id}')">${n} · ${state.inventory[id]||0}<small class="muted"> — ${d}</small></button>`).join('')+'<button class="btn btn-quiet" onclick="closeModal()">Cancel</button>',null,null,true)}function useBattleItem(id){if(!state.inventory[id]){toast('You do not have any of those.');return}let p=currentMon();if(id==='elixir'){if(p.hp>=p.maxHp){toast('Already at full health.');return}let heal=Math.min(Math.ceil(p.maxHp*.5),p.maxHp-p.hp);p.hp+=heal;setBattleMessage(`${species(p.species).name} restored ${heal} HP.`)}else{if(!state.party.some(m=>m.status)){toast('No status effects to clear.');return}state.party.forEach(m=>m.status=null);setBattleMessage('The Elixir clears every lingering status.')}state.inventory[id]--;save();closeModal();battle.turnLock=true;renderBattle();setTimeout(enemyTurn,650)}
function confirmRetreat(){if(!battle)return;modal('Leave this encounter?','Your progress on this node will not be marked complete. A gatekeeper challenge may be attempted again.',`<button class="btn btn-danger" onclick="retreatNow()">Retreat</button><button class="btn btn-quiet" onclick="closeModal()">Stay</button>`,null,true)}function retreatNow(){closeModal();battle=null;showScreen('map')}
function renderDex(){let owned=new Set(state.party.map(m=>m.species));document.getElementById('dexGrid').innerHTML=DB.map(d=>{let known=state.seen.includes(d.id)||owned.has(d.id),statsTxt=d.base;return `<article class="glass dex-card ${known?'':'locked-mon'}"><div class="dex-card-top"><div class="dex-avatar">${sprite(d)}</div><div><h3>${known?d.name:'Unknown Resonant'}</h3><span class="type-pill type-${d.type.toLowerCase()}">${TYPES[d.type].icon} ${d.type}</span><div class="muted text-xs mt-1">${known?`Tier ${d.tier} · ${d.evolveAt?`evolves at ${d.evolveAt}`:'final form'}`:'Undiscovered form'}</div></div></div><p>${known?d.desc:'A frequency has been recorded in the archive, but its true form remains unknown.'}</p><div class="dex-stats"><span><b>${statsTxt.hp}</b>HP</span><span><b>${statsTxt.atk}</b>ATK</span><span><b>${statsTxt.def}</b>DEF</span><span><b>${statsTxt.spd}</b>SPD</span></div><div class="muted text-xs mt-3">${d.moves.map(n=>`${n} · Lv ${MOVES[n].unlock}`).join(' · ')}</div></article>`}).join('')}
const SHOP=[{id:'harmonizer',name:'Harmonizer Prism',icon:'◇',cost:24,desc:'A reliable starter matrix. Capture a weakened wild Resonant.',unit:'Aether Prism'}, {id:'hyper',name:'Hyper-Conductive Prism',icon:'✧',cost:58,desc:'A finely tuned lattice with a much stronger capture response.',unit:'Prism'}, {id:'elixir',name:'Core Restorer',icon:'◉',cost:32,desc:'Restores 50% of one Resonant’s maximum health.',unit:'Restorer'}, {id:'cleanser',name:'Resonance Elixir',icon:'✦',cost:26,desc:'Clears every lingering status effect from your ensemble.',unit:'Elixir'}];function renderShop(){document.getElementById('shopGrid').innerHTML=SHOP.map(x=>`<article class="glass shop-item"><div class="shop-icon">${x.icon}</div><h3>${x.name}</h3><p>${x.desc}</p><div class="shop-buy"><span class="muted text-xs">In satchel: <b style="color:white">${state.inventory[x.id]||0}</b></span><button class="btn btn-primary btn-small" onclick="buy('${x.id}')">✧ ${x.cost}</button></div></article>`).join('')}
function buy(id){let x=SHOP.find(a=>a.id===id);if(state.shards<x.cost){toast('Not enough Aether Shards.');return}state.shards-=x.cost;state.inventory[id]=(state.inventory[id]||0)+1;save();renderShop();synth.play('select');toast(`${x.name} added to your satchel.`)}function restAtCamp(){if(state.shards<18){toast('Resting costs 18 shards.');return}state.shards-=18;state.party.forEach(m=>{m.hp=m.maxHp;m.status=null});save();renderShop();toast('Your Resonants are fully restored.')}
function renderTeam(){let slots=state.party.map((m,i)=>{let d=species(m.species),s=stats(m),xp=Math.min(100,m.xp/xpNeed(m.level)*100);return `<article class="glass team-card"><div class="team-visual">${sprite(d)}</div><div class="flex justify-between items-center"><h3>${d.name}</h3><span class="type-pill type-${d.type.toLowerCase()}">${d.type}</span></div><div class="team-data">Level ${m.level} · ${m.status?`Status: ${m.status}`:'Ready'} · ${d.evolveAt?`Next form at Lv ${d.evolveAt}`:'Final form'}</div><div class="progress-line"><i style="width:${xp}%"></i></div><div class="team-stats"><span>HP ${m.hp}/${m.maxHp}</span><span>ATK ${s.atk}</span><span>DEF ${s.def}</span><span>SPD ${s.spd}</span></div><div class="flex gap-2 flex-wrap"><button class="btn btn-quiet btn-small" onclick="teamHeal(${i})">Core Restorer · ${state.inventory.elixir||0}</button><button class="btn btn-quiet btn-small" onclick="teamCleanse(${i})">Cleanser · ${state.inventory.cleanser||0}</button>${i>0?`<button class="btn btn-quiet btn-small" onclick="leadMon(${i})">Lead</button>`:''}</div></article>`}).join('');while(state.party.length+((3-state.party.length)>0?0:0)<0){}let empties=Array.from({length:Math.max(0,3-state.party.length)},()=>'<div class="empty-slot">An open place in your ensemble<br><span class="muted">Capture a Resonant in the wild</span></div>').join('');document.getElementById('teamGrid').innerHTML=slots+empties}
function teamHeal(i){if(!state.inventory.elixir){toast('No Core Restorers in your satchel.');return}let m=state.party[i];if(m.hp===m.maxHp){toast('Already at full health.');return}state.inventory.elixir--;m.hp=Math.min(m.maxHp,m.hp+Math.ceil(m.maxHp*.5));save();renderTeam();toast(`${species(m.species).name} restored.`)}function teamCleanse(i){if(!state.inventory.cleanser){toast('No Resonance Elixirs in your satchel.');return}let m=state.party[i];if(!m.status){toast('No status effect to clear.');return}state.inventory.cleanser--;m.status=null;save();renderTeam();toast(`${species(m.species).name} is cleansed.`)}function leadMon(i){let m=state.party.splice(i,1)[0];state.party.unshift(m);save();renderTeam();toast(`${species(m.species).name} now leads the ensemble.`)}
function modal(title,text,actions,callback,raw=false){let content=document.getElementById('modalContent');content.innerHTML=`<div class="eyebrow">AETHELGARD · FIELD EVENT</div><h2>${title}</h2>${raw?`<p>${text}</p><div class="flex flex-col gap-2 mt-4">${actions||''}</div>`:`<p>${text}</p><div class="modal-actions">${actions||''}</div>`}`;document.getElementById('modalBack').classList.add('show');if(callback)content.dataset.callback='yes'}function closeModal(){document.getElementById('modalBack').classList.remove('show')}function toast(text){let el=document.getElementById('toast');el.textContent=text;el.classList.add('show');clearTimeout(toastTimer);toastTimer=setTimeout(()=>el.classList.remove('show'),2600)}function backFromDex(){if(returnScreen==='menu')showScreen('menu');else showScreen('map')}
function showEvolution(d){document.getElementById('evoSprite').innerHTML=sprite(d);document.getElementById('evoName').textContent=d.name;document.getElementById('evoBanner').classList.add('show')}function closeEvolution(){document.getElementById('evoBanner').classList.remove('show')}
function resetProgress(){modal('Reset your field journal?','This permanently erases your local ensemble, inventory, and region progress.',`<button class="btn btn-danger" onclick="doReset()">Erase journal</button><button class="btn btn-quiet" onclick="closeModal()">Keep journey</button>`,null,true)}function doReset(){localStorage.removeItem('aethelgard-save');state=newProfile();battle=null;closeModal();showScreen('menu');updateHeader();toast('A fresh field journal is ready.')}
document.getElementById('soundToggle').addEventListener('click',e=>{synth.enabled=!synth.enabled;e.currentTarget.textContent=synth.enabled?'♫':'♪';toast(synth.enabled?'Sound on':'Sound muted');if(synth.enabled)synth.play('select')});document.getElementById('soundToggle').addEventListener('mouseenter',()=>synth.play('hover'));document.addEventListener('keydown',e=>{if(e.key==='Escape'&&document.getElementById('modalBack').classList.contains('show'))closeModal()});document.getElementById('modalBack').addEventListener('click',e=>{if(e.target.id==='modalBack')closeModal()});
load();updateHeader();if(state.starterChosen)renderMap();else renderStarters();
</script></main></body></html>
<!-- agent-meta {"model":"openai/gpt-6-luna","provider":"openrouter","persona":"full","sessionId":"0b1a488e-406a-458b-ba3b-80325b799587","tokensIn":1212052,"tokensOut":32572,"tokensTotal":1244624,"cost":0.03456578,"turns":31,"toolCalls":30,"failedToolCalls":2,"timestamp":"2026-09-23T03:52:51.058Z"} -->