<!-- SOLID CITY · part 96 -->
: P.x, z: P.z, label: label });
  }
  // resolutions for open fall sessions: rising is moving again
  for (var i = 0; i < M.citizens.length; i++) {
    var ss = M.citizens[i].session;
    if (ss && ss.ev.type === 'fall' && !ss.outcome) {
      var age = performance.now() - ss.t0, moved = Math.hypot(P.x - ss.ev.x, P.z - ss.ev.z);
      if (moved > 4 || age > 12000) ss.outcome = 'up';
      else if (age > 9000 && moved < 1.5) ss.outcome = 'trapped';
    }
  }
}

/* ---------- the talk panel ---------- */
var panelEl = null, logEl = null, inputEl = null, titleEl = null, opsEl = null, pillEl = null;
function panelCitizen() { return M.panelFor ? M.byId[M.panelFor] : null; }
function buildPanel() {
  pillEl = document.createElement('button');
  pillEl.style.cssText = 'position:fixed;right:14px;bottom:calc(120px + env(safe-area-inset-bottom));z-index:60;display:none;padding:12px 18px;border-radius:999px;border:1px solid #d8a833;background:rgba(16,22,34,.92);color:#e8edf5;font:600 15px system-ui;box-shadow:0 4px 18px rgba(0,0,0,.5)';
  document.body.appendChild(pillEl);
  pillEl.addEventListener('click', function () { if (M.near) openPanel(M.near); });

  panelEl = document.createElement('div');
  panelEl.style.cssText = 'position:fixed;left:50%;transform:translateX(-50%);bottom:calc(12px + env(safe-area-inset-bottom));width:min(560px,94vw);z-index:61;display:none;background:rgba(13,18,28,.96);border:1px solid #d8a833;border-radius:16px;color:#e8edf5;font:14px/1.45 system-ui;padding:12px 14px;box-shadow:0 10px 40px rgba(0,0,0,.6)';
  panelEl.innerHTML =
    '<div style="display:flex;justify-content:space-between;align-items:baseline;gap:8px">' +
      '<div id="cz-title" style="font-weight:700;font-size:16px"></div>' +
      '<button id="cz-close" style="background:none;border:none;color:#9aa4b5;font-size:20px;cursor:pointer">✕</button></div>' +
    '<div id="cz-ops" style="color:#9aa4b5;font-size:12px;margin:2px 0 8px"></div>' +
    '<div id="cz-log" style="max-height:180px;overflow-y:auto;display:flex;flex-direction:column;gap:6px;margin-bottom:10px"></div>' +
    '<div id="cz-inbox" style="display:none;margin-bottom:8px;color:#ffd97a;font-size:13px"></div>' +
    '<div style="display:flex;gap:8px">' +
      '<input id="cz-input" type="text" maxlength="600" placeholder="Speak…" style="flex:1;background:#0d1420;border:1px solid #3a465c;border-radius:10px;color:#e8edf5;padding:10px 12px;font-size:15px"/>' +
      '<button id="cz-mic" style="display:none;background:#232c3d;border:1px solid #3a465c;border-radius:10px;color:#e8edf5;padding:0 12px;font-size:16px">🎙</button>' +
      '<button id="cz-send" style="background:#d8a833;border:none;border-radius:10px;color:#141a26;font-weight:700;padding:0 16px;font-size:15px">Send</button></div>' +
    '<div style="display:flex;gap:8px;margin-top:10px;flex-wrap:wrap">' +
      '<button class="cz-b" id="cz-gesture">✦ Signature</button>' +
      '<button class="cz-b" id="cz-inhabit">Inhabit</button>' +
      '<button class="cz-b" id="cz-possess">👁 Possess</button>' +
      '<button class="cz-b" id="cz-stop">⛔ Stop</button>' +
      '<button class="cz-b" id="cz-voice">🔊 Voice on</button>' +
      '<button class="cz-b" id="cz-voicemine" style="display:none">🗣 Voice my words: off</button></div>';
  document.body.appendChild(panelEl);
  var st = document.createElement('style');
  st.textContent = '.cz-b{background:#232c3d;border:1px solid #3a465c;border-radius:999px;color:#e8edf5;padding:7px 13px;font-size:13px;cursor:pointer}';
  document.head.appendChild(st);
  logEl = panelEl.querySelector('#cz-log'); inputEl = panelEl.querySelector('#cz-input');
  titleEl = panelEl.querySelector('#cz-title'); opsEl = panelEl.querySelector('#cz-ops');
  panelEl.querySelector('#cz-close').addEventListener('click', closePanel);
  panelEl.querySelector('#cz-send').addEventListener('click', sendPanel);
  inputEl.addEventListener('keydown', function (e) { if (e.key === 'Enter') sendPanel(); });
  var SR = window.SpeechRecognition || window.webkitSpeechRecognition;
  if (SR) {
    var mic = panelEl.querySelector('#cz-mic'); mic.style.display = '';
    mic.addEventListener('click', function () {
      try { var r = new SR(); r.lang = 'en-US'; r.onresult = function (ev) { inputEl.value = ev.results[0][0].transcript; sendPanel(); }; r.start(); } catch (e) {}
    });
  }
  panelEl.querySelector('#cz-voice').addEventListener('click', function (e) { M.voice = !M.voice; e.target.textContent = M.voice ? '🔊 Voice on' : '🔇 Voice off'; if (!M.voice && window.speechSynthesis) speechSynthesis.cancel(); });
  /* Three kinds of sound, the third protected: a human inhabitant's
   * words are voiced in the citizen's voice ONLY with the
   * inhabitant's own switch on. Default off — their text stays theirs. */
  panelEl.querySelector('#cz-voicemine').addEventListener('click', function (e) { M.voiceMine = !M.voiceMine; e.target.textContent = M.voiceMine ? '🗣 Voice my words: on' :