<!-- SS · part 76 -->
80 : 86; var yy = y0 + LH - 6 - (Math.max(lo, Math.min(hi, m)) - lo) / (hi - lo) * (LH - 12); g.fillRect(x, yy - 1.5, Math.max(1.5, len * spx - 1), 3); });
          });
        }
      }
    });
  }
  $('#es-tl-wrap').addEventListener('click', function (e) {
    var r = this.getBoundingClientRect(), t = (e.clientX - r.left) / r.width * totalSec();
    var it = locate(Math.floor(t / barSec())); if (it && it.i !== S.sel) { S.sel = it.i; refreshSection(); }
    seekTo(t);
  });
  function renderSecButtons() {
    $('#es-secs').innerHTML = PLAN.items.map(function (it) {
      return '<button type="button" class="es-secbtn' + (it.i === S.sel ? ' is-sel' : '') + '" data-i="' + it.i + '" style="flex-grow:' + it.bars + '">' + it.name.toUpperCase() + '<small>' + mmss(it.start * barSec()) + '–' + mmss((it.start + it.bars) * barSec()) + '</small></button>';
    }).join('');
  }
  $('#es-secs').addEventListener('click', function (e) { var b = e.target.closest('.es-secbtn'); if (!b) return; S.sel = +b.getAttribute('data-i'); refreshSection(); if (!playing) seekTo(PLAN.items[S.sel].start * barSec()); else if (loopSection) play(); });
  function moveSel(d) { var i = S.sel, j = i + d; if (j < 0 || j >= S.arrangement.length) return; var a = S.arrangement, t = a[i]; a[i] = a[j]; a[j] = t; S.sel = j; arrChanged(); }
  $('#es-arr-left').addEventListener('click', function () { moveSel(-1); });
  $('#es-arr-right').addEventListener('click', function () { moveSel(1); });
  $('#es-arr-dup').addEventListener('click', function () { if (S.arrangement.length >= 24) return toast('24 sections max.'); S.arrangement.splice(S.sel + 1, 0, selName()); S.sel++; arrChanged(); });
  $('#es-arr-del').addEventListener('click', function () { if (S.arrangement.length <= 1) return; S.arrangement.splice(S.sel, 1); S.sel = Math.min(S.sel, S.arrangement.length - 1); arrChanged(); });
  $('#es-arr-add').addEventListener('change', function () { var v = this.value; this.value = ''; if (!v) return; if (S.arrangement.length >= 24) return toast('24 sections max.'); S.arrangement.splice(S.sel + 1, 0, v); S.sel++; arrChanged(); });
  $('#es-sec-bars').addEventListener('change', function () { var n = Math.max(1, Math.min(16, Math.round(+this.value || 4))); this.value = n; S.sections[selName()].bars = n; arrChanged(); });
  function arrChanged() { save(); PLAN = plan(); refreshSection(); drawCurve(); if (playing) play(Math.min(curPos(), Math.max(0, totalSec() - 0.1))); }

  // ------------------------------------------------------------------ sequencer + piano roll
  function refreshSection() {
    PLAN = plan();
    var n = selName(); $('#es-seq-sec').textContent = n.toUpperCase(); $('#es-sec-sel').value = n; $('#es-sec-bars').value = S.sections[n].bars;
    renderSeq(); renderRoll(); drawTimeline(); save();
  }
  $('#es-sec-sel').addEventListener('change', function () { var n = this.value, i = S.arrangement.indexOf(n); if (i < 0) { S.arrangement.push(n); i = S.arrangement.length - 1; toast(cap(n) + ' added to the end of the song.'); } S.sel = i; refreshSection(); });
  function renderSeq() {
    var p = selPat(), h = '<span></span>';
    for (var i = 0; i < 16; i++) h += '<span class="es-num">' + (i + 1) + '</span>';
    DRUM_ROWS.forEach(function (r) {
      h += '<span class="es-rl">' + r.toUpperCase() + '</span>';
      for (var s = 0; s < 16; s++) h += '<button type="button" class="es-cell' + (p.drums[r][s] ? ' on' : '') + (s % 4 === 0 ? ' q' : '') + '" data-r="' + r + '" data-s="' + s + '" aria-label="' + r + ' step ' + (s + 1) + '"></button>';
    });
    $('#es-seqgrid').innerHTML = h;
  }
  $('#es-seqgrid').addEventListener('click', function (e) {
    var c = e.target.closest('.es-cell'); if (!c) return; var r = c.getAttribute('data-r'), s = +c.getAttribute('data-s'), p = selPat();
    p.drums[r][s] = p.drums[r][s] ? 0 : 1; c.classList.toggle('on'); save(); drawTimeline();
    if (p.drums[r][s] && E && !playing && Tone.getContext().state === 'running') E.inst.drums.play(r, undefined, 0.9);
  });
  function rollRows(lane) {
    if (lane === 'chords') { var ch = (S.chords || []).slice(); diatonic(S.key).forEach(function (c) { if (ch.indexOf(c) < 0) ch.push(c); }); selPat().chords.forEach(function (c) { if (c && ch.indexOf(c) < 0) ch.push(c); }); return ch.slice(0, 14); }
    var rows = scaleNotes(S.key, lane === 'bass' ? [1, 2] : [4, 5]);
    selPat()[lane].forEach(function (n) { if (n && rows.indexOf(n) < 0) rows.push(n); });
    rows.sort(function (a, b) { return noteMidi(a) - noteMidi(b); });
    return rows.reverse();
  }
  function renderRoll() {
    var lane = S.rollLane, p = selPat()[lane], rows = rollRows(lane), h = '';
    $$('#es-roll-lane button').forEach(function (b) { b.classList.toggle('is-on', b.getAttribute('data-lane') === lane); });
    $('#es-roll-snd').innerHTML = SOUNDS[lane].map(function (o) { return '<option' + (S.sounds[lane] === o ? ' selected' : '') + '>' + o + '</option>'; }).join('');
    rows.forEach