<!-- SOLID CITY · part 440 -->
 {
    var ok = j && j.loggedIn;
    if (w) { $('sp-login').hidden = true; $('sp-form').style.display = 'none'; $('sp-wlogin').hidden = !!ok; $('sp-wform').hidden = !ok; if (ok) { wallPoll(); if (!IS_TOUCH) $('sp-wmsg').focus(); } else $('sp-wall').textContent = ''; }
    else { $('sp-wlogin').hidden = true; $('sp-wform').hidden = true; $('sp-login').hidden = !!ok; $('sp-form').style.display = ok ? '' : 'none'; }
  });
}
function wallFmt(t) { try { return new Date(t).toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }); } catch (e) { return ''; } }
function wallAdd(m) {
  if (!m || m.id <= wall.last) return; wall.last = m.id;
  var box = $('sp-wall'), atEnd = box.scrollHeight - box.scrollTop - box.clientHeight < 60;
  var d = document.createElement('div'), mine = me && me.name && m.from === me.name; d.className = 'wl' + (mine ? ' me' : '') + (m.king ? ' king' : '');
  var h = document.createElement('b'); h.textContent = (m.king ? '\u265B ' : '') + m.from; d.appendChild(h);
  var tm = document.createElement('small'); tm.textContent = ' ' + wallFmt(m.t); d.appendChild(tm);
  var p = document.createElement('p'); p.textContent = m.text; d.appendChild(p);
  var em = $('sp-wempty'); if (em) em.remove();
  box.appendChild(d); wall.n++;
  spStatus();
  if (atEnd || wall.n < 3) box.scrollTop = 1e9;
}
function spStatus() {   // header status line: 'Soul Print Tower · LIVE · N check-ins' (display only)
  var s = $('sp-status'); if (!s) return;
  s.textContent = 'Soul Print Tower \u00b7 '; var i = document.createElement('i'); i.className = 'sp-live'; i.textContent = 'LIVE'; s.appendChild(i);
  if (wall.n) s.appendChild(document.createTextNode(' \u00b7 ' + wall.n + ' check-in' + (wall.n === 1 ? '' : 's')));
}
function wallPoll() {
  clearTimeout(wall.timer);
  if ($('sp-talk').hidden || wall.tab !== 'wall') return;
  if (!document.hidden && !wall.inflight) { wall.inflight = true; fetch('/api/soluv-room?since=' + wall.last, { credentials: 'same-origin', cache: 'no-store' }).then(function (r) { return r.json(); }).then(function (j) {
    var first = !wall.n; (j.msgs || []).forEach(wallAdd);
    if (first && !wall.n && !$('sp-wempty')) { var e = document.createElement('div'); e.className = 'sys'; e.id = 'sp-wempty'; e.textContent = 'No check-ins in the last 2 days yet. The room clears messages after 2 days.'; $('sp-wall').appendChild(e); }
    if (first) $('sp-wall').scrollTop = 1e9;
  }).catch(function () {}).then(function () { wall.inflight = false; }); }
  wall.timer = setTimeout(wallPoll, 4000);
}
function wallPost(text) {
  text = String(text || '').trim().slice(0, 500); if (!text || wall.busy) return;
  wall.busy = true; $('sp-wsend').disabled = true;
  getMe().then(function (m) {
    return fetch('/api/soluv-room', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': (m && m.csrf) || '' }, body: JSON.stringify({ msg: text, localKey: 'city' + Date.now().toString(36) }) });
  }).then(function (r) { return r.json().catch(function () { return {}; }).then(function (j) { return { s: r.status, j: j }; }); })
    .then(function (o) {
      if (o.j && o.j.ok) { $('sp-wmsg').value = ''; if (o.j.msg) wallAdd(o.j.msg); $('sp-wall').scrollTop = 1e9; }
      else { var e = document.createElement('div'); e.className = 'sys'; e.textContent = o.s === 403 ? 'Session check failed. Reload and try again.' : o.s === 429 ? 'Slow down a little.' : 'Post failed (' + ((o.j && o.j.error) || o.s) + ').'; $('sp-wall').appendChild(e); }
    }).catch(function () {}).then(function () { wall.busy = false; $('sp-wsend').disabled = false; });
}
$('sp-tab-talk').addEventListener('click', function () { spTab('talk'); });
$('sp-tab-wall').addEventListener('click', function () { spTab('wall'); });
$('sp-close2').addEventListener('click', function () { $('sp-close').click(); });
$('sp-wform').addEventListener('submit', function (e) { e.preventDefault(); wallPost($('sp-wmsg').value); });
$('sp-close').addEventListener('click', function () { clearTimeout(wall.timer); });
(function () { // push-to-talk via the browser's speech recognition
  var SR = window.SpeechRecognition || window.webkitSpeechRecognition, rec = null, btn = $('t-ptt'), heard = '';
  if (!SR) { btn.addEventListener('click', function () { logLine('sys', 'Voice input isn\u2019t supported in this browser — type instead.'); }); return; }
  function start(e) { e.preventDefault(); if (rec) return; heard = ''; rec = new SR(); rec.lang = 'en-US'; rec.interimResults = true; rec.continuous = true;
    rec.onresult = function (ev) { var s = ''; for (var i = 0; i < ev.results.length; i++) s += ev.results[i][0].transcript; heard = s; $('t-msg').value = s; };
    rec.onerror = function (ev) { logLine('sys', 'Mic: ' + ev.error); };
    rec.onend = function () { btn.classList.remove('is-on'); var h = heard; rec = null; if (h.trim()) sendTalk(h); };
    try { speechSynthesis.cancel(