← Creature Collection results

Creature Collection

GPT 5.6 Luna · legacy

Initial view of Creature Collection

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 32,050 bytes · SHA-256 ddc7915c422d
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>VELLUM//BINDING</title>
<style>
:root{--ink:#111629;--deep:#182044;--glass:#ffffff16;--line:#ffffff2d;--text:#edf4ff;--muted:#aab8d6;--gold:#ffd875;--mint:#75f2c2;--red:#ff7187;--blue:#75bcff;--shadow:0 18px 60px #03051188}*{box-sizing:border-box}html,body{margin:0;height:100%;overflow:hidden;background:#0c1024;color:var(--text);font:14px system-ui,-apple-system,Segoe UI,sans-serif}button{font:inherit;color:inherit;cursor:pointer;border:0}#app{height:100%;position:relative;overflow:hidden}canvas{position:absolute;inset:0;width:100%;height:100%;image-rendering:auto}.screen{position:absolute;inset:0;display:none}.screen.show{display:block}.shade{position:absolute;inset:0;background:radial-gradient(circle at 50% 35%,#304b7e33,transparent 45%),linear-gradient(0deg,#080b18aa,transparent 60%)}
#title{background:linear-gradient(140deg,#171738,#243d58 52%,#12152d);align-items:center;justify-content:center;text-align:center}.titlebox{position:relative;width:min(680px,90vw);padding:50px 25px}.sigil{width:98px;height:98px;margin:auto;border:1px solid #fff8;border-radius:34% 66% 58% 42%;transform:rotate(35deg);box-shadow:0 0 45px var(--mint),inset 0 0 28px #75f2c244;animation:float 4s ease-in-out infinite}.sigil:after{content:'✦';display:block;transform:rotate(-35deg);font-size:42px;line-height:98px;color:var(--mint)}@keyframes float{50%{transform:rotate(35deg) translateY(-10px)}}h1{font-size:clamp(36px,8vw,76px);letter-spacing:.16em;margin:28px 0 5px;color:#fff;text-shadow:0 0 25px #75f2c2aa}.tag{color:var(--gold);letter-spacing:.35em;font-size:11px;margin-bottom:35px}.btn{background:linear-gradient(100deg,#ffffff1c,#ffffff08);border:1px solid #ffffff3d;padding:13px 24px;border-radius:8px;transition:.2s;letter-spacing:.08em}.btn:hover{background:#75f2c233;border-color:var(--mint);transform:translateY(-2px)}.btn.primary{background:linear-gradient(110deg,#4e9b9c,#6c65ba);border-color:#b6fff0}.titlebuttons{display:flex;gap:12px;justify-content:center;flex-wrap:wrap}.caption{color:var(--muted);font-size:12px;margin-top:25px}
#world{background:#10152d}.hud{position:absolute;top:18px;left:22px;right:22px;display:flex;align-items:flex-start;justify-content:space-between;pointer-events:none}.logo{font-weight:700;letter-spacing:.15em;color:var(--mint);text-shadow:0 0 12px #75f2c288}.stats{display:flex;gap:13px;flex-wrap:wrap;justify-content:flex-end}.chip{background:#10172dcc;border:1px solid var(--line);padding:8px 12px;border-radius:8px;color:var(--muted)}.chip b{color:#fff}.panel{background:#11182ddd;border:1px solid var(--line);box-shadow:var(--shadow);border-radius:14px;backdrop-filter:blur(14px)}#zoneInfo{position:absolute;left:22px;bottom:20px;padding:15px 18px;max-width:320px}.zoneName{font-size:20px;color:var(--gold);font-weight:700}.zoneInfoText{color:var(--muted);line-height:1.45;font-size:12px;margin-top:4px}.worldActions{position:absolute;right:22px;bottom:20px;display:flex;gap:8px}.worldActions .btn{padding:10px 14px}.touch{position:absolute;bottom:92px;left:22px;display:grid;grid-template-columns:45px 45px 45px;grid-template-rows:45px 45px;gap:4px;opacity:.8}.touch button{border:1px solid #fff3;background:#172345aa;border-radius:9px}.touch .up{grid-column:2}.touch .left{grid-column:1}.touch .down{grid-column:2}.touch .right{grid-column:3}
.modalWrap{position:absolute;inset:0;background:#080c19aa;display:none;align-items:center;justify-content:center;padding:18px;z-index:5}.modalWrap.show{display:flex}.modal{width:min(760px,100%);max-height:90vh;overflow:auto;padding:24px;background:#11182df2;border:1px solid #ffffff30;border-radius:16px;box-shadow:var(--shadow)}.modal h2{margin:0 0 15px;color:var(--gold)}.tabs{display:flex;gap:7px;margin-bottom:14px;flex-wrap:wrap}.tabs button.active{background:#75f2c233;border-color:var(--mint)}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.card{padding:12px;border:1px solid var(--line);background:#ffffff09;border-radius:10px}.card h3{margin:0 0 5px}.small{font-size:12px;color:var(--muted);line-height:1.4}.row{display:flex;align-items:center;justify-content:space-between;gap:10px}.close{float:right;color:var(--muted);font-size:19px;background:none}.bar{height:7px;background:#ffffff17;border-radius:20px;overflow:hidden;margin:5px 0}.bar i{display:block;height:100%;background:linear-gradient(90deg,var(--red),var(--gold));border-radius:20px;transition:width .4s}.bar.blue i{background:linear-gradient(90deg,#66abff,#75f2c2)}
#battle{background:#17142a}.battleTop{position:absolute;top:16px;left:5%;right:5%;display:flex;justify-content:space-between;z-index:2}.enemyPanel,.allyPanel{width:min(300px,42vw);background:#11182dce;border:1px solid #fff3;border-radius:11px;padding:10px}.enemyPanel{margin-left:auto}.combatCanvas{position:absolute;inset:0}.battleBottom{position:absolute;bottom:0;left:0;right:0;padding:17px 5%;display:grid;grid-template-columns:1fr 2fr;gap:14px;background:linear-gradient(0deg,#0b0f20ee,transparent);z-index:2}.battleLog{height:70px;padding:11px;color:var(--muted);font-size:13px}.moves{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}.movebtn{text-align:left;background:#ffffff12;border:1px solid #fff2;border-radius:8px;padding:9px}.movebtn:hover{border-color:var(--mint);background:#75f2c220}.movebtn b{display:block}.movebtn small{color:var(--muted)}.type{font-size:10px;color:var(--mint);letter-spacing:.1em}.battleTools{display:flex;gap:7px;margin-top:8px;flex-wrap:wrap}.battleTools button{font-size:12px;padding:8px 10px}.flash{position:absolute;inset:0;pointer-events:none;background:#fff;opacity:0;z-index:3}.flash.on{animation:flash .35s}@keyframes flash{30%{opacity:.7}100%{opacity:0}}.damage{position:absolute;font-size:28px;font-weight:800;color:#fff;text-shadow:0 2px 5px #000;animation:rise 1s forwards;z-index:4}@keyframes rise{to{transform:translateY(-55px);opacity:0}}
#toast{position:absolute;top:80px;left:50%;transform:translateX(-50%);background:#10182dea;border:1px solid var(--mint);padding:11px 19px;border-radius:9px;opacity:0;transition:.3s;z-index:8;pointer-events:none}#toast.show{opacity:1;transform:translate(-50%,8px)}.encounter{position:absolute;inset:0;background:#0c1024dd;display:flex;align-items:center;justify-content:center;z-index:7}.encounterBox{text-align:center;padding:35px;border:1px solid #fff4;border-radius:16px;background:#11182df2}.creatureIcon{font-size:74px;filter:drop-shadow(0 0 14px #75f2c2aa);animation:bob 2s infinite}@keyframes bob{50%{transform:translateY(-8px) rotate(4deg)}}.captureMeter{width:260px;height:15px;border-radius:20px;background:#ffffff1a;margin:16px auto;overflow:hidden}.captureMeter i{display:block;height:100%;background:linear-gradient(90deg,var(--red),var(--gold),var(--mint));width:0}.rhythm{padding:11px 28px;background:#6c65ba;border-radius:7px}.miniNote{color:var(--muted);font-size:12px}
@media(max-width:600px){.hud{left:12px;right:12px}.stats{gap:5px}.chip{padding:6px;font-size:11px}.battleBottom{grid-template-columns:1fr;padding:10px 4%}.battleLog{height:45px}.moves{grid-template-columns:repeat(2,1fr)}#zoneInfo{left:12px;bottom:12px}.worldActions{right:12px;bottom:12px;flex-direction:column}.titlebox{padding:20px}.modal{padding:17px}}
</style></head><body><div id="app">
<section id="title" class="screen show"><div class="shade"></div><div class="titlebox"><div class="sigil"></div><h1>VELLUM//BINDING</h1><div class="tag">A FIELD GUIDE TO THE UNMAPPED</div><div class="titlebuttons"><button class="btn primary" id="newBtn">NEW BINDING</button><button class="btn" id="continueBtn">CONTINUE</button></div><div class="caption">Six regions. Eight rare signatures. Four anchors.<br>Move with <b>WASD</b> or the touch glyphs. Listen for the quiet between stars.</div></div></section>
<section id="world" class="screen"><canvas id="worldCanvas"></canvas><div class="hud"><div class="logo">VELLUM//BINDING</div><div class="stats"><div class="chip">SHARDS <b id="shards">0</b></div><div class="chip">BONDS <b id="bonds">0/8</b></div><div class="chip">⚙ <b id="levelBadge">FIELD 1</b></div><button class="btn" id="pauseBtn" style="pointer-events:auto;padding:7px 11px">MENU</button></div></div><div id="zoneInfo" class="panel"><div class="zoneName" id="zoneName">—</div><div class="zoneInfoText" id="zoneText">—</div></div><div class="worldActions"><button class="btn" id="teamBtn">TEAM</button><button class="btn" id="guideBtn">GUIDE</button><button class="btn" id="anchorBtn">ANCHOR</button><button class="btn" id="travelBtn">TRAVEL</button><button class="btn" id="shrineBtn">SHRINE</button></div><div class="touch"><button class="up" data-key="ArrowUp">▲</button><button class="left" data-key="ArrowLeft">◀</button><button class="down" data-key="ArrowDown">▼</button><button class="right" data-key="ArrowRight">▶</button></div></section>
<section id="battle" class="screen"><canvas id="battleCanvas" class="combatCanvas"></canvas><div class="battleTop"><div class="allyPanel"><div class="row"><b id="allyName">—</b><span id="allyLv">Lv 1</span></div><div class="bar"><i id="allyHp"></i></div><div class="small" id="allyHpText">—</div></div><div class="enemyPanel"><div class="row"><b id="enemyName">—</b><span id="enemyLv">Lv 1</span></div><div class="bar"><i id="enemyHp"></i></div><div class="small" id="enemyHpText">—</div></div></div><div class="battleBottom"><div><div class="battleLog panel" id="battleLog">The field is listening.</div><div class="battleTools"><button class="btn" id="bagBtn">BAG</button><button class="btn" id="switchBtn">SWITCH</button><button class="btn" id="fleeBtn">WITHDRAW</button></div></div><div class="moves" id="moves"></div></div><div class="flash" id="flash"></div></section>
<div id="modal" class="modalWrap"><div class="modal"><button class="close" id="closeModal">×</button><div id="modalContent"></div></div></div><div id="toast"></div><div id="encounter" class="encounter" style="display:none"><div class="encounterBox"><div class="creatureIcon" id="encIcon">◈</div><h2 id="encTitle">A signature stirs</h2><p class="miniNote" id="encText">The wilds have noticed you.</p><button class="btn primary" id="encFight">ENTER THE THREAD</button></div></div></div>
<script>
'use strict';
const $=s=>document.querySelector(s), $$=s=>[...document.querySelectorAll(s)], clamp=(n,a,b)=>Math.max(a,Math.min(b,n)), rnd=(a,b)=>Math.floor(Math.random()*(b-a+1))+a;
const TYPES=['Auralis','Brin','Cindrel','Dorsen','Echra','Ferrox','Gloam'];
const typeColor={Auralis:'#8fe5ff',Brin:'#7de9ad',Cindrel:'#ff9275',Dorsen:'#d09aff',Echra:'#ffe083',Ferrox:'#b9c8dd',Gloam:'#7580bd'};
// The seven affinity relationships are deliberately encoded here: A>B>C>A; D>A,C; E>D,B; F slight all; G resists all, weak to F.
const strong={Auralis:['Brin'],Brin:['Cindrel'],Cindrel:['Auralis'],Dorsen:['Auralis','Cindrel'],Echra:['Dorsen','Brin'],Ferrox:TYPES.filter(x=>x!=='Ferrox'&&x!=='Gloam'),Gloam:[]};
const weak={Auralis:['Cindrel','Dorsen'],Brin:['Auralis','Echra'],Cindrel:['Brin','Dorsen'],Dorsen:['Echra'],Echra:['Ferrox'],Ferrox:['Gloam'],Gloam:['Ferrox']};
const moves=[
['Lumen Pin',28,96,'Auralis','burn'],['Tideglass',34,92,'Brin','regen'],['Cinder Clasp',38,88,'Cindrel','burn'],['Dusk Stitch',30,94,'Gloam','frost'],['Rift Bell',42,83,'Dorsen','shock'],['Sunspoke',36,98,'Echra','boost'],['Iron Psalm',45,88,'Ferrox','barrier'],['Hush Bloom',22,100,'Brin','heal'],['Prism Bite',32,95,'Auralis','multi'],['Ashen Coil',39,90,'Cindrel','speed'],['Vesper Dart',25,100,'Gloam','priority'],['Morrow Ray',50,78,'Dorsen','frost'],['Copper Wake',27,97,'Echra','burn'],['Thorn Oath',44,87,'Brin','drain'],['Furnace Wink',31,93,'Cindrel','crit'],['Quietus Arc',48,84,'Gloam','shock'],['Halo Thread',20,100,'Auralis','heal'],['Stone Choir',52,78,'Ferrox','barrier'],['Meltwater Kiss',29,99,'Brin','regen'],['Dawn Hinge',35,92,'Echra','boost'],['Gloaming',41,90,'Gloam','frost'],['Red Meridian',47,85,'Cindrel','multi'],['Cloud Needle',26,100,'Auralis','priority'],['Vine Circuit',37,90,'Brin','drain'],['Sable Orbit',43,88,'Dorsen','crit'],['Flint Waltz',33,96,'Cindrel','speed'],['Glass Anvil',55,75,'Ferrox','barrier'],['Lilt of Rain',24,100,'Echra','heal'],['Night Ravel',40,91,'Gloam','burn'],['Starless Hook',46,82,'Dorsen','shock'],['Brass Comet',35,95,'Echra','multi'],['Root Signal',30,98,'Brin','regen'],['Ember Veil',43,88,'Cindrel','frost'],['Blue Hour',27,100,'Auralis','boost'],['Gravemark',49,85,'Ferrox','priority'],['Pale Chime',38,90,'Dorsen','crit'],['Crown of Dust',32,96,'Gloam','drain'],['Sunken Spark',45,89,'Echra','burn'],['Feral Syntax',39,94,'Auralis','multi'],['Bellflower Null',58,72,'Gloam','shock']];
const species=[
['Mirelet','Auralis','A tiny lantern that remembers routes no map can hold.',44,35,32,48,'Lumen Pin',18],['Vaskel','Brin','Its glassy fronds hum when rain is approaching.',50,30,40,38,'Tideglass',20],['Clyrax','Cindrel','Clyrax stores warm sparks beneath its shell for winter dreams.',42,48,27,42,'Cinder Clasp',21],['Orrivane','Dorsen','A patient ribbon-creature that knots stray echoes into music.',39,38,30,51,'Dusk Stitch',22],['Nimbral','Gloam','Nimbral dims the stars around it so lost travelers can rest.',48,32,38,34,'Vesper Dart',19],['Kestrelune','Echra','Its mirrored face shows the sky from one minute ahead.',41,43,35,45,'Sunspoke',23],['Brontide','Ferrox','A walking bell of ironwood, built by a vanished weather guild.',62,52,55,20,'Iron Psalm',25],['Serefin','Auralis','Serefin glides through dust and leaves a clean note in the air.',45,39,29,60,'Prism Bite',24],['Pollenox','Brin','A friendly cloud of motes that shares its stored daylight.',55,36,42,30,'Hush Bloom',26],['Caskroot','Brin','Caskroot grows around forgotten tools and calls them family.',64,42,59,18,'Thorn Oath',27],['Veyra','Cindrel','Veyra dances on hot stone, never touching the same place twice.',38,55,25,64,'Ashen Coil',28],['Talm','Dorsen','Talm hears the shape of an unopened door.',47,50,33,54,'Morrow Ray',29],['Rindle','Echra','Rindle paints small suns on everything it trusts.',52,45,38,47,'Dawn Hinge',30],['Mournix','Gloam','Mournix is a soft shadow that collects unfinished goodbyes.',54,47,46,36,'Gloaming',31],['Caldrake','Cindrel','Caldrake sings in furnaces and mistakes smoke for applause.',58,58,39,40,'Red Meridian',32],['Quillik','Ferrox','Quillik folds its metal wings into precise little shelters.',66,44,67,25,'Stone Choir',33],['Ossel','Auralis','Ossel only appears where a promise has been kept.',43,51,35,62,'Cloud Needle',34],['Tremoril','Ferrox','Tremoril maps the world by tapping its feet against bedrock.',72,63,70,15,'Gravemark',35]];
const zones=[['Morrow Fen','#142b46','#4a9a91','Mosslight reedways. First steps are watched by the gentle Fenward.','Auralis'],['Copper Rise','#44243e','#c2675f','Red terraces where old instruments grow from the ground.','Cindrel'],['Hush Meridian','#182d4c','#6b72c7','A silent salt plane crossed by patient shadow winds.','Gloam'],['Aster Vault','#3b2a54','#bb86d6','A suspended garden beneath a ceiling of broken moons.','Dorsen'],['Iron Orchard','#343238','#bca26c','A metallic grove whose fruit rings when struck.','Ferrox'],['The Far Loom','#173943','#73d2bc','At the edge of the chart, every affinity overlaps.','Echra']];
const forms=[['Mirelet','Mirelume','Auralis',34],['Vaskel','Vaskora','Brin',34],['Clyrax','Clyrune','Cindrel',35],['Orrivane','Orrivox','Dorsen',36],['Nimbral','Nimbrisk','Gloam',33],['Kestrelune','Kestrelux','Echra',37],['Brontide','Brontitan','Ferrox',38],['Serefin','Serefane','Auralis',36],['Caldrake','Caldracon','Cindrel',40]];
let state, player={x:0,y:0}, pressed={}, audio=null, ambient=[];
function blankState(){return {zone:0,shards:80,bonds:0,unlocked:0,anchors:0,post:false,team:[],seen:[],bag:{snare:8, prism:2, salve:5, catalyst:1},story:0,shrines:[false,false,false,false,false,false],tutorial:true}}
function makeMon(id,lv=3){let s=species[id],m={id,name:s[0],type:s[1],lore:s[2],level:lv,hp:s[3]+lv*5,max:s[3]+lv*5,atk:s[4]+lv*2,def:s[5]+lv*2,spd:s[6]+lv*2,moves:[...new Set([s[7],moves[(id*3+3)%moves.length][0],moves[(id*3+8)%moves.length][0],moves[(id*3+15)%moves.length][0]])],statuses:{},exp:0};return m}
function save(){localStorage.setItem('vellum-binding',JSON.stringify({state,player}))}function load(){try{let x=JSON.parse(localStorage.getItem('vellum-binding'));if(x){state=x.state;player=x.player;return true}}catch(e){}return false}
function switchScreen(id){$$('.screen').forEach(x=>x.classList.remove('show'));$('#'+id).classList.add('show')}
function toast(t){let e=$('#toast');e.textContent=t;e.classList.add('show');setTimeout(()=>e.classList.remove('show'),2400)}
function newGame(){state=blankState();state.team=[makeMon(0,4)];state.seen=[0];player={x:0,y:0};save();audioStart();switchScreen('world');narrative('FIELD NOTE 01','The vellum under your hand is warm. It has no ink, but it points toward a living signal. Walk to find the first bond.','#173c4b');updateWorld()}
function continueGame(){if(load()){audioStart();switchScreen('world');updateWorld();toast('The last shrine remembers you.')}else newGame()}
function narrative(title,text,color){let m=$('#modalContent');$('#modal').classList.add('show');m.innerHTML=`<div style="padding:18px 5px;text-align:center;border-radius:10px;background:linear-gradient(135deg,${color},transparent)"><h2>${title}</h2><p class="small" style="font-size:15px">${text}</p><button class="btn primary" onclick="closeModal()">CONTINUE</button></div>`}
function closeModal(){$('#modal').classList.remove('show')};window.closeModal=closeModal;
function audioStart(){if(audio)return;try{audio=new AudioContext();let o=audio.createOscillator(),g=audio.createGain();o.type='sine';o.frequency.value=110;g.gain.value=.012;o.connect(g);g.connect(audio.destination);o.start()}catch(e){}}
function updateWorld(){let z=zones[state.zone];$('#zoneName').textContent=z[0];$('#zoneText').textContent=z[3];$('#shards').textContent=state.shards;$('#bonds').textContent=state.bonds+'/8';$('#levelBadge').textContent='FIELD '+(state.zone+1);drawWorld()}
function drawWorld(){let c=$('#worldCanvas'),ctx=c.getContext('2d'),d=devicePixelRatio||1,w=innerWidth,h=innerHeight;c.width=w*d;c.height=h*d;ctx.scale(d,d);let z=zones[state.zone],gr=ctx.createLinearGradient(0,0,w,h);gr.addColorStop(0,z[1]);gr.addColorStop(1,z[2]);ctx.fillStyle=gr;ctx.fillRect(0,0,w,h);ctx.globalAlpha=.22;for(let i=0;i<90;i++){let x=(i*97+state.zone*31)%w,y=(i*53+Math.sin(i)*40+performance.now()/30)%h;ctx.fillStyle=i%2?'#fff':'#75f2c2';ctx.beginPath();ctx.arc(x,y,1+(i%4),0,7);ctx.fill()}ctx.globalAlpha=1;let tw=44,ox=w/2-player.x*tw,oy=h/2-player.y*tw;for(let y=-5;y<6;y++)for(let x=-8;x<9;x++){let px=ox+x*tw,py=oy+y*tw;ctx.strokeStyle='#ffffff12';ctx.strokeRect(px,py,tw,tw);if((x+player.x)%7===0&&(y+player.y)%6===0){ctx.fillStyle='#ffffff13';ctx.beginPath();ctx.arc(px+22,py+22,10,0,7);ctx.fill()}}ctx.fillStyle=typeColor[z[4]];ctx.shadowBlur=20;ctx.shadowColor=ctx.fillStyle;ctx.beginPath();ctx.arc(w/2,h/2,10,0,7);ctx.fill();ctx.shadowBlur=0;ctx.fillStyle='#fff';ctx.font='12px system-ui';ctx.fillText('YOU',w/2-13,h/2-19);ctx.fillStyle='#ffd875';ctx.fillText('✦ SHRINE',w/2+35,h/2-20)}
function move(dx,dy){if($('#world').classList.contains('show')){player.x=clamp(player.x+dx,-7,7);player.y=clamp(player.y+dy,-5,5);drawWorld();if(Math.random()<.16+state.zone*.025)wildEncounter();if(Math.abs(player.x)+Math.abs(player.y)>8&&Math.random()<.045){state.shards+=rnd(3,11);toast('You found loose luminance shards.');updateWorld()} }}
function wildEncounter(){if($('#encounter').style.display==='flex')return;let choices=species.filter((s,i)=>state.zone*3+i<18);let id=choices[rnd(0,choices.length-1)]?species.indexOf(choices[rnd(0,choices.length-1)]):0;let lv=clamp(rnd(2+state.zone*2,7+state.zone*4)+(state.post?8:0),1,50);let m=makeMon(id,lv);$('#encIcon').textContent=['◈','◉','✦','◇','⬡','✧'][id%6];$('#encTitle').textContent=m.name+' surfaced';$('#encText').textContent=m.lore;$('#encounter').style.display='flex';$('#encFight').onclick=()=>{$('#encounter').style.display='none';startBattle(m)} }
function startBattle(enemy){battle={enemy,ally:state.team[0],turn:true,over:false,isAnchor:!!enemy.isAnchor};battle.enemy.hp=battle.enemy.max;battle.ally.hp=Math.min(battle.ally.hp,battle.ally.max);switchScreen('battle');renderBattle();log('A wild '+enemy.name+' enters the thread.');drawBattle()}
let battle=null;
function log(t){$('#battleLog').textContent=t}
function renderBattle(){let a=battle.ally,e=battle.enemy;$('#allyName').textContent=a.name;$('#allyLv').textContent='Lv '+a.level;$('#allyHp').style.width=100*a.hp/a.max+'%';$('#allyHpText').textContent=Math.max(0,a.hp)+' / '+a.max;$('#enemyName').textContent=e.name;$('#enemyLv').textContent='Lv '+e.level;$('#enemyHp').style.width=100*e.hp/e.max+'%';$('#enemyHpText').textContent=Math.max(0,e.hp)+' / '+e.max;$('#moves').innerHTML=a.moves.map(n=>{let m=moves.find(x=>x[0]===n)||moves[0];return `<button class="movebtn" data-move="${m[0]}"><b>${m[0]}</b><span class="type">${m[3]}</span> <small>POW ${m[1]} · ${m[2]}%</small></button>`}).join('');$$('.movebtn').forEach(b=>b.onclick=()=>useMove(b.dataset.move));}
function mult(a,b){if(strong[a]?.includes(b))return 1.55;if(weak[a]?.includes(b))return .65;if(b==='Gloam'&&a!=='Ferrox')return .55;if(a==='Ferrox'&&b==='Gloam')return 1.25;return 1}
function useMove(name){if(!battle||battle.over)return;let m=moves.find(x=>x[0]===name);battle.turn=false;let a=battle.ally,e=battle.enemy;setTimeout(()=>{if(Math.random()*100>m[2]){log(a.name+' sends '+m[0]+' wide.');enemyTurn();return}let stab=a.type===m[3]?1.25:1;let tm=mult(m[3],e.type);let crit=Math.random()<.09;let dmg=Math.max(1,Math.floor(.4*a.level*a.atk/e.def*tm*stab*(crit?1.5:1)+rnd(0,5)));if(m[4]==='heal'){a.hp=clamp(a.hp+Math.floor(a.max*.22),0,a.max);log(a.name+' restores a strand with '+m[0]+'.')}else{e.hp-=dmg;damage(dmg,crit,tm>1);log(a.name+' used '+m[0]+' for '+dmg+' harm.'+(tm>1?' Resonant!':''));if(m[4]==='multi')e.hp-=Math.floor(dmg*.45);if(m[4]==='burn')e.statuses.burn=3;if(m[4]==='frost')e.statuses.frost=2;if(m[4]==='shock')e.statuses.shock=2;if(m[4]==='boost')a.statuses.boost=3;if(m[4]==='barrier')a.statuses.barrier=1}if(e.hp<=0){winBattle();return}renderBattle();enemyTurn()},240)}
function enemyTurn(){let a=battle.ally,e=battle.enemy;setTimeout(()=>{if(e.statuses.shock&&Math.random()<.35){log(e.name+' crackles, unable to act.');battle.turn=true;return}let em=moves.find(x=>x[0]===e.moves[0])||moves[0];let tm=mult(em[3],a.type),dmg=Math.max(1,Math.floor(.4*e.level*e.atk/a.def*tm*(Math.random()*.25+.9)));if(a.statuses.barrier){a.statuses.barrier=0;dmg=0;log(a.name+' deflects the strike.')}else{a.hp-=dmg;log(e.name+' answers with '+em[0]+' for '+dmg+'.');damage(dmg,false,tm>1)}if(e.statuses.burn){e.hp-=Math.max(1,Math.floor(e.atk/2));e.statuses.burn--}if(a.hp<=0){a.hp=0;let next=state.team.find(x=>x.hp>0);if(next&&next!==a){battle.ally=next;log(a.name+' fades; '+next.name+' takes the thread.')}else{log('Your binding unravels. The shrine pulls you back.');setTimeout(()=>{state.team.forEach(x=>x.hp=x.max);switchScreen('world');updateWorld()},1200);return}}renderBattle();battle.turn=true},500)}
function damage(n,crit,strongHit){let d=document.createElement('div');d.className='damage';d.textContent=(crit?'CRIT ':'')+'-'+n;d.style.left=(innerWidth*.74+Math.random()*45-20)+'px';d.style.top='38%';document.body.appendChild(d);setTimeout(()=>d.remove(),1000);if(strongHit){$('#flash').classList.add('on');setTimeout(()=>$('#flash').classList.remove('on'),350)}}
function winBattle(){battle.over=true;let e=battle.enemy,a=battle.ally,xp=e.level*18+20;if(battle.isAnchor){state.anchors++;state.unlocked=Math.min(5,state.anchors+1);state.post=state.anchors>=4;toast('Anchor broken. A permanent path opens beyond the next veil.')}a.exp+=xp;while(a.exp>=a.level*35&&a.level<50){a.exp-=a.level*35;a.level++;a.max+=5;a.hp=a.max;a.atk+=2;a.def+=2;a.spd+=2}state.shards+=e.level*4;state.seen.push(e.id);log(e.name+' disperses into a readable mark.');setTimeout(()=>capturePrompt(e),850)}
function capturePrompt(e){let quality=state.bag.prism>0?'Prismatic Snare':'Thread Snare';$('#encounter').style.display='flex';$('#encIcon').textContent='◎';$('#encTitle').textContent='Bind the readable mark?';$('#encText').textContent=quality+'s are carried: '+state.bag.prism+' prism / '+state.bag.snare+' thread. Tap when the ring crosses the center.';let btn=$('#encFight');btn.textContent='CAST SNARE';let meter=document.createElement('div');meter.className='captureMeter';meter.innerHTML='<i></i>';$('#encounter .encounterBox').insertBefore(meter,btn);let fill=meter.firstChild,active=false,val=0,dir=1,raf;function tick(){if(!active)return;val+=dir*2.7;if(val>100||val<0)dir*=-1;fill.style.width=val+'%';raf=requestAnimationFrame(tick)}btn.onclick=()=>{if(!active){active=true;btn.textContent='TAP THE CENTER';tick()}else{cancelAnimationFrame(raf);active=false;let dist=Math.abs(val-55),chance=clamp((e.max-e.hp)/e.max*.65+(state.bag.prism>0?.28:.12)-dist/230,0,.97);if(Math.random()<chance){if(state.bag.prism>0)state.bag.prism--;else state.bag.snare--;let caught=makeMon(e.id,e.level);if(state.team.length<6)state.team.push(caught);state.bonds=Math.min(8,new Set(state.team.map(x=>x.id)).size);toast(e.name+' is now a bound companion.');$('#encounter').style.display='none';switchScreen('world');updateWorld();save()}else{if(state.bag.prism>0)state.bag.prism--;else state.bag.snare--;toast('The mark slipped the snare.');$('#encounter').style.display='none';switchScreen('world');updateWorld()}meter.remove();btn.textContent='ENTER THE THREAD'}};}
function drawBattle(){let c=$('#battleCanvas'),ctx=c.getContext('2d'),d=devicePixelRatio||1,w=innerWidth,h=innerHeight;c.width=w*d;c.height=h*d;ctx.scale(d,d);let g=ctx.createLinearGradient(0,0,w,h);g.addColorStop(0,'#322447');g.addColorStop(1,'#11162e');ctx.fillStyle=g;ctx.fillRect(0,0,w,h);ctx.fillStyle='#ffffff12';for(let i=0;i<30;i++){ctx.beginPath();ctx.arc((i*113)%w,100+(i*71)%h,2+(i%4),0,7);ctx.fill()}let draw=(x,y,col,size,flip)=>{ctx.save();ctx.translate(x,y);if(flip)ctx.scale(-1,1);ctx.fillStyle=col;ctx.shadowBlur=30;ctx.shadowColor=col;ctx.beginPath();ctx.moveTo(0,-size);ctx.lineTo(size*.7,0);ctx.lineTo(0,size);ctx.lineTo(-size*.7,0);ctx.closePath();ctx.fill();ctx.fillStyle='#ffffffaa';ctx.beginPath();ctx.arc(-size*.15,-size*.25,size*.2,0,7);ctx.fill();ctx.restore()};draw(w*.73,h*.36,typeColor[battle.enemy.type],65,false);draw(w*.27,h*.59,typeColor[battle.ally.type],76,true)}
function transform(i){let x=state.team[i],f=forms.find(q=>q[0]===x.name);if(!f||x.level<34||!state.bag.catalyst)return;state.bag.catalyst--;x.name=f[1];x.max+=18;x.hp=x.max;x.atk+=8;x.def+=8;x.spd+=6;toast(x.name+' unfolded into a higher pattern.');save();openModal('team')}
function openModal(kind){let m=$('#modalContent');$('#modal').classList.add('show');if(kind==='team'){m.innerHTML='<h2>Bound Companions</h2><div class="grid">'+state.team.map((x,i)=>`<div class="card"><div class="row"><h3>${x.name}</h3><span style="color:${typeColor[x.type]}">${x.type}</span></div><div class="small">Lv ${x.level} · ${x.lore}</div><div class="bar"><i style="width:${100*x.hp/x.max}%"></i></div><div class="small">HP ${x.hp}/${x.max} · ATK ${x.atk} · DEF ${x.def} · SPD ${x.spd}</div>${forms.some(f=>f[0]===x.name)&&x.level>=34&&state.bag.catalyst?`<button class="btn" onclick="transform(${i})">USE CATALYST</button>`:''}</div>`).join('')+'</div>'}else if(kind==='guide'){m.innerHTML='<h2>Field Guide</h2><p class="small">The seven affinities bend one another. Auralis > Brin > Cindrel > Auralis. Dorsen overtakes Auralis and Cindrel; Echra overtakes Dorsen and Brin. Ferrox is a little stronger against all. Gloam resists all except Ferrox.</p><div class="grid">'+species.map((s,i)=>`<div class="card"><h3>${s[0]} <span class="type">${s[1]}</span></h3><div class="small">${s[2]}</div><div class="small">${state.seen.includes(i)?'READABLE · '+s[7]:'UNREADABLE MARK'}</div></div>`).join('')+'</div>'}else if(kind==='menu'){m.innerHTML='<h2>Pause the Thread</h2><div class="grid"><button class="btn" onclick="save();toast(\'Binding inscribed at current shrine.\');closeModal()">SAVE BINDING</button><button class="btn" onclick="openModal(\'bag\')">BAG & CATALYSTS</button><button class="btn" onclick="audioToggle()">MUTE / SOUND</button><button class="btn" onclick="closeModal()">RETURN TO FIELD</button></div>'}else if(kind==='bag'){m.innerHTML=`<h2>Satchel</h2><div class="grid"><div class="card"><h3>Thread Snare ×${state.bag.snare}</h3><div class="small">A practical collection filament.</div></div><div class="card"><h3>Prismatic Snare ×${state.bag.prism}</h3><div class="small">Raises capture chance and makes the mini-game kinder.</div></div><div class="card"><h3>Warm Salve ×${state.bag.salve}</h3><button class="btn" onclick="healParty()">USE AT SHRINE</button></div><div class="card"><h3>Phase Catalyst ×${state.bag.catalyst}</h3><div class="small">At level 34+, transforms nine compatible signatures.</div></div></div>`}}
function healParty(){state.team.forEach(x=>x.hp=x.max);if(state.bag.salve)state.bag.salve--;save();toast('The shrine mends every bond.');closeModal()};function audioToggle(){if(audio)audio.suspend();toast('Ambient channel toggled.');}
function shrine(){state.team.forEach(x=>x.hp=x.max);state.shrines[state.zone]=true;state.shards+=10;save();toast('Shrine rest: party healed, binding saved, +10 shards.');if(state.zone>0&&state.story<4){state.story++;narrative('STORY NODE '+state.story,['The reedways answer your footsteps.','A red instrument rings beneath your palm.','A shadow opens its quiet eye.','The Far Loom begins to turn.'][state.story-1],zones[state.zone][1])}updateWorld()}
function anchor(){if(state.anchors>=4){toast('All four anchors already answer to you.');return}if(state.zone>4||state.zone!==state.anchors){toast('The next anchor waits beyond the unlocked veil.');return}let boss=makeMon((state.zone*3+5)%18,12+state.zone*4);boss.name='ANCHOR · '+zones[state.zone][0];boss.isAnchor=true;startBattle(boss)}
function travel(){if(state.zone<5&&state.anchors>state.zone){state.zone++;player={x:0,y:0};save();updateWorld();narrative('VEIL CROSSED',zones[state.zone][3],zones[state.zone][1])}else toast('The vellum shows no open route yet. Break the local anchor.')} 
function keysInit(){window.onkeydown=e=>{pressed[e.key.toLowerCase()]=true;if(e.key==='ArrowUp'||e.key==='ArrowDown'||e.key==='ArrowLeft'||e.key==='ArrowRight')e.preventDefault();if($('#world').classList.contains('show')){if(e.key==='ArrowUp'||e.key.toLowerCase()==='w')move(0,-1);if(e.key==='ArrowDown'||e.key.toLowerCase()==='s')move(0,1);if(e.key==='ArrowLeft'||e.key.toLowerCase()==='a')move(-1,0);if(e.key==='ArrowRight'||e.key.toLowerCase()==='d')move(1,0)}};$$('.touch button').forEach(b=>{b.addEventListener('touchstart',e=>{e.preventDefault();let k=b.dataset.key;move(k==='ArrowLeft'?-1:k==='ArrowRight'?1:0,k==='ArrowUp'?-1:k==='ArrowDown'?1:0)})})}
$('#newBtn').onclick=newGame;$('#continueBtn').onclick=continueGame;$('#closeModal').onclick=closeModal;$('#pauseBtn').onclick=()=>openModal('menu');$('#teamBtn').onclick=()=>openModal('team');$('#guideBtn').onclick=()=>openModal('guide');$('#anchorBtn').onclick=anchor;$('#travelBtn').onclick=travel;$('#bagBtn').onclick=()=>openModal('bag');$('#switchBtn').onclick=()=>{let i=state.team.indexOf(battle.ally);let n=state.team[(i+1)%state.team.length];if(n.hp>0){battle.ally=n;renderBattle();log('You shift to '+n.name+'.');enemyTurn()}};$('#fleeBtn').onclick=()=>{switchScreen('world');updateWorld();toast('You withdrew to the open field.')};$('#shrineBtn').onclick=shrine;window.addEventListener('resize',()=>{if($('#world').classList.contains('show'))drawWorld();if($('#battle').classList.contains('show')&&battle)drawBattle()});keysInit();
</script></body></html>