<!-- SS · part 110 -->
change', function () { songsLoaded = false; loadSongs(); });
  function loadSongs() {
    if (songsLoaded) return; songsLoaded = true; var ul = $('#es-songlist'); ul.innerHTML = '<li class="es-hint">Loading…</li>';
    api('GET', '/api/ss/songs' + (IS_ADMIN && $('#es-all').checked ? '?all=1' : '')).then(function (j) {
      var songs = j.songs || [];
      ul.innerHTML = songs.length ? songs.map(function (s) {
        return '<li class="es-song" data-id="' + s.id + '"><div class="es-song-h"><span class="es-song-t">' + esc(s.title) + '</span><span class="es-song-m">' + esc((s.owner ? s.owner + ' · ' : '') + s.bpm + ' BPM · ' + s.key + ' · ' + fmtDate(s.created_at)) + '</span></div>' +
          (s.url ? '<audio controls preload="none" src="' + esc(s.url) + '"></audio>' : '<div class="es-hint">Project only (no audio)</div>') +
          '<div class="es-prompt-row"><button type="button" class="es-btn es-btn-sm" data-act="load">Open</button><button type="button" class="es-btn es-btn-sm" data-act="rename">Rename</button>' +
          (s.url ? '<a class="es-btn es-btn-sm" href="' + esc(s.url) + '" download>Download</a>' : '') +
          (IS_ADMIN && s.url ? '<button type="button" class="es-btn es-btn-sm" data-act="radio">Send to Radio</button>' : '') +
          '<button type="button" class="es-btn es-btn-sm" data-act="delete">Delete</button></div></li>';
      }).join('') : '<li class="es-hint">No songs yet. Generate one, then Export.</li>';
    }).catch(function (e) { songsLoaded = false; ul.innerHTML = '<li class="es-hint">' + esc(e.message) + '</li>'; });
  }
  $('#es-songlist').addEventListener('click', function (e) {
    var b = e.target.closest('[data-act]'); if (!b) return; var li = b.closest('.es-song'), id = li.getAttribute('data-id'), act = b.getAttribute('data-act');
    if (act === 'rename') { var t = prompt('New title', li.querySelector('.es-song-t').textContent); if (!t || !t.trim()) return; api('POST', '/api/ss/songs/' + id + '/rename', { title: t.trim() }).then(function () { li.querySelector('.es-song-t').textContent = t.trim(); toast('Renamed.'); }).catch(function (x) { toast(x.message); }); }
    else if (act === 'delete') { if (!confirm('Delete this song for good?')) return; api('POST', '/api/ss/songs/' + id + '/delete', {}).then(function () { li.remove(); toast('Deleted.'); }).catch(function (x) { toast(x.message); }); }
    else if (act === 'radio') { if (!confirm('Add this song to the radio library?')) return; busy(b, true); api('POST', '/api/ss/songs/' + id + '/radio', {}).then(function () { b.textContent = 'On the radio ✓'; toast('Added to the radio library.'); }).catch(function (x) { busy(b, false); toast(x.message); }); }
    else if (act === 'load') {
      api('GET', '/api/ss/songs/' + id).then(function (j) {
        var s = j.song, st = s.studio || {}; stop();
        S.title = s.title; S.lyrics = s.lyrics || ''; S.bpm = s.bpm; S.key = parseKey(s.key).name;
        if (s.sections && s.arrangement && s.arrangement.length) { SECS.forEach(function (n) { if (s.sections[n]) S.sections[n] = { bars: s.sections[n].bars, patterns: normPat(s.sections[n].patterns) }; }); S.arrangement = s.arrangement.slice(); }
        else { SECS.forEach(function (n) { S.sections[n].patterns = normPat(s.patterns); }); }
        ['sounds', 'vols', 'mutes', 'solos', 'master', 'voice'].forEach(function (k) { if (st[k]) S[k] = Object.assign({}, S[k], st[k]); });
        if (typeof st.swing === 'number') S.swing = st.swing; S.chords = st.chords || []; S.suno = st.suno || ''; S.genre = st.genre || ''; S.mood = st.mood || '';
        S.sel = 0; S.ai = clone(S.sections); VOX.clips = {}; seekPos = 0;
        if (E) { ['drums', 'bass', 'chords', 'lead'].forEach(function (t) { setSound(E, t, S.sounds[t]); }); applyMix(E); applyMaster(E); applyVocalFx(E); }
        syncAll(); root.classList.remove('songs-open'); if (window.innerWidth < 1100) setMTab('arrange'); toast('Opened "' + s.title + '". Render vocals to hear the voice.');
      }).catch(function (x) { toast(x.message); });
    }
  });

  // ------------------------------------------------------------------ boot
  function syncAll() { syncTop(); renderChat(); renderBlocks(); renderFaders(); syncKnobs(); syncVoiceUI(); refreshSection(); drawCurve(); idleSpectrum(); }
  syncAll(); checkUnlock();
  window.ES = { S: S, engine: function () { return E; }, unlock: unlock, play: play, stop: stop, isPlaying: function () { return playing; }, curPos: curPos, seekTo: seekTo, totalSec: totalSec, songEnd: songEnd, barSec: barSec, drawTimeline: drawTimeline, toast: toast, api: api, busy: busy, esc: esc, mmss: mmss, wavEncode: wavEncode, renderOffline: renderOffline, setMTab: setMTab, VOX: VOX, vocalPlan: vocalPlan, b64ToBuf: b64ToBuf, analyse: analyse };
  hook('ready', window.ES);
  window.addEventListener('resize', function () { clearTimeout(window.__esR); window.__esR = setTimeout(function () { drawTimeline(); drawCurve(); if (!playing) idleSpectrum();