<!-- SS · part 34 -->
istener('click', function () { var k = kit(); if (!k) return; var n = prompt('Rename kit', k.name); if (!n) return; k.name = n.slice(0, 60); renderKits(); persist({ kits: D.kits }); });
  $('#es-kt-del').addEventListener('click', function () { var k = kit(); if (!k || !confirm('Delete kit "' + k.name + '"? (Samples stay.)')) return; D.kits = D.kits.filter(function (x) { return x !== k; }); K.kit = D.kits[0] ? D.kits[0].id : ''; keep(); renderKits(); renderPads(); persist({ kits: D.kits }); });
  $('#es-kt-save').addEventListener('click', function () { persist({ kits: D.kits }).then(function () { $('#es-kt-save').textContent = 'Save kit'; say('Kit saved on the server.'); }); });
  function persist(body) { return ES.api('POST', '/api/ss/kits/save', body).then(function (j) { D = { samples: j.samples, kits: j.kits, templates: j.templates }; renderKits(); renderTpl(); return j; }).catch(function (x) { say(x.message); }); }
  function refresh() { return ES.api('GET', '/api/ss/kits').then(function (j) { D = { samples: j.samples || [], kits: j.kits || [], templates: j.templates || [] }; renderKits(); renderLib(); renderPads(); renderTpl(); renderProv(); return loadKitBufs(); }).then(rewire); }
  // switches
  $('#es-kt-on').addEventListener('change', function () { K.on = this.checked; keep(); ES.unlock().catch(function () {}).then(loadKitBufs).then(rewire); renderProv(); say(K.on ? 'Your kit now plays the drum, bass, keys and lead lanes (lanes without a pad keep the built-in sound unless My Sounds Only is on).' : 'Built-in sounds restored.'); });
  $('#es-kt-only').addEventListener('change', function () {
    K.only = this.checked; if (K.only && !K.on) { K.on = true; $('#es-kt-on').checked = true; }
    keep(); syncOnly(); loadKitBufs().then(rewire); renderProv();
    if (K.only && aiPatternsPresent()) say('Heads-up: some sections still hold AI-generated patterns. Clear them or program your own beat — the provenance note will say so until you do.');
    else say(K.only ? 'My Sounds Only: built-in synths, AI patterns, AI lyrics/voice and Sing are off. Only your kit and your recordings make sound.' : 'All tools are available again.');
  });
  $('#es-kt-lc').addEventListener('change', function () { K.leadChops = this.checked; keep(); });
  // chopper
  var CH = { id: '', slices: [] };
  function onsets(b, n) {
    var d = b.getChannelData(0), hop = 512, E = [], out = [0];
    for (var i = 0; i + hop < d.length; i += hop) { var e = 0; for (var j = i; j < i + hop; j++) e += d[j] * d[j]; E.push(e); }
    if (n !== 'auto') { var k = +n; for (var q = 1; q < k; q++) out.push(q / k); }
    else { var gap = Math.ceil(0.09 * b.sampleRate / hop), last = -gap, mx = Math.max.apply(null, E) || 1; for (var x = 8; x < E.length; x++) { var avg = 0; for (var y = x - 8; y < x; y++) avg += E[y]; avg /= 8; if (E[x] > avg * 3 && E[x] > mx * 0.02 && x - last >= gap) { out.push(x * hop / d.length); last = x; if (out.length >= 16) break; } } }
    return out.map(function (s, i) { return [s, i + 1 < out.length ? out[i + 1] : 1]; }).filter(function (s) { return s[1] - s[0] > 0.005; });
  }
  function drawChop() {
    var cv = $('#es-kt-cv'), w = cv.clientWidth, h = cv.clientHeight; if (!w) return; var dpr = window.devicePixelRatio || 1; cv.width = w * dpr; cv.height = h * dpr; var g = cv.getContext('2d'); g.setTransform(dpr, 0, 0, dpr, 0, 0); g.fillStyle = '#050505'; g.fillRect(0, 0, w, h);
    var b = BUF[CH.id]; if (!b) { g.fillStyle = '#777'; g.font = '11px sans-serif'; g.fillText('Pick a sample and press Chop.', 8, h / 2); return; }
    var d = b.getChannelData(0); g.fillStyle = '#e3122c'; for (var x = 0; x < w; x++) { var i0 = Math.floor(x / w * d.length), i1 = Math.floor((x + 1) / w * d.length), m = 0; for (var i = i0; i < i1; i += 8) m = Math.max(m, Math.abs(d[i])); g.fillRect(x, h / 2 - m * h / 2, 1, Math.max(1, m * h)); }
    CH.slices.forEach(function (s, i) { g.fillStyle = '#e8c36a'; g.fillRect(s[0] * w, 0, 1, h); g.font = '10px sans-serif'; g.fillText(String(i + 1), s[0] * w + 3, 11); });
  }
  $('#es-kt-chop').addEventListener('click', function () { var id = $('#es-kt-chs').value; if (!id) return say('Upload a sample first.'); ES.unlock().catch(function () {}).then(function () { return loadBuf(id); }).then(function (b) { CH = { id: id, slices: onsets(b, $('#es-kt-chn').value) }; drawChop(); say(CH.slices.length + ' slices. Tap the wave to hear a slice, then Assign to Chop pad.'); }).catch(function () { say('Could not load that sample.'); }); });
  $('#es-kt-cv').addEventListener('click', function (e) { var b = BUF[CH.id]; if (!b || !CH.slices.length) return; var r = this.getBoundingClientRect(), f = (e.clientX - r.left) / r.width, s = CH.slices.filter(function (x) { return f >= x[0] && f < x[1]; })[0]; if (!s) return; var c = Tone.getContext().rawContext, src = c.createBufferSource(); src.buffer = b; src.connect(c.destination); src.start(0, s[0] * b.duration, (s[1] - s[0]) * b.duration); });
  $('#es-kt-as