// Jersey Scuba Trail — v0.45 Gear Perks + Animated Interior
const W=1280,H=720;
const CAN=document.getElementById('c'), g=CAN.getContext('2d');
g.imageSmoothingEnabled=false;
CAN.setAttribute('tabindex','0');
CAN.addEventListener('pointerdown', ()=>CAN.focus());
CAN.addEventListener('mouseenter', ()=>CAN.focus());

// INPUT
const keys={}; let clickedOnce=false;
addEventListener('keydown',e=>{ if(['ArrowUp','ArrowDown','ArrowLeft','ArrowRight',' ','Enter','Escape'].includes(e.key)) e.preventDefault(); keys[e.key]=true; });
addEventListener('keyup',e=>{ keys[e.key]=false; });
CAN.addEventListener('pointerdown',()=>{ clickedOnce=true; initAudio(); });

// ASSETS
const A={
  title:'assets/title.png', choose_boat:'assets/choose_boat.png', gear:'assets/gear_screen.png',
  ext1:'assets/wreck_exterior.png', ext2:'assets/wreck_exterior_2.png', interior:'assets/wreck_interior.png',
  logo:'assets/logo.png', kevin:'assets/diver_kevin.png', ed:'assets/diver_ed.png',
  shark:'assets/shark.png', lobster:'assets/lobster.png', coin:'assets/coin.png',
  treasure:'assets/treasure.png', hatch:'assets/hatch.png'
};
const IM={}, LO=[];
function loadImage(k,src){ return new Promise(res=>{ const i=new Image(); i.onload=()=>{IM[k]=i;res();}; i.onerror=()=>{IM[k]=i;res();}; i.src=src; }); }
async function preload(){ for(const [k,src] of Object.entries(A)) LO.push(loadImage(k,src)); await Promise.all(LO); }
function drawCentered(img){ const w=img.width||512,h=img.height||288; const s=Math.min(W/w,H/h),dw=w*s,dh=h*s; g.drawImage(img,(W-dw)/2,(H-dh)/2,dw,dh); }
function txt(t,x,y,s=24,c='#fff',align='center'){ g.font=`bold ${s}px monospace`; g.textAlign=align; g.fillStyle=c; g.fillText(t,x,y); }

// AUDIO
let audioCtx, master, musicGain, sfxGain, booted=false;
function initAudio(){ if(booted) return; audioCtx=new (window.AudioContext||window.webkitAudioContext)(); master=audioCtx.createGain(); master.gain.value=0.9; master.connect(audioCtx.destination); musicGain=audioCtx.createGain(); musicGain.gain.value=0.22; musicGain.connect(master); sfxGain=audioCtx.createGain(); sfxGain.gain.value=0.7; sfxGain.connect(master); startMusic(); booted=true; }
function beep(f,d=0.12,v=0.8,t='square'){ if(!audioCtx) return; const o=audioCtx.createOscillator(), g1=audioCtx.createGain(); o.type=t; o.frequency.value=f; o.connect(g1); g1.connect(sfxGain); g1.gain.value=v; g1.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime+d); o.start(); o.stop(audioCtx.currentTime+d); }
function startMusic(){ const base=196, seq=[0,3,7,10,0,5,7,12]; let i=0; function tick(){ if(!audioCtx) return; const o=audioCtx.createOscillator(), g1=audioCtx.createGain(); o.type='triangle'; o.frequency.value=base*Math.pow(2,seq[i%seq.length]/12); g1.gain.value=0.13; o.connect(g1); g1.connect(musicGain); o.start(); o.stop(audioCtx.currentTime+0.18); i++; setTimeout(tick, 220);} setTimeout(tick, 320); }

// GAME STATE
const ST={TITLE:0, BOAT:1, DIVER:2, GEAR:3, MAP:4, EXTERIOR:5, INTERIOR:6, SUMMARY:7};
let state=ST.TITLE, camX=0;
let boat=0, diver='kevin', selectedWreck=0;
let gear={reel:false,smb:false,camera:false,speargun:false,knife:false,buddy:false};
let score=0;

const wrecks=[
  {name:'Stolt', objective:'Recover helm wheel', diff:2},
  {name:'Tolten', objective:'Photograph boilers', diff:2},
  {name:'Great Isaac', objective:'Tag two lobster dens', diff:3},
  {name:'Algol', objective:'Map the starboard corridor', diff:4},
  {name:'Resor', objective:'Retrieve cargo tag', diff:3},
  {name:'Mohawk', objective:'Find the bell mount', diff:4},
  {name:'Asphalto', objective:'Locate pump room hatch', diff:4},
  {name:'Random Barge', objective:'Collect 10 coins', diff:1},
];

// RUNTIME
let player, items=[], hazards=[], hatch={x:1100,y:420,w:110,h:130,img:'hatch'};
const clamp=(v,a,b)=>Math.max(a,Math.min(b,v));
const tNow=()=>performance.now()/1000;
function resetCam(){ camX=0; }

// TITLE
function screenTitle(){ drawCentered(IM.title); txt('Press ENTER or CLICK', W/2, H-60, 26); if(clickedOnce||keys['Enter']||keys[' ']){ clickedOnce=false; keys['Enter']=keys[' ']=false; initAudio(); state=ST.BOAT; beep(880); } }

// BOAT
function screenBoat(){ drawCentered(IM.choose_boat); txt('◄ ►  ENTER', W/2, H-50, 24); if(keys['ArrowLeft']){boat=(boat+2)%3; keys['ArrowLeft']=false; beep(660,0.08);} if(keys['ArrowRight']){boat=(boat+1)%3; keys['ArrowRight']=false; beep(660,0.08);} txt(['GYPSY BLOOD','INDY II','DINA DEE'][boat], W/2, H-90, 28, '#ff0'); if(keys['Enter']){keys['Enter']=false; state=ST.DIVER; beep(990);} }

// DIVER
function screenDiver(){ g.fillStyle='#04212e'; g.fillRect(0,0,W,H); txt('CHOOSE YOUR DIVER', W/2, 90, 44, '#ffde59'); g.drawImage(IM.kevin, W/2-320, 150, 256, 256); g.drawImage(IM.ed, W/2+64, 150, 256, 256); txt('Kevin (Rebreather: stealth+ O2+)', W/2-200, 440, 22, diver==='kevin'?'#0f0':'#fff','left'); txt('Ed (Doubles: O2++ sturdy)', W/2+200, 440, 22, diver==='ed'?'#0f0':'#fff','right'); txt('◄ ► switch • ENTER confirm', W/2, H-60, 24); if(keys['ArrowLeft']||keys['ArrowRight']){ diver=(diver==='kevin'?'ed':'kevin'); keys['ArrowLeft']=keys['ArrowRight']=false; beep(700,0.08);} if(keys['Enter']){keys['Enter']=false; state=ST.GEAR; beep(1000,0.1);} }

// GEAR (interactive)
let gearFocus=0; const gearList=[
  {id:'reel', label:'Wreck reel', hint:'+Nav arrow to hatch/interior'},
  {id:'smb', label:'SMB', hint:'+Safety (HP buffer)'},
  {id:'camera', label:'Camera', hint:'+Score bonus +Treasure value'},
  {id:'speargun', label:'Spear gun', hint:'Space: stun shark (cooldown)'},
  {id:'knife', label:'Knife', hint:'Reduce trap dmg / cut free'},
  {id:'buddy', label:'Dive buddy', hint:'One revive on 0 HP'},
];
function screenGear(){
  // dimmed background using gear image
  drawCentered(IM.gear); g.fillStyle='rgba(0,0,0,0.45)'; g.fillRect(0,0,W,H);
  txt('YOU HAVE 10 DIVES PLANNED — CHOOSE WISELY', W/2, 70, 24, '#ffde59');
  const cols=3, rows=2, startX=220, startY=160, cw=320, ch=180;
  for(let i=0;i<gearList.length;i++){
    const r=Math.floor(i/cols), c=i%cols, x=startX+c*cw, y=startY+r*ch;
    const selected=gear[gearList[i].id]; const hot=i===gearFocus;
    // card
    g.fillStyle= selected? '#174f6a':'#0b2a3b'; g.fillRect(x,y,260,130);
    g.strokeStyle= hot? '#fff36e' : '#49f3d0'; g.lineWidth=2; g.strokeRect(x+1,y+1,258,128); g.lineWidth=1;
    txt(gearList[i].label, x+10, y+36, 22, '#e9ffee','left');
    txt(gearList[i].hint, x+10, y+78, 16, '#bfe','left');
    txt(selected?'[ON]':'[OFF]', x+245, y+118, 16, selected?'#7CFF68':'#ddd','right');
  }
  // instructions
  txt('Arrows to move • ENTER toggle • SPACE to continue', W/2, H-50, 22, '#fff');
  // input
  if(keys['ArrowRight']){ gearFocus=(gearFocus+1)%gearList.length; keys['ArrowRight']=false; beep(760,0.08); }
  if(keys['ArrowLeft']) { gearFocus=(gearFocus+gearList.length-1)%gearList.length; keys['ArrowLeft']=false; beep(760,0.08); }
  if(keys['ArrowUp'])   { gearFocus=(gearFocus+gearList.length-3)%gearList.length; keys['ArrowUp']=false; beep(760,0.08); }
  if(keys['ArrowDown']) { gearFocus=(gearFocus+3)%gearList.length; keys['ArrowDown']=false; beep(760,0.08); }
  if(keys['Enter'])     { keys['Enter']=false; const id=gearList[gearFocus].id; gear[id]=!gear[id]; beep(1020,0.12); }
  if(keys[' '])         { keys[' ']=false; state=ST.MAP; beep(1200,0.12); }
}

// MAP
function screenMap(){
  g.fillStyle='#032230'; g.fillRect(0,0,W,H);
  txt('SELECT WRECK', W/2, 60, 44, '#ffde59');
  // simple NJ block & list
  g.fillStyle='#1c7aa8'; g.fillRect(0,120,W,H-240);
  g.fillStyle='#2aa34a'; g.fillRect(580,140,140,420);
  wrecks.forEach((w,i)=>{ const c=i===selectedWreck?'#ff0':'#fff'; txt('⛴', 760-(i*20), 200+(i*42), 26, c,'left'); txt(w.name, 800, 200+(i*42), 20, c,'left'); });
  const w = wrecks[selectedWreck];
  txt('Objective: '+w.objective, W/2, H-120, 24,'#fff');
  txt('◄ ► to cycle • ENTER to dive', W/2, H-60, 22, '#fff');
  if(keys['ArrowRight']){selectedWreck=(selectedWreck+1)%wrecks.length; keys['ArrowRight']=false; beep(760,0.08);}
  if(keys['ArrowLeft']) {selectedWreck=(selectedWreck+wrecks.length-1)%wrecks.length; keys['ArrowLeft']=false; beep(760,0.08);}
  if(keys['Enter'])     {keys['Enter']=false; resetCam(); setupExterior(); state=ST.EXTERIOR; beep(1200,0.12);}
}

// EXTERIOR
let levelWidth, speargunCD=0;
function setupExterior(){
  const diff=wrecks[selectedWreck].diff||2;
  levelWidth = 2800 + diff*600;
  const baseO2 = diver==='ed'? 190: 150;
  player={x:80,y:H/2, air:baseO2, hp: 3+(gear.smb?1:0), img:(diver==='kevin'?'kevin':'ed'),
          speed:(diver==='kevin'?210:175), turn:(diver==='kevin'?1.0:0.85)};
  score=0; speargunCD=0;
  items=[]; hazards=[];
  for(let i=0;i<8+diff*2;i++){
    const type = Math.random()<0.7?'coin':(Math.random()<0.5?'lobster':'treasure');
    items.push({x: 300+Math.random()*(levelWidth-600), y: 200+Math.random()*420, img:type, w: type==='coin'?42:(type==='lobster'?54:64), h: type==='coin'?42:(type==='lobster'?54:64), type});
  }
  for(let i=0;i<2+diff;i++){
    const dir=Math.random()<.5?-1:1;
    hazards.push({x: 600+Math.random()*(levelWidth-800), y: 220+Math.random()*360, vx: dir* (80+Math.random()*60), vy:0, state:'patrol', timer:0});
  }
  hatch={x: levelWidth-200, y: H/2-10, w:120, h:120, img:'hatch'};
}
function exteriorPhysics(dt){
  const sp=player.speed*dt, tv=player.turn;
  if(keys['ArrowLeft'])  player.x-=sp;
  if(keys['ArrowRight']) player.x+=sp;
  if(keys['ArrowUp'])    player.y-=sp*tv;
  if(keys['ArrowDown'])  player.y+=sp*tv;
  player.x=clamp(player.x,0,levelWidth-60); player.y=clamp(player.y,120,H-120);
  camX=clamp(player.x - W*0.4, 0, levelWidth-W);
  // O2 drain
  const drainBase = (diver==='kevin'? 0.7: 0.9);
  player.air=Math.max(0, player.air - dt*drainBase);
  // speargun stun
  speargunCD=Math.max(0, speargunCD-dt);
  if(gear.speargun && keys[' '] && speargunCD<=0){
    keys[' ']=false; speargunCD=1.6; // cooldown
    hazards.forEach(h=>{ if(Math.abs(player.x-h.x)<220 && Math.abs(player.y-h.y)<90){ h.state='stun'; h.timer=1.2; } });
    beep(300,0.1,'sawtooth');
  }
  // shark AI
  const aggroR = (diver==='kevin'? 130: 190);
  hazards.forEach(sh=>{
    if(sh.state==='stun'){ sh.timer-=dt; if(sh.timer<=0){ sh.state='patrol'; } return; }
    const dx = (player.x - sh.x), dy=(player.y - sh.y), dist=Math.hypot(dx,dy);
    if (sh.state==='patrol'){
      sh.x += (dx>0?1:-1)*50*dt; // slow patrol drift toward diver
      sh.y += Math.sin(tNow()*2 + sh.x*0.01)*20*dt;
      if(dist < aggroR){ sh.state='lunge'; sh.vx = (dx>0?1:-1) * (160+Math.random()*120); sh.vy = (dy>0?1:-1) * (60+Math.random()*40); sh.timer=0.7; }
    } else { // lunge
      sh.x += sh.vx*dt; sh.y += sh.vy*dt; sh.timer -= dt;
      if(sh.timer<=0){ sh.state='patrol'; sh.vy=0; }
    }
    // collision
    if (Math.abs(player.x-sh.x)<70 && Math.abs(player.y-sh.y)<50){
      // knife reduces damage
      const dmg = gear.knife? 0.3 : 0.5;
      player.air = Math.max(0, player.air - 10*dt*(gear.knife?0.8:1));
      player.hp = Math.max(0, player.hp - dmg*dt);
      if(Math.random()<0.12) beep(120,0.18);
    }
  });
  // collect items
  items = items.filter(it=>{
    const hit = Math.abs(player.x-it.x)<40 && Math.abs(player.y-it.y)<40;
    if(hit){
      let val=1;
      if(it.type==='coin'){ val = gear.camera?2:1; score += val; beep(1200,0.08); }
      if(it.type==='lobster'){ val = gear.camera?2:1; score += val; beep(800,0.08); }
      if(it.type==='treasure'){ val = gear.camera?5:3; score += val; beep(1000,0.12); }
      return false;
    }
    return true;
  });
}
function parallax(bg){
  const img = bg, scale = H/(img.height||H), w=(img.width||W)*scale, h=H;
  let x = -((camX*0.5)%w);
  for(let i=-1;i<Math.ceil(W/w)+1;i++) g.drawImage(img, x+i*w, 0, w, h);
}
function screenExterior(dt){
  const bg = (Math.floor(performance.now()/3000)%2)? IM.ext2 : IM.ext1;
  parallax(bg);
  exteriorPhysics(dt);
  // items
  items.forEach(it=> g.drawImage(IM[it.img], it.x-camX- it.w/2, it.y- it.h/2, it.w, it.h));
  // sharks
  hazards.forEach(h=>{
    const pulse = h.state==='stun'? (1+Math.sin(tNow()*12)*0.1):1;
    g.save(); g.translate(h.x-camX, h.y);
    g.drawImage(IM.shark, -60*pulse, -40*pulse, 120*pulse,80*pulse);
    g.restore();
  });
  // player
  g.drawImage(IM[diver==='kevin'?'kevin':'ed'], player.x-camX-48, player.y-48, 96,96);
  // hatch (pulse if reel is equipped, + add nav arrow)
  const pulse = gear.reel? (1+Math.sin(tNow()*4)*0.05):1;
  g.drawImage(IM.hatch, hatch.x-camX, hatch.y, hatch.w*pulse, hatch.h*pulse);
  if(gear.reel){
    // draw subtle arrow toward hatch
    const angle = Math.atan2((hatch.y+60)-player.y, (hatch.x)-(player.x));
    const ax = (player.x-camX)+Math.cos(angle)*60, ay=(player.y)+Math.sin(angle)*60;
    g.strokeStyle='#7CFF68'; g.beginPath(); g.moveTo(player.x-camX,player.y); g.lineTo(ax,ay); g.stroke();
  }
  // HUD
  txt(`O2 ${player.air.toFixed(0)}  HP ${player.hp.toFixed(0)}  Score ${score}`, 16, 26, 20, '#bff','left');
  txt(`Speargun ${gear.speargun? (speargunCD>0? 'CD':'Ready') : '—'}`, 16, 50, 18, '#9df','left');
  // enter
  const atHatch = Math.abs(player.x - hatch.x)<90 && Math.abs(player.y - hatch.y)<90;
  txt('ENTER hatch', W/2, 60, 22);
  if(atHatch && keys['Enter']){ keys['Enter']=false; setupInterior(); state=ST.INTERIOR; beep(900,0.12); }
}

// INTERIOR (animated)
let interior, summary;
function setupInterior(){
  const diff = wrecks[selectedWreck].diff;
  interior = {
    width: 2400 + diff*500,
    fogs: [], pipes: [], loot: [], eels: [], bubbles: [], exit:{x: 200 + Math.random()*400, y: H/2+40, w:120,h:120},
    timer: 90 - diff*5, objective: wrecks[selectedWreck].objective,
  };
  // particles
  for(let i=0;i<60;i++){ interior.bubbles.push({x:Math.random()*W, y:Math.random()*H, s:0.5+Math.random()*1.5}); }
  for(let i=0;i<3+diff;i++){ interior.fogs.push({x: 300+Math.random()*(interior.width-600), y: 260+Math.random()*220, r: 80+Math.random()*80}); }
  for(let i=0;i<2+diff;i++){ interior.pipes.push({x: 400+Math.random()*(interior.width-800), y: 300+Math.random()*240, w: 160+Math.random()*120, drip: Math.random()*2}); }
  for(let i=0;i<2+Math.ceil(diff/2);i++){ interior.eels.push({x: 500+Math.random()*(interior.width-800), y: 280+Math.random()*220, dir: Math.random()<.5?-1:1, t:Math.random()*6}); }
  for(let i=0;i<4+diff;i++){ const type=Math.random()<0.6?'coin':'treasure'; interior.loot.push({x: 300+Math.random()*(interior.width-600), y: 240+Math.random()*240, type, w: type==='coin'?40:60, h: type==='coin'?40:60, sparkle:Math.random()*6}); }
  // reset player near entry
  player.x = 120; player.y = H/2; camX=0;
  summary={coins:0, lobsters:0, treasure:0, objective:false, time:0};
}
function interiorPhysics(dt){
  const sp= (diver==='kevin'? 180: 160)*dt;
  if(keys['ArrowLeft'])  player.x-=sp;
  if(keys['ArrowRight']) player.x+=sp;
  if(keys['ArrowUp'])    player.y-=sp*0.9;
  if(keys['ArrowDown'])  player.y+=sp*0.9;
  player.x=clamp(player.x,0,interior.width-60);
  player.y=clamp(player.y,140,H-140);
  camX=clamp(player.x - W*0.4, 0, interior.width-W);
  // timer & O2
  interior.timer = Math.max(0, interior.timer - dt);
  const baseDrain = (diver==='kevin'?0.7:0.9) + (gear.camera?0.0:0.0);
  player.air = Math.max(0, player.air - dt*baseDrain);
  // pipes = obstacles (knife reduces damage)
  interior.pipes.forEach(p=>{
    const left=p.x, right=p.x+p.w, y=p.y;
    if(player.x>left && player.x<right && Math.abs(player.y - y)<40){
      if(keys['ArrowRight']) player.x = left-2;
      if(keys['ArrowLeft'])  player.x = right+2;
      if(Math.random()<0.1){
        const dmg = gear.knife? 0.1:0.2;
        player.hp = Math.max(0, player.hp - dmg);
      }
    }
    p.drip = (p.drip + dt) % 2.5;
  });
  // fog (silt) hurts O2
  interior.fogs.forEach(f=>{
    const dx=player.x - f.x, dy=player.y - f.y; if(dx*dx+dy*dy < (f.r*f.r)){ player.air = Math.max(0, player.air - 0.6*dt); }
  });
  // eel slither & nip (knife reduces dmg)
  interior.eels.forEach(e=>{
    e.t += dt; e.x += e.dir*70*dt; e.y += Math.sin(e.t*2)*12*dt;
    if(e.x<80){e.x=80;e.dir=1;} if(e.x>interior.width-80){e.x=interior.width-80;e.dir=-1;}
    if(Math.abs(player.x-e.x)<60 && Math.abs(player.y-e.y)<40){
      const dmg = gear.knife? 0.2:0.4;
      player.hp = Math.max(0, player.hp - dmg*dt);
      if(Math.random()<0.1) beep(160,0.18);
    }
  });
  // loot with camera bonus
  interior.loot = interior.loot.filter(it=>{
    if(Math.abs(player.x-it.x)<40 && Math.abs(player.y-it.y)<40){
      if(it.type==='coin'){ summary.coins+=(gear.camera?2:1); beep(1200,0.08);} else { summary.treasure+=(gear.camera?2:1); beep(1000,0.12); }
      return false;
    } return true;
  });
  // bubbles rise
  interior.bubbles.forEach(b=>{ b.y -= b.s*20*dt; if(b.y<-10){ b.y=H+10; b.x = (camX + Math.random()*W); } });
  // buddy revive
  if(gear.buddy && player.hp<=0){ player.hp=2; gear.buddy=false; beep(660,0.2,'triangle'); }
}
function screenInterior(dt){
  // background repeat
  const img=IM.interior, scale=H/(img.height||H), w=(img.width||W)*scale, h=H;
  let x = -((camX*0.4)%w); for(let i=-1;i<Math.ceil(W/w)+1;i++) g.drawImage(img, x+i*w, 0, w, h);
  interiorPhysics(dt);
  // fog (silt)
  g.save(); g.globalAlpha=0.25; g.fillStyle='#031219'; interior.fogs.forEach(f=>{ g.beginPath(); g.arc(f.x-camX, f.y, f.r*(1+0.05*Math.sin(tNow()*2)), 0, Math.PI*2); g.fill(); }); g.restore();
  // pipes + drips
  g.fillStyle='#345'; interior.pipes.forEach(p=>{ g.fillRect(p.x-camX, p.y-10, p.w, 20); const dx = (p.x + (p.drip/p.w)*p.w) - camX; const dy = p.y+10 + (Math.sin(tNow()*4+p.drip)*10); g.fillRect(dx, dy, 3, 12); });
  // eels
  interior.eels.forEach(e=>{ g.fillStyle='#6cf'; const wob=1+0.1*Math.sin(e.t*6); g.fillRect(e.x-camX-30*wob, e.y-10*wob, 60*wob, 20*wob); });
  // loot sparkle
  interior.loot.forEach(it=>{
    const spr = it.type==='coin'? IM.coin : IM.treasure;
    const s = 1 + 0.1*Math.sin(tNow()*6 + it.sparkle);
    g.drawImage(spr, it.x-camX - (it.w*s)/2, it.y- (it.h*s)/2, it.w*s, it.h*s);
  });
  // bubbles
  g.save(); g.globalAlpha=0.3; g.fillStyle='#aee';
  interior.bubbles.forEach(b=>{ g.beginPath(); g.arc(b.x-camX, b.y, 2*b.s, 0, Math.PI*2); g.fill(); });
  g.restore();
  // player
  g.drawImage(IM[diver==='kevin'?'kevin':'ed'], player.x-camX-48, player.y-48, 96,96);
  // exit hatch pulse
  const pulse = 1+0.06*Math.sin(tNow()*4);
  g.drawImage(IM.hatch, interior.exit.x-camX, interior.exit.y, interior.exit.w*pulse, interior.exit.h*pulse);
  // HUD
  txt(`O2 ${player.air.toFixed(0)}  HP ${player.hp.toFixed(0)}  Time ${Math.max(0,interior.timer).toFixed(0)}  Score ${score + summary.coins + summary.treasure*3}`, 14, 26, 20, '#bff','left');
  txt(`Objective: ${interior.objective}`, W/2, 50, 22, '#ffde59');
  txt('ENTER to exit', W/2, 80, 18, '#fff');
  // exit conditions
  const nearExit = Math.abs(player.x - interior.exit.x)<90 && Math.abs(player.y - interior.exit.y)<90;
  if(nearExit && keys['Enter'] || player.air<=0 || player.hp<=0 || interior.timer<=0){
    keys['Enter']=false; state=ST.SUMMARY; beep(1100,0.12);
  }
}

// SUMMARY
function screenSummary(){
  g.fillStyle='#003246'; g.fillRect(0,0,W,H);
  const total = score + (summary? (summary.coins + summary.treasure*3) : score);
  txt('DIVE COMPLETE!', W/2, 140, 54, '#7cff7a');
  txt(wrecks[selectedWreck].name, W/2, 200, 28, '#fff');
  txt(`Score: ${total}`, W/2, 240, 26, '#fff');
  // show perks used
  const perks = Object.entries(gear).filter(([k,v])=>v).map(([k])=>k.toUpperCase()).join(', ') || 'NONE';
  txt(`Perks: ${perks}`, W/2, 280, 22, '#9df');
  txt('ENTER for map', W/2, H-80, 28, '#fff');
  if(keys['Enter']){ keys['Enter']=false; state=ST.MAP; beep(1000,0.1); }
}

// LOOP
let last=0;
function loop(ms){
  requestAnimationFrame(loop);
  const dt=Math.min(0.033,(ms-last)/1000||0); last=ms;
  switch(state){
    case ST.TITLE:    screenTitle(); break;
    case ST.BOAT:     screenBoat(); break;
    case ST.DIVER:    screenDiver(); break;
    case ST.GEAR:     screenGear(); break;
    case ST.MAP:      screenMap(); break;
    case ST.EXTERIOR: screenExterior(dt); break;
    case ST.INTERIOR: screenInterior(dt); break;
    case ST.SUMMARY:  screenSummary(); break;
  }
}
preload().then(()=>requestAnimationFrame(loop));
