<!-- SOLID BIG ISLAND — the game shell · part 3 -->
n()}).then(d=>{
    songs=d.songs; songs.forEach((s,i)=>{s.i=i; s.hay=norm(s.title+' '+s.lyrics+' #'+s.n)});
    $('#total').textContent=songs.length;
    try{shuffle=localStorage.getItem('sbi_shuffle')==='1'}catch(e){}
    $('#shuf').setAttribute('aria-pressed',shuffle);
    render(); filter('');
  }).catch(e=>{$('#count').textContent='Could not load songs.json ('+e.message+')'});

  function render(){
    list.innerHTML=songs.map(s=>{
      const th=s.thumb?`<img class="thumb" loading="lazy" decoding="async" src="${esc(s.thumb)}" alt="" width="56" height="56">`:`<div class="thumb ph" aria-hidden="true">♛</div>`;
      const lyr=s.excerpt?`<div class="tag">auto-transcribed</div><div class="lyr">${esc(s.excerpt)}</div>`:`<div class="tag">${s.kind==='instrumental'?'instrumental: no vocals transcribed':'no lyrics transcribed'}</div>`;
      return `<li class="song" id="s${s.n}" data-i="${s.i}">
        <div class="line">
          <button class="row" data-act="toggle" aria-expanded="false">${th}
            <div class="meta"><div class="t"><span class="no">${s.n}</span>${esc(s.title)}</div>
            <div class="d">${fdate(s.date)} · ${fmt(s.dur)}</div></div></button>
          <button class="btn" data-act="play" aria-label="Play ${esc(s.title)}"><svg viewBox="0 0 24 24">${PLAY}</svg></button>
        </div>
        <div class="detail">${lyr}<div class="actions">
          <button class="pill" data-act="play">▶ Play</button>
          <a class="pill" href="${esc(s.url)}" target="_blank" rel="noopener">TikTok ↗</a></div></div></li>`}).join('');
  }
  list.addEventListener('click',e=>{
    const b=e.target.closest('[data-act]'); if(!b)return; const li=b.closest('li'); const i=+li.dataset.i;
    if(b.dataset.act==='toggle'){const o=li.classList.toggle('open'); b.setAttribute('aria-expanded',o)}
    else if(b.dataset.act==='play'){ if(i===cur){toggle()} else {history.push(cur); load(i,true)} }
  });

  let qt; q.addEventListener('input',()=>{clearTimeout(qt);qt=setTimeout(()=>filter(q.value),120)});
  function filter(v){
    const terms=norm(v.trim()).replace(/^#/,'').split(/\s+/).filter(Boolean);
    visible=[];
    songs.forEach(s=>{const ok=!terms.length||terms.every(t=>s.hay.includes(t)||String(s.n)===t);
      document.getElementById('s'+s.n).hidden=!ok; if(ok)visible.push(s.i)});
    $('#count').textContent=terms.length?`${visible.length} of ${songs.length} songs match “${v.trim()}”`:`${songs.length} songs · newest first`;
    let em=$('#empty'); if(!visible.length){if(!em){em=document.createElement('li');em.id='empty';em.className='empty';list.appendChild(em)}em.textContent='No song matches that word.'} else if(em) em.remove();
  }

  function load(i,play){
    const s=songs[i]; if(!s)return; cur=i;
    document.querySelectorAll('li.playing').forEach(l=>l.classList.remove('playing'));
    const li=document.getElementById('s'+s.n); li&&li.classList.add('playing');
    audio.src='mp3/'+encodeURIComponent(s.file);
    $('#npTitle').innerHTML=`<span class="no">${s.n}</span>${esc(s.title)}`;
    $('#npSub').textContent=`${fdate(s.date)} · SolidBigIsland`;
    const t=$('#npThumb'); t.outerHTML=s.thumb?`<img class="thumb" id="npThumb" src="${esc(s.thumb)}" alt="">`:`<div class="thumb ph" id="npThumb">♛</div>`;
    $('#dur').textContent=fmt(s.dur); $('#cur').textContent='0:00'; setSeek(0);
    $('#status').textContent='';
    if('mediaSession' in navigator){navigator.mediaSession.metadata=new MediaMetadata({title:s.title,artist:'SolidBigIsland',album:'The Record Studio',
      artwork:s.thumb?[{src:new URL(s.thumb,location.href).href,sizes:'200x200',type:'image/webp'}]:[]})}
    if(play){const p=audio.play(); p&&p.catch(err=>{$('#status').textContent='Tap ▶ to start playback'; icon(false)})}
  }
  function order(){return visible.length?visible:songs.map(s=>s.i)}
  function next(auto){
    const o=order(); if(!o.length)return;
    let n;
    if(shuffle){ if(o.length===1)n=o[0]; else do{n=o[Math.floor(Math.random()*o.length)]}while(n===cur) }
    else { const k=o.indexOf(cur); n=k<0? (o.find(i=>i>cur) ?? o[0]) : o[(k+1)%o.length] }
    history.push(cur); load(n,true);
  }
  function prev(){
    if(audio.currentTime>4){audio.currentTime=0;return}
    if(shuffle&&history.length){let h; while(history.length&&(h=history.pop())<0); if(h>=0)return load(h,true)}
    const o=order(); const k=o.indexOf(cur); load(k<=0?o[o.length-1]:o[k-1],true);
  }
  function toggle(){ if(cur<0){return load(order()[0],true)} if(audio.paused){audio.play().catch(()=>{})}else audio.pause() }
  function icon(p){$('#playIcon').innerHTML=p?PAUSE:PLAY;$('#play').setAttribute('aria-label',p?'Pause':'Play');
    document.querySelectorAll('li.song [data-act=play].btn svg').forEach(s=>s.innerHTML=PLAY);
    if(p&&cur>=0){const b=document.querySelector(`#s${songs[cur].n} .btn[data-act=play] svg`); b&&(b.innerHTML=PAUSE)}}
  function setSeek(f){const s=$('#seek'); s.value=Math.round(f*1000); s.s