<!-- SS · part 77 -->
(function (rv) {
      var blk = lane !== 'chords' && /^[A-G](#|b)\d$/.test(rv);
      h += '<span class="es-key' + (blk ? ' blk' : '') + '">' + esc(rv) + '</span>';
      for (var s = 0; s < 16; s++) h += '<button type="button" class="es-cell' + (p[s] === rv ? ' on' : '') + (s % 4 === 0 ? ' q' : '') + '" data-v="' + esc(rv) + '" data-s="' + s + '" aria-label="' + esc(rv) + ' step ' + (s + 1) + '"></button>';
    });
    $('#es-rollgrid').innerHTML = h;
  }
  $('#es-roll-lane').addEventListener('click', function (e) { var b = e.target.closest('button[data-lane]'); if (!b) return; S.rollLane = b.getAttribute('data-lane'); renderRoll(); save(); });
  $('#es-roll-snd').addEventListener('change', function () { S.sounds[S.rollLane] = this.value; setSound(E, S.rollLane, this.value); renderFaders(); save(); });
  $('#es-rollgrid').addEventListener('click', function (e) {
    var c = e.target.closest('.es-cell'); if (!c) return; var lane = S.rollLane, s = +c.getAttribute('data-s'), v = c.getAttribute('data-v'), p = selPat()[lane];
    p[s] = p[s] === v ? null : v; save(); renderRoll(); drawTimeline();
    if (p[s] && E && !playing && Tone.getContext().state === 'running') { try { E.inst[lane].triggerAttackRelease(lane === 'chords' ? chordNotes(v) : v, '8n'); } catch (x) {} }
  });
  $('#es-load-ai').addEventListener('click', function () {
    var n = selName(); if (!S.ai || !S.ai[n]) return toast('Generate a song first — then this restores the AI pattern for ' + cap(n) + '.');
    S.sections[n].patterns = normPat(S.ai[n].patterns); refreshSection(); toast('AI pattern restored for ' + cap(n) + '.');
  });
  $('#es-clear').addEventListener('click', function () { if (!confirm('Clear every lane in ' + cap(selName()) + '?')) return; S.sections[selName()].patterns = emptyPat(); refreshSection(); });
  function regen(track, btn) {
    busy(btn, true);
    api('POST', '/api/ss/pattern', { track: track, section: selName(), key: S.key, bpm: S.bpm, chords: S.chords, genre: S.genre, mood: S.mood, current: selPat()[track] })
      .then(function (j) { var p = selPat(); if (track === 'drums') p.drums = normPat({ drums: j.pattern }).drums; else p[track] = j.pattern; refreshSection(); toast(cap(track) + ' regenerated for ' + cap(selName()) + '.'); })
      .catch(function (e) { toast(e.message, 5000); }).then(function () { busy(btn, false); });
  }
  $$('[data-regen]').forEach(function (b) { b.addEventListener('click', function () { regen(b.getAttribute('data-regen'), b); }); });
  $('#es-roll-regen').addEventListener('click', function () { regen(S.rollLane, this); });
  var kitSel = $('select[data-snd="drums"]');
  kitSel.innerHTML = SOUNDS.drums.map(function (o) { return '<option' + (S.sounds.drums === o ? ' selected' : '') + '>' + o + '</option>'; }).join('');
  kitSel.addEventListener('change', function () { S.sounds.drums = this.value; setSound(E, 'drums', this.value); renderFaders(); save(); });

  // ------------------------------------------------------------------ transport readouts
  var keySel = $('#es-key'); keySel.innerHTML = KEYS.map(function (k) { return '<option>' + k + '</option>'; }).join('');
  function syncTop() { $('#es-bpm').value = S.bpm; $('#es-swing').value = S.swing; if (KEYS.indexOf(S.key) < 0) S.key = parseKey(S.key).name; keySel.value = S.key; }
  $('#es-bpm').addEventListener('change', function () { S.bpm = Math.min(200, Math.max(60, Math.round(+this.value || 92))); this.value = S.bpm; if (E) E.T.bpm.value = S.bpm; save(); drawTimeline(); drawCurve(); if (playing) play(0); });
  $('#es-swing').addEventListener('input', function () { S.swing = +this.value; if (E) E.T.swing = S.swing; save(); });
  keySel.addEventListener('change', function () { S.key = this.value; save(); renderRoll(); });

  // ------------------------------------------------------------------ mixer + master + spectrum
  function renderFaders() {
    $('#es-faders').innerHTML = LANES.map(function (t) {
      return '<div class="es-fader" style="--c:' + LANE_C[t] + '"><b>' + t.toUpperCase() + '</b><input type="range" min="-36" max="6" step="1" value="' + (S.vols[t] || 0) + '" data-fader="' + t + '" aria-label="' + t + ' volume">' +
        '<div class="es-ms"><button type="button" data-mute="' + t + '" class="' + (S.mutes[t] ? 'is-on' : '') + '" title="Mute">M</button><button type="button" data-solo="' + t + '" class="' + (S.solos[t] ? 'is-on' : '') + '" title="Solo">S</button></div>' +
        (t === 'vocals' ? '<span class="es-hint">Kokoro</span>' : '<select data-fsnd="' + t + '" aria-label="' + t + ' sound">' + SOUNDS[t].map(function (o) { return '<option' + (S.sounds[t] === o ? ' selected' : '') + '>' + o + '</option>'; }).join('') + '</select>') + '</div>';
    }).join('');
  }
  $('#es-faders').addEventListener('input', function (e) { var f = e.target.getAttribute('data-fader'); if (!f) return; S.vols[f] = +e.target.value; applyMix(E); save(); });
  $('#es-faders').addEventListener('click', function (e) {