Creature Collection
GPT 5.6 Luna Pro · middle

Browser check: Artifact runtime error. This is a technical check, not a quality review.
Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 23,806 bytes · SHA-256 cd786194ea35
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><title>Veyra: The Quiet Binding</title><style>
:root{--ink:#eaf7ff;--muted:#91a9bf;--glass:#0b1730cc;--line:#ffffff22;--a:#72e7d1;--b:#b99cff;--danger:#ff7385;--gold:#ffd477}*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#071020;color:var(--ink);font:15px system-ui,Segoe UI,sans-serif}button{font:inherit;color:inherit;border:1px solid var(--line);background:#ffffff12;border-radius:12px;padding:11px 16px;cursor:pointer;transition:.2s}button:hover{background:#ffffff25;transform:translateY(-1px)}button:disabled{opacity:.35;cursor:not-allowed}.hidden{display:none!important}#app{height:100%;position:relative;overflow:hidden}.screen{position:absolute;inset:0}.title{display:grid;place-items:center;text-align:center;background:radial-gradient(circle at 50% 35%,#22446e 0,#101c3b 36%,#050a17 80%)}.orb{width:150px;height:150px;border-radius:50%;margin:auto;background:conic-gradient(from 40deg,#74e7d1,#8879ff,#ff829d,#74e7d1);box-shadow:0 0 70px #69d9df99,inset 0 0 30px #fff8;animation:float 4s ease-in-out infinite}.title h1{font-size:clamp(38px,8vw,80px);letter-spacing:.13em;margin:18px 0 5px;background:linear-gradient(90deg,#c1fff4,#c8b7ff);color:transparent;background-clip:text}.title p{color:var(--muted);letter-spacing:.18em}.actions{display:flex;gap:12px;justify-content:center;margin-top:25px;flex-wrap:wrap}.primary{background:linear-gradient(135deg,#55d6cb,#746de5);border:0}.small{font-size:12px;color:var(--muted)}@keyframes float{50%{transform:translateY(-10px) scale(1.04)}}#world{background:#09172a}.hud{position:absolute;top:12px;left:12px;right:12px;display:flex;justify-content:space-between;align-items:start;z-index:3;pointer-events:none}.hud>*{pointer-events:auto}.glass{background:var(--glass);border:1px solid var(--line);box-shadow:0 10px 30px #0005, inset 0 1px #fff1;backdrop-filter:blur(12px);border-radius:16px}.zonecard{padding:10px 14px}.zonecard b{color:var(--a);font-size:18px}.tools{display:flex;gap:7px}.tools button{padding:8px 10px}.worldcanvas{width:100%;height:100%;display:block}.hint{position:absolute;bottom:18px;left:50%;transform:translateX(-50%);padding:9px 16px;color:#d6e7f8;white-space:nowrap}.pad{position:absolute;bottom:25px;left:20px;display:grid;grid-template-columns:repeat(3,45px);gap:5px}.pad button{height:42px;padding:0;font-size:20px}.pad button:nth-child(1){grid-column:2}.pad button:nth-child(2){grid-column:1;grid-row:2}.pad button:nth-child(3){grid-column:2;grid-row:2}.pad button:nth-child(4){grid-column:3;grid-row:2}.modalshade{position:absolute;inset:0;background:#020614aa;display:grid;place-items:center;z-index:8;padding:18px}.modal{width:min(850px,100%);max-height:92vh;overflow:auto;padding:22px;background:linear-gradient(145deg,#142848ee,#0a1429ee);border:1px solid #ffffff2a;border-radius:22px;box-shadow:0 20px 80px #0009}.modal h2{margin:0 0 15px;color:var(--a)}.tabs{display:flex;gap:7px;flex-wrap:wrap;margin-bottom:15px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px}.card{padding:13px;background:#ffffff0b;border:1px solid var(--line);border-radius:14px}.card h3{margin:0 0 5px}.bar{height:9px;border-radius:10px;background:#ffffff16;overflow:hidden}.bar i{display:block;height:100%;background:linear-gradient(90deg,#62dfc8,#8c9dff);transition:width .5s}.battle{background:radial-gradient(circle at 50% 40%,#273463,#100e2d 55%,#070b1c)}#battleCanvas{position:absolute;inset:0;width:100%;height:100%}.battleui{position:absolute;inset:0;z-index:2;pointer-events:none}.battleui>*{pointer-events:auto}.enemybox,.allybox{position:absolute;width:min(290px,44vw);padding:12px}.enemybox{top:10%;left:7%}.allybox{bottom:18%;right:7%}.battlelog{position:absolute;bottom:5%;left:50%;transform:translateX(-50%);width:min(460px,90%);padding:12px;text-align:center}.moves{display:grid;grid-template-columns:repeat(2,1fr);gap:7px;margin-top:10px}.movebtn{display:flex;justify-content:space-between;align-items:center}.movebtn em{font-size:11px;color:var(--muted);font-style:normal}.switchline{display:flex;gap:5px;margin-top:8px}.flash{animation:flash .3s}@keyframes flash{50%{filter:brightness(2.5)}}.shake{animation:shake .25s}@keyframes shake{25%{transform:translate(5px,-3px)}75%{transform:translate(-5px,3px)}}.toast{position:absolute;top:75px;left:50%;transform:translateX(-50%);z-index:20;padding:12px 18px;background:#182c4df2;border-radius:12px;animation:toast 2.5s forwards}@keyframes toast{80%{opacity:1}100%{opacity:0;transform:translate(-50%,-18px)}}.dialog{position:absolute;left:50%;bottom:12%;transform:translateX(-50%);width:min(680px,90%);padding:18px;z-index:15}.dialog b{color:var(--gold)}.dialog button{float:right}.type{font-size:11px;color:var(--gold);letter-spacing:.06em}.joy{display:none}@media(max-width:650px){.hud{top:6px}.tools button{font-size:0}.tools button:before{font-size:17px}.tools button:nth-child(1):before{content:'☰'}.tools button:nth-child(2):before{content:'◈'}.tools button:nth-child(3):before{content:'?'}.enemybox{top:13%;left:4%}.allybox{bottom:24%;right:4%}.hint{bottom:8px}.pad{display:grid}.battlelog{bottom:3%}}
</style></head><body><div id="app"><section id="title" class="screen title"><div><div class="orb"></div><h1>VEYRA</h1><p>THE QUIET BINDING</p><div class="actions"><button class="primary" onclick="newGame()">New Binding</button><button id="continue" onclick="continueGame()">Continue</button></div><div class="small" style="margin-top:20px">A field journal of resonant life</div></div></section><section id="world" class="screen hidden"><canvas id="map"></canvas><div class="hud"><div class="zonecard glass"><b id="zname">Murmur Vale</b><div class="small" id="zsub">Story node I · 0 anchors</div></div><div class="tools"><button class="glass" onclick="openMenu()">Menu</button><button class="glass" onclick="openTeam()">Team</button><button class="glass" onclick="openDex()">Codex</button></div></div><div class="hint glass" id="hint">WASD / arrows to roam · find the teal shrine</div><div class="pad"><button ontouchstart="move(0,-1)">▲</button><button ontouchstart="move(-1,0)">◀</button><button ontouchstart="move(0,1)">▼</button><button ontouchstart="move(1,0)">▶</button></div></section><section id="battle" class="screen battle hidden"><canvas id="battleCanvas"></canvas><div class="battleui"><div class="enemybox glass" id="enemybox"></div><div class="allybox glass" id="allybox"></div><div class="battlelog glass" id="battlelog"></div></div></section><div id="modalshade" class="modalshade hidden"><div class="modal" id="modal"></div></div></div><script>
const TYPES=['Lumen','Gale','Bramble','Cinder','Tide','Null','Aegis'];const chart={Lumen:['Gale'],Gale:['Bramble'],Bramble:['Lumen'],Cinder:['Lumen','Bramble'],Tide:['Cinder','Gale'],Null:TYPES,Aegis:TYPES.filter(x=>x!=='Null')};
const moves=[['Glimmer Pin','Lumen',28,95,8,'none'],['Sway Current','Gale',22,100,8,'speed'],['Rootlet','Bramble',25,95,8,'regen'],['Kindle Flick','Cinder',24,100,8,'burn'],['Drip Stitch','Tide',20,100,8,'heal'],['Mute Prick','Null',30,90,7,'shock'],['Hush Shell','Aegis',0,100,7,'barrier'],['Prism Jab','Lumen',40,90,12,'none'],['Whistle Arc','Gale',35,95,10,'priority'],['Briar Coil','Bramble',38,90,10,'frost'],['Ashen Loop','Cinder',36,95,10,'burn'],['Blue Mend','Tide',0,100,10,'heal'],['Quietus','Null',45,85,11,'shock'],['Ward Bloom','Aegis',0,100,10,'barrier'],['Sun Thread','Lumen',52,90,14,'speed'],['Storm Needle','Gale',48,90,13,'multi'],['Moss Lance','Bramble',50,90,13,'regen'],['Furnace Veil','Cinder',48,90,13,'burn'],['Rain Bell','Tide',35,100,12,'heal'],['Blank Chime','Null',55,80,14,'shock'],['Pale Rampart','Aegis',0,100,14,'barrier'],['Lusterfall','Lumen',70,85,18,'none'],['Cyclone Writ','Gale',65,88,17,'priority'],['Thorn Census','Bramble',68,88,17,'frost'],['Red Orbit','Cinder',68,88,17,'burn'],['Tideglass','Tide',55,95,16,'heal'],['Zero Psalm','Null',72,80,18,'shock'],['Citadel Seed','Aegis',0,100,17,'barrier'],['Dawn Array','Lumen',82,82,22,'speed'],['Gust Choir','Gale',78,85,20,'multi'],['Verdant Maw','Bramble',80,85,20,'regen'],['Coal Halo','Cinder',80,85,20,'burn'],['Deluge Sigil','Tide',70,90,19,'heal'],['Unword','Null',88,75,22,'shock'],['Aegis Cant','Aegis',0,100,22,'barrier'],['Ray Ravel','Lumen',100,78,25,'none'],['Sky Rasp','Gale',96,80,23,'priority'],['Root Meridian','Bramble',98,80,23,'frost'],['Pyre Spiral','Cinder',98,80,23,'burn'],['Ocean Inside','Tide',85,85,22,'heal'],['Absolute Hush','Null',110,70,27,'shock'],['Last Bastion','Aegis',0,100,27,'barrier']].map((m,i)=>({id:i,name:m[0],type:m[1],power:m[2],acc:m[3],cost:m[4],effect:m[5]}));
const raw=[['Auralit','A lantern-bodied grazer that drinks dawn mist.','Lumen',44,35,38,42],['Veylisk','It follows unspoken promises through tall grass.','Gale',38,34,52,40],['Mosskin','A patient knot of roots that remembers footsteps.','Bramble',48,46,25,44],['Cindrel','A coal-eyed mote warming abandoned hollows.','Cinder',42,40,36,39],['Rillune','Its translucent fins ripple before rain arrives.','Tide',50,35,31,43],['Orr','A small absence with a fondness for bells.','Null',36,42,48,38],['Pallidra','A shell of hush forms around its sleeping core.','Aegis',55,55,18,35],['Solenne','It stores noon in seven bright chambers.','Lumen',52,42,44,48],['Kestrelum','A ribbon creature that knots the wind into paths.','Gale',43,38,61,46],['Brontide','Its thorn crown blooms only near old secrets.','Bramble',58,50,30,49],['Emberel','A wandering spark that paints circles in soot.','Cinder',49,46,43,45],['Nacreth','It sings beneath puddles, where small worlds gather.','Tide',58,40,35,49],['Vantae','A soft silhouette found beside unlit thresholds.','Null',42,48,55,43],['Iriarch','Its mirrored hide turns fear into direction.','Aegis',64,58,24,40],['Lumera','A drifting petal with a fierce inner sunrise.','Lumen',46,33,49,54],['Zephra','It sleeps in motion and wakes wherever it lands.','Gale',40,32,67,51],['Rootide','The oldest garden keeps one seat for it.','Bramble',62,54,28,52],['Pyrren','A patient ember whose shadow burns blue.','Cinder',54,48,45,50]];
const forms=[['Auralith','Auralit',28,'Lumen'],['Veylora','Veylisk',25,'Gale'],['Mossarch','Mosskin',30,'Bramble'],['Cindervane','Cindrel',26,'Cinder'],['Rillux','Rillune',29,'Tide'],['Orraxis','Orr',24,'Null'],['Pallidorn','Pallidra',31,'Aegis'],['Solaryn','Solenne',32,'Lumen'],['Kestrelux','Kestrelum',30,'Gale']];
let creatures=[];raw.forEach((r,i)=>creatures.push({id:i,name:r[0],lore:r[1],type:r[2],base:r.slice(3),form:null}));forms.forEach((f,i)=>{let b=creatures.find(x=>x.name===f[1]);creatures.push({id:18+i,name:f[0],lore:'A transformed resonance, carrying its first shape as a quiet memory.',type:f[3],base:b.base.map((n,j)=>n+(j<3?12:8)),form:f[1]})});
const zones=[['Murmur Vale','#183c55','#0b1b2d',.045,'A wind has learned your name.'],['Saffron Reach','#5b3d39','#1d1729',.06,'The ochre stones hum in sequence.'],['Glassfen','#15515a','#081f32',.075,'Something below the water answers.'],['Ember Steps','#632f3d','#211426',.09,'The old furnace is breathing.'],['Violet Shelf','#483768','#141530',.11,'Stars collect in the snow.'],['Crown of Quiet','#344c5c','#0a1425',.15,'The final anchor waits beyond the hush.']];
let S,keys={},audio=null;const $=x=>document.getElementById(x);function blank(){return {zone:0,x:4,y:4,party:[],box:[],items:{snare:8,loom:2,tonic:5,catalyst:1},anchors:0,seen:[],story:0,won:false,shrine:0,steps:0}}function newGame(){S=blank();chooseStarter()}function chooseStarter(){show(`<h2>Choose a first resonance</h2><p>The field opens to three quiet companions. This choice shapes your first rhythm.</p><div class="grid">${[0,3,5].map(i=>{let c=creatures[i];return `<div class="card"><h3>${c.name}</h3><span class="type">${c.type}</span><p>${c.lore}</p><button class="primary" onclick="startWith(${i})">Bind ${c.name}</button></div>`}).join('')}</div>`)}function startWith(i){S.party=[make(i,5)];S.story=1;close();enterWorld();dialog('Nera',zones[0][4]+' Move with the arrows. The teal shrine saves your binding.');save()}
function make(id,l=3){let c=creatures[id],m=[0,1,2,3].map((_,j)=>moves[(id*2+j)%moves.length]);return {id,name:c.name,type:c.type,level:l,max:Math.min(50,Math.floor(c.base[0]+l*4)),hp:Math.min(50,Math.floor(c.base[0]+l*4)),atk:c.base[1]+l*2,def:c.base[2]+l*2,spd:c.base[3]+l*2,moves:m.map(x=>x.id),status:{},exp:0}}function save(){if(S)localStorage.setItem('veyra-save',JSON.stringify(S))}function continueGame(){let x=localStorage.getItem('veyra-save');if(x){S=JSON.parse(x);enterWorld()}else newGame()}function enterWorld(){$('title').classList.add('hidden');$('world').classList.remove('hidden');resize();updateZone();requestAnimationFrame(loop)}function resize(){let c=$('map');c.width=innerWidth*devicePixelRatio;c.height=innerHeight*devicePixelRatio;c.style.width='100%';c.style.height='100%'}addEventListener('resize',resize);addEventListener('keydown',e=>{keys[e.key.toLowerCase()]=1;if(['arrowup','arrowdown','arrowleft','arrowright','w','a','s','d'].includes(e.key.toLowerCase())){e.preventDefault();let d={w:[0,-1],arrowup:[0,-1],s:[0,1],arrowdown:[0,1],a:[-1,0],arrowleft:[-1,0],d:[1,0],arrowright:[1,0]}[e.key.toLowerCase()];move(...d)}});function move(dx,dy){if(!S||$('world').classList.contains('hidden'))return;S.x=Math.max(1,Math.min(8,S.x+dx));S.y=Math.max(1,Math.min(8,S.y+dy));S.steps++;if(S.x===4&&S.y===4){S.shrine=S.zone;S.party.forEach(c=>{c.hp=c.max;c.status={}});save();toast('Shrine glow: party restored and binding saved.')}if(S.x===8&&S.y===8&&S.zone>0)anchor();if(Math.random()<zones[S.zone][3]&&S.steps%2===0)encounter();draw()}
function updateZone(){$('zname').textContent=zones[S.zone][0];$('zsub').textContent=`Story node ${['I','II','III','IV','V','VI'][S.zone]} · ${S.anchors}/4 anchors`;draw()}function loop(){if(!$('world').classList.contains('hidden')){draw();requestAnimationFrame(loop)}}function draw(){let c=$('map'),x=c.getContext('2d'),d=devicePixelRatio,w=innerWidth*d,h=innerHeight*d;x.clearRect(0,0,w,h);let g=x.createLinearGradient(0,0,w,h);g.addColorStop(0,zones[S.zone][1]);g.addColorStop(1,zones[S.zone][2]);x.fillStyle=g;x.fillRect(0,0,w,h);x.globalAlpha=.2;for(let i=0;i<45;i++){x.fillStyle=i%2?'#a3f8e8':'#d6b2ff';let px=((i*97+Date.now()/30*(i%3+1))%w),py=(i*173%h);x.beginPath();x.arc(px,py,(i%4+1)*d,0,7);x.fill()}x.globalAlpha=1;let size=Math.min(w,h)/11;for(let j=0;j<9;j++)for(let i=0;i<9;i++){x.fillStyle=(i+j)%2?'#ffffff0c':'#0000000c';x.fillRect((i+1)*size,(j+1)*size,size-2,size-2)}x.fillStyle='#6de8d0';x.shadowBlur=25*d;x.shadowColor='#6de8d0';x.beginPath();x.arc(5*size,5*size,size*.25,0,7);x.fill();x.shadowBlur=0;x.fillStyle='#f8cf79';x.beginPath();x.arc((S.x+1)*size,(S.y+1)*size,size*.28,0,7);x.fill();}
function encounter(){let pool=raw.filter((r,i)=>i<7+S.zone*2);let id=pool[Math.floor(Math.random()*pool.length)];let c=make(raw.indexOf(id),Math.min(48,3+S.zone*5+Math.floor(Math.random()*4)));startBattle([c],false)}function anchor(){if(S.anchors>=4){toast('The crown is quiet. Roam freely.');return}let id=[6,11,13,17][S.anchors];startBattle([make(id,12+S.anchors*7),make((id+2)%18,10+S.anchors*7)],true)}
function startBattle(enemies,isAnchor){S.battle={enemies,active:0,enemy:0,anchor:isAnchor,turn:true};$('world').classList.add('hidden');$('battle').classList.remove('hidden');renderBattle();audioStart()}
function active(){return S.party[S.battle.active]}function renderBattle(){let a=active(),e=S.battle.enemies[S.battle.enemy];$('enemybox').innerHTML=box(e);$('allybox').innerHTML=box(a)+`<div class="moves">${a.moves.map(id=>{let m=moves[id];return `<button class="movebtn" onclick="useMove(${id})" ${a.status.frost?'disabled':''}>${m.name}<em>${m.cost}e · ${m.type}</em></button>`}).join('')}</div><div class="switchline">${S.party.map((c,i)=>`<button onclick="switchTo(${i})" ${i===S.battle.active||c.hp<=0?'disabled':''}>${c.name} ${Math.ceil(c.hp)}/${c.max}</button>`).join('')}</div>`;$('battlelog').innerHTML=`<b>${e.name}</b> watches from the far side of the veil.<br><span class="small">Choose a resonance or change your lead.</span>`;drawBattle()}
function box(c){return `<b>${c.name}</b> <span class="type">${c.type} · Lv ${c.level}</span><div class="bar"><i style="width:${Math.max(0,c.hp/c.max*100)}%"></i></div><div class="small">${Math.ceil(c.hp)} / ${c.max} vitality</div>`}function useMove(id){if(!S.battle||!active())return;let a=active(),e=S.battle.enemies[S.battle.enemy],m=moves[id];if(Math.random()*100>m.acc){log(a.name+' releases '+m.name+'—but the veil turns it aside.');enemyTurn();return}let mult=chart[m.type]?.includes(e.type)?1.35:1;if(e.type==='Aegis'&&m.type!=='Null')mult*=.7;let stab=m.type===a.type?1.25:1;let crit=Math.random()<.1?1.5:1;let rawd=m.power?Math.max(1,Math.floor(.4*a.level*a.atk/Math.max(1,e.def)*mult*stab*crit+Math.random()*4)):0;if(m.effect==='multi')rawd=Math.floor(rawd*1.4);e.hp=Math.max(0,e.hp-rawd);if(m.effect==='burn')e.status.burn=3;if(m.effect==='frost')e.status.frost=2;if(m.effect==='shock')e.status.shock=2;if(m.effect==='heal')a.hp=Math.min(a.max,a.hp+Math.floor(a.max*.22));if(m.effect==='barrier')a.status.barrier=1;if(m.effect==='regen')a.status.regen=3;if(m.effect==='speed')a.status.speed=2;log(`${a.name} uses ${m.name}! ${rawd?rawd+' vitality fades. ':''}${crit>1?'CRITICAL! ':''}${mult>1?'Resonant! ':''}`);if(e.hp<=0){victory();return}enemyTurn()}
function enemyTurn(){let a=active(),e=S.battle.enemies[S.battle.enemy];if(a.status.shock&&Math.random()<.35){log(a.name+' flickers, losing its turn.');a.status.shock--}else{let m=moves[e.moves[Math.floor(Math.random()*e.moves.length)]],d=m.power?Math.max(1,Math.floor(.4*e.level*e.atk/Math.max(1,a.def)*(chart[m.type]?.includes(a.type)?1.35:1)*(m.type===e.type?1.25:1)+Math.random()*3)):0;if(a.status.barrier){d=0;delete a.status.barrier;log('A hush-shell deflects the strike.')}else{a.hp=Math.max(0,a.hp-d);if(m.effect==='burn')a.status.burn=3;if(m.effect==='shock')a.status.shock=2;log(`${e.name} answers with ${m.name}. ${d} vitality lost.`)}}if(a.hp<=0){let n=S.party.findIndex(c=>c.hp>0);if(n>=0){S.battle.active=n;log(a.name+' dims. '+S.party[n].name+' enters.')}else{S.x=S.y=4;finishBattle();toast('The shrine gathers you back.');return}}renderBattle()}
function switchTo(i){if(S.party[i].hp>0){S.battle.active=i;log(S.party[i].name+' takes the front.');enemyTurn()}}function log(t){$('battlelog').innerHTML=`${t}<div class="small" style="margin-top:5px">Choose your next resonance.</div>`}function victory(){let e=S.battle.enemies[S.battle.enemy],a=active();a.exp+=e.level*12; if(a.exp>=a.level*35&&a.level<50){a.level++;a.max+=4;a.hp=a.max;a.atk+=2;a.def+=2;a.spd+=2;log(a.name+' reaches level '+a.level+'!')}if(S.battle.anchor){S.anchors++;S.zone=Math.min(5,S.zone+1);S.story++;S.items.catalyst++;log('Anchor unknots. A catalyst joins your pack.')}else{S.seen.push(e.id);log(e.name+' is quiet enough to bind.')}renderBattle();setTimeout(()=>captureOrEnd(e),900)}function captureOrEnd(e){if(S.battle.anchor){setTimeout(finishBattle,1000);return}let chance=.25+(1-e.hp/e.max)*.55+(S.items.loom||0)*.08;if(S.items.snare>0&&Math.random()<chance){S.items.snare--;let c=make(e.id,e.level);if(S.party.length<6)S.party.push(c);else S.box.push(c);log(e.name+' accepts the binding.')}else log('The binding slips. The wild resonance remains free.');setTimeout(finishBattle,1100)}function finishBattle(){S.battle=null;$('battle').classList.add('hidden');$('world').classList.remove('hidden');updateZone();save()}
function openMenu(){show(`<h2>Field satchel</h2><div class="tabs"><button onclick="useItem('tonic')">Tonic ×${S.items.tonic}</button><button onclick="useItem('snare')">Snare ×${S.items.snare}</button><button onclick="useItem('loom')">Luminous Loom ×${S.items.loom}</button><button onclick="useItem('catalyst')">Catalyst ×${S.items.catalyst}</button></div><hr><p>Anchors: ${S.anchors}/4 · Bound: ${S.party.length+S.box.length}/27 · Bestiary seen: ${S.seen.length}</p><p class="small">The teal shrine at the center restores vitality and autosaves. Collect 8 distinct resonances to complete your field objective.</p><button class="primary" onclick="save();close();toast('Binding saved.')">Save and close</button>`)}function useItem(it){if(it==='tonic'&&S.items.tonic&&active()){S.items.tonic--;S.party[0].hp=Math.min(S.party[0].max,S.party[0].hp+25);toast('A tonic restores vitality.')}else toast('That item waits for a quieter moment.');save();close()}function openTeam(){show(`<h2>Resonance party</h2><div class="grid">${S.party.map(c=>`<div class="card"><h3>${c.name} <span class="type">${c.type}</span></h3><p>Lv ${c.level} · ATK ${c.atk} · DEF ${c.def} · SPD ${c.spd}</p>${box(c)}${c.level>=24&&S.items.catalyst&&c.id<9?`<button onclick="transform(${c.id})">Transform · catalyst</button>`:''}</div>`).join('')}</div><button onclick="close()">Return to field</button>`)}
function transform(id){let c=S.party.find(x=>x.id===id),f=forms.findIndex(x=>x[1]===creatures[id].name);if(!c||f<0||c.level<forms[f][2]||!S.items.catalyst)return;S.items.catalyst--;c.id=18+f;c.name=creatures[c.id].name;c.type=creatures[c.id].type;c.max+=12;c.hp=c.max;c.atk+=8;c.def+=8;c.spd+=8;toast(c.name+' unfolds from its former shape.');save();openTeam()}function openDex(){show(`<h2>Field codex</h2><p>Seven affinities braid the veil: Lumen → Gale → Bramble → Lumen. Cinder overcomes Lumen and Bramble; Tide overcomes Cinder and Gale. Null presses lightly against all. Aegis resists all except Null.</p><div class="grid">${creatures.map((c,i)=>`<div class="card" style="opacity:${S.seen.includes(i)||S.party.some(x=>x.id===i)||i===0?.5:.45}"><h3>${(S.seen.includes(i)||S.party.some(x=>x.id===i)||i===0)?c.name:'Unrecorded form'}</h3><span class="type">${c.type}</span><p>${(S.seen.includes(i)||S.party.some(x=>x.id===i)||i===0)?c.lore:'The shape is only a rumor in your notes.'}</p></div>`).join('')}</div><button onclick="close()">Return to field</button>`)}function show(html){$('modal').innerHTML=html;$('modalshade').classList.remove('hidden')}function close(){$('modalshade').classList.add('hidden')}function toast(t){let n=document.createElement('div');n.className='toast';n.textContent=t;$('app').appendChild(n);setTimeout(()=>n.remove(),2600)}function dialog(w,t){let n=document.createElement('div');n.className='dialog glass';n.innerHTML=`<b>${w}</b><p>${t}</p><button onclick="this.parentNode.remove()">Understood</button>`;$('world').appendChild(n)}function drawBattle(){let c=$('battleCanvas'),x=c.getContext('2d');c.width=innerWidth*devicePixelRatio;c.height=innerHeight*devicePixelRatio;let w=c.width,h=c.height;x.clearRect(0,0,w,h);x.fillStyle='#ffffff12';for(let i=0;i<18;i++){x.beginPath();x.arc((i*149+Date.now()/20)%w,(i*83)%h,3+ i%5,0,7);x.fill()}let e=S.battle.enemies[S.battle.enemy],a=active();x.fillStyle='#b99cff';x.shadowBlur=35;x.shadowColor='#b99cff';x.beginPath();x.arc(w*.68,h*.35,Math.min(w,h)*.1,0,7);x.fill();x.fillStyle='#72e7d1';x.shadowColor='#72e7d1';x.beginPath();x.arc(w*.32,h*.63,Math.min(w,h)*.12,0,7);x.fill();x.shadowBlur=0}function audioStart(){if(!audio){audio=new (window.AudioContext||window.webkitAudioContext)();let o=audio.createOscillator(),g=audio.createGain();o.type='sine';o.frequency.value=110;g.gain.value=.018;o.connect(g);g.connect(audio.destination);o.start()}}
$('continue').disabled=!localStorage.getItem('veyra-save');
</script></body></html>
<!-- agent-meta {"model":"openai/gpt-5.6-luna-pro","provider":"openrouter","persona":"middle","sessionId":"d1583267-afb9-441e-a195-b4f01df1492c","tokensIn":136342,"tokensOut":28291,"tokensTotal":164633,"turns":4,"toolCalls":4,"failedToolCalls":0,"timestamp":"2026-07-12T04:35:45.917Z"} -->