<!-- SS · part 11 -->
adding:0 1px;font:600 10px/1 ui-monospace,Menlo,monospace;cursor:pointer;overflow:hidden;white-space:nowrap}
.ss-step.q{background:#141414}
.ss-step.on{background:var(--r);border-color:var(--r2);color:#000}
.ss-step.now{outline:2px solid #fff;outline-offset:-2px}
.ss-pen{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--mu)}
.ss-lines{margin:12px 0 0;padding-left:26px;max-height:46vh;overflow:auto}
.ss-lines li{padding:6px 4px;border-bottom:1px solid #1f1f1f;font-size:14px}
.ss-lines li .ss-ldur{color:var(--mu);font-size:12px;margin-left:6px}
.ss-lines li.warn .ss-ldur{color:#ffb000}
.ss-lines li.ok::marker{color:var(--r2)}
.ss-status{margin-top:10px;color:#ffc7ce;font-size:14px;min-height:1.4em}
.ss-songs{list-style:none;margin:8px 0 0;padding:0}
.ss-song{border-top:1px solid #222;padding:12px 0}
.ss-song-h{display:flex;justify-content:space-between;gap:8px;align-items:baseline}
.ss-song-t{font-weight:700;color:#fff;overflow-wrap:anywhere}
.ss-song-m{color:var(--mu);font-size:12px;white-space:nowrap}
.ss-song audio,#ss-rec-audio{width:100%;margin-top:8px;height:40px}
.ss-song .ss-actions{margin-top:8px}
.ss-dialog{background:var(--c1);color:var(--tx);border:1px solid var(--r);border-radius:14px;padding:0;width:min(560px,94vw);max-height:84vh}
.ss-dialog::backdrop{background:rgba(0,0,0,.75)}
.ss-dlg-in{padding:14px;display:flex;flex-direction:column;gap:10px;max-height:82vh}
.ss-dlg-in h2{margin:0;font-size:17px}
.ss-remix-list{list-style:none;margin:0;padding:0;overflow:auto;flex:1;min-height:120px}
.ss-remix-list li button{width:100%;text-align:left;background:none;border:0;border-bottom:1px solid #222;color:#fff;padding:12px 6px;font:15px inherit;cursor:pointer}
.ss-remix-list li button:hover{background:#1a0306}
.ss-remix-list small{color:var(--mu);margin-left:6px}
.ss-toast{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:9999;background:#000;border:1px solid var(--r);color:#fff;padding:10px 16px;border-radius:12px;max-width:92vw;font-size:14px;box-shadow:0 6px 24px rgba(0,0,0,.6)}
.ss-busy{position:relative;pointer-events:none;opacity:.7}
.ss-busy::after{content:"";position:absolute;right:10px;top:50%;width:14px;height:14px;margin-top:-7px;border:2px solid #000;border-right-color:transparent;border-radius:50%;animation:ss-spin .7s linear infinite}
@keyframes ss-spin{to{transform:rotate(360deg)}}
@media(max-width:420px){.ss-head p{display:none}.ss-transport{top:60px}.ss-pos{margin-left:0}}
/* State colours must beat the site-wide holo/tactical button skins. */
#ss-app .ss-tab.is-on{background:var(--r) !important;color:#000 !important;border-color:var(--r) !important}
#ss-app .ss-step{background:#0c0c0c !important;color:#fff !important;text-transform:none !important;letter-spacing:0 !important;padding:0 1px !important;min-width:0 !important;font:600 10px/1 ui-monospace,Menlo,monospace !important}
#ss-app .ss-step.q{background:#171717 !important}
#ss-app .ss-step.on{background:var(--r) !important;border-color:var(--r2) !important;color:#000 !important;box-shadow:0 0 10px rgba(227,18,44,.55) !important}
#ss-app .ss-step.now{outline:2px solid #fff !important;outline-offset:-2px}
#ss-app .ss-step.on.now{background:#fff !important}
#ss-app .ss-mute.is-on{background:#000 !important;color:var(--r2) !important;border-color:var(--r) !important}
#ss-app .ss-btn-red{background:var(--r) !important;color:#000 !important;border-color:var(--r) !important}
#ss-app .ss-tab{letter-spacing:0 !important;padding:12px 2px !important;font-size:13px !important;min-width:0 !important;overflow:hidden;text-overflow:ellipsis}


===== FILE: public/ss/ss.js =====
/* /ss — SS Music Studio front end (Tone.js, self-hosted). */
(function () {
  'use strict';
  var $ = function (s, r) { return (r || document).querySelector(s); };
  var $$ = function (s, r) { return Array.prototype.slice.call((r || document).querySelectorAll(s)); };
  var app = $('#ss-app'); if (!app) return;
  var IS_ADMIN = app.getAttribute('data-admin') === '1';
  var CSRF = (document.querySelector('meta[name="csrf-token"]') || {}).content || '';
  var STEPS = 16;
  var LS_KEY = 'ss_state_v1';

  // ---------------------------------------------------------------- utils
  function toast(msg, ms) {
    var t = $('#ss-toast'); t.textContent = msg; t.hidden = false;
    clearTimeout(toast._t); toast._t = setTimeout(function () { t.hidden = true; }, ms || 3200);
  }
  function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]; }); }
  function busy(btn, on) { if (!btn) return; btn.disabled = !!on; btn.classList.toggle('ss-busy', !!on); }
  function api(method, url, body) {
    var opt = { method: method, credentials: 'same-origin', headers: { 'x-csrf-token': CSRF, 'Accept': 'application/json' } };
    if (body instanceof FormData) opt.body = body;
    else if (body !== undefined) { opt.headers['Content-T