<!-- SOLID CITY · part 120 -->
o are you?', 'What do you do?', 'Where are you headed?', 'Tell me about the city'].forEach(function (q) { var b = document.createElement('button'); b.type = 'button'; b.textContent = q; b.addEventListener('click', function () { send(q); }); chips.appendChild(b); });
  panel.hidden = false;
  getMe().then(function (j) {
    var ok = j && j.loggedIn; $('cz-login').hidden = !!ok;
    if (!$('cz-log').children.length) logLine('her', pick(c.hi));
    if (!A.IS_TOUCH) $('cz-msg').focus();
  });
  return true;
}
function closeTalk() { panel.hidden = true; if (cur) cur.talking = false; }
$('cz-close').addEventListener('click', closeTalk);
$('cz-form').addEventListener('submit', function (e) { e.preventDefault(); send($('cz-msg').value); });
addEventListener('keydown', function (e) { if (e.key === 'Escape' && !panel.hidden) closeTalk(); });
function scripted(c, text) {
  var t = text.toLowerCase();
  if (/\b(hi|hello|hey|aloha|yo|hola|sup)\b/.test(t)) return pick(c.hi);
  if (/who|your name|what are you/.test(t)) return 'I am ' + c.name + ', ' + c.role.toLowerCase() + ' of Solid City. ' + pick(c.job);
  if (/where|headed|going|doing|now/.test(t)) return 'Right now I am ' + doing(c) + ', near ' + where(c) + '.';
  if (/job|do you do|work|role/.test(t)) return pick(c.job);
  if (/music|song|radio|studio/.test(t)) return 'Studio Radio plays all over the city. The Music Studio Tower is east of the Plaza.';
  if (/forge|build/.test(t)) return 'Tap \uD83D\uDD28 BUILD on the Hyper Controller to raise something in the Forge. Everything you build is solid.';
  if (/city|tour|see|visit/.test(t)) return 'Forum Plaza in the middle, the Music Studio Tower east, Soul Print Tower south-east, and the Orbital Spaceport south-west for flights.';
  if (/bye|later|goodnight|see you/.test(t)) return 'See you around the city!';
  if (/thank/.test(t)) return 'Anytime.';
  return pick(c.job.concat(c.chat));
}
function send(text) {
  var c = cur; text = String(text || '').trim(); if (!c || !text || busy) return;
  $('cz-msg').value = ''; logLine('me', text); busy = true; $('cz-send').disabled = true;
  var done = function (reply, tag) { c.hist.push({ role: 'user', text: text }, { role: 'cz', text: reply }); if (c.hist.length > 12) c.hist.splice(0, 2); if (cur === c) logLine('her', reply, tag); bubble(c, reply); busy = false; $('cz-send').disabled = false; };
  getMe().then(function (j) {
    if (!j || !j.loggedIn) return setTimeout(function () { done(scripted(c, text), '(scripted)'); }, 350);
    var wait = document.createElement('div'); wait.className = 'sys'; wait.textContent = c.name + ' is thinking\u2026'; $('cz-log').appendChild(wait);
    fetch('/api/city/citizen', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': j.csrf || '' },
      body: JSON.stringify({ id: c.id, message: text, where: where(c), doing: doing(c), history: c.hist.slice(-6).map(function (h) { return { role: h.role === 'user' ? 'user' : 'cz', text: h.text }; }) }) })
      .then(function (r) { return r.json().catch(function () { return {}; }).then(function (b) { return { s: r.status, b: b }; }); })
      .then(function (o) {
        wait.remove();
        if (o.s === 401 || o.s === 403) { me = null; return done(scripted(c, text), '(scripted)'); }
        if (o.b && o.b.reply) return done(o.b.reply, o.b.cached ? '(AI \u00b7 cached)' : '(AI)');
        done(scripted(c, text), '(scripted \u00b7 AI ' + (o.s === 429 ? 'rate-limited' : 'busy') + ')');
      })
      .catch(function () { wait.remove(); done(scripted(c, text), '(scripted \u00b7 offline)'); });
  });
}

/* ---------------- radio chatter (mostly scripted, sometimes AI) ---------------- */
var radio = document.createElement('div'); radio.id = 'cz-radio'; radio.setAttribute('aria-live', 'polite'); $('sc-hud').appendChild(radio);
var radioT = rnd(20, 35), radioN = 0, heard = {};
function onAir(c, text, ai) {
  radio.textContent = ''; var b = document.createElement('b'); b.textContent = '\uD83D\uDCFB ' + c.name + (ai ? ' \u00b7 AI' : ''); b.style.color = hex(c.accent);
  radio.appendChild(b); radio.appendChild(document.createTextNode(' ' + text)); radio.classList.add('on');
  clearTimeout(radio._h); radio._h = setTimeout(function () { radio.classList.remove('on'); }, 7000);
}
function radioEvent(c, landed) {
  if (radioT > 12 || document.hidden) return;   // keep the air quiet: at most one line per window
  radioT = rnd(40, 90);
  onAir(c, landed ? 'Touching down at ' + STOPS[c.sched[c.i][0]][2] + '.' : c.st === 'fly' ? 'Lifting off for ' + STOPS[c.dest][2] + '.' : 'Heading over to ' + STOPS[c.dest][2] + '.');
}
function radioTick(dt) {
  radioT -= dt; if (radioT > 0 || document.hidden) return;
  radioT = rnd(45, 100); radioN++;
  if (radioN % 4 === 0) {
    fetch('/api/city/citizens/chatter', { credentials: 'same-origin', cache: 'no-store' }).then(function (r) { return r.json(); }).then(function (j) {
      var l = (j.lines