<!-- SOLID CITY · part 121 -->
 || []).filter(function (x) { return BY[x.id] && !heard[x.at]; }).pop();
      if (l) { heard[l.at] = 1; onAir(BY[l.id], l.text, true); } else { var c = pick(ROSTER); onAir(c, pick(c.chat)); }
    }).catch(function () {});
    return;
  }
  var c = pick(ROSTER); onAir(c, pick(c.chat));
}

/* ---------------- WATCH: follow a citizen with the camera (on foot) ---------------- */
var fbar = document.createElement('div'); fbar.id = 'cz-follow'; fbar.hidden = true; $('sc-hud').appendChild(fbar);
var fcam = new T.Vector3(), fInit = false;
function watch(c) {
  if (S.mode !== 'walk') { hyperSay('WATCH-follow works on foot. Land first, then try again.'); return; }
  if (window.CityHyper && CityHyper.stopAI) CityHyper.stopAI(true);
  follow = c; fInit = false; lodT = 0;
  fbar.innerHTML = '\uD83C\uDFAC WATCHING <b>' + esc(c.name) + '</b> <button type="button" id="cz-unfollow">\u2715 STOP</button>'; fbar.hidden = false;
  $('cz-unfollow').addEventListener('click', unwatch);
}
function unwatch() { follow = null; fbar.hidden = true; }
function cam(dt, camAbs, camLook) {
  if (!follow) return false;
  var I = A.input, k = I.keys;
  if (S.mode !== 'walk' || Math.abs(I.sx) > 0.15 || Math.abs(I.sy) > 0.15 || k.KeyW || k.KeyA || k.KeyS || k.KeyD || k.ArrowUp || k.ArrowDown || k.ArrowLeft || k.ArrowRight) { unwatch(); return false; }
  var c = follow, fl = c.st === 'fly', back = fl ? 26 : 7.5, up = fl ? 9 : 3.2, h = fl ? c.hdg : c.face;
  var wx = c.x - Math.sin(h) * back, wz = c.z - Math.cos(h) * back, wy = c.y + up;
  if (!fInit) { fcam.copy(camAbs); fInit = true; }
  fcam.x += (wx - fcam.x) * Math.min(1, dt * 2.5); fcam.y += (wy - fcam.y) * Math.min(1, dt * 2.5); fcam.z += (wz - fcam.z) * Math.min(1, dt * 2.5);
  var g = A.groundAt(fcam.x, fcam.z) + 1; if (fcam.y < g) fcam.y = g;
  camAbs.copy(fcam); camLook.set(c.x, c.y + (fl ? 1 : 1.6), c.z);
  fbar.querySelector('b').textContent = c.name + ' \u00b7 ' + doing(c);
  return true;
}

/* ---------------- Hyper Controller: 👥 CITIZENS list ---------------- */
function hyperSay(t) { if (window.CityHyper && CityHyper.narrate) CityHyper.narrate(esc(t), 6); else A.help(t, 5); }
function dirWord(dx, dz) { var a = (Math.atan2(dx, -dz) * 180 / Math.PI + 360) % 360; return ['north', 'north-east', 'east', 'south-east', 'south', 'south-west', 'west', 'north-west'][Math.round(a / 45) % 8]; }
function fmtD(m) { return m < 1000 ? Math.round(m) + ' m' : (m / 1000).toFixed(1) + ' km'; }
function myPos() { return S.mode === 'walk' ? S.p : S.ship; }
function find(c) {
  var p = myPos(), d = Math.hypot(c.x - p.x, c.z - p.z);
  if (window.CityHyper) CityHyper.state.focus = { name: c.name, x: c.x, z: c.z, cz: c };
  hyperSay(c.name + ' is ' + fmtD(d) + ' ' + dirWord(c.x - p.x, c.z - p.z) + ', ' + doing(c) + '. Marked in red on the radar.');
}
function goTo(c, thenTalk) {
  if (!window.CityHyper || !CityHyper.driveTo) return;
  var tries = 0;
  function go() {
    var p = { id: 'cz-' + c.id, name: c.name, x: c.x + 2, z: c.z + 2, text: c.role };
    CityHyper.driveTo(p, 'AI DRIVE', function () {
      var me2 = myPos(); if (Math.hypot(c.x - me2.x, c.z - me2.z) > 6 && ++tries < 4 && S.mode === 'walk') return go();
      if (thenTalk) open(c.id);
    });
  }
  go();
}
function citizenCard(c) {
  if (!window.CityHyper || !CityHyper.showCard) return;
  var p = myPos(), d = Math.hypot(c.x - p.x, c.z - p.z), fl = CityHyper.flying();
  var btns = [{ label: '\uD83D\uDCCD FIND', fn: function () { CityHyper.hideCard(); find(c); } },
    { label: fl ? '\uD83E\uDDED FLY TO' : '\uD83E\uDDED GO TO', gold: true, fn: function () { CityHyper.hideCard(); find(c); goTo(c, false); } }];
  if (!fl && !c.fixed) btns.push({ label: '\uD83C\uDFAC WATCH', fn: function () { CityHyper.hideCard(); watch(c); } });
  if (!fl) btns.push({ label: '\uD83D\uDCAC TALK', fn: function () { CityHyper.hideCard(); if (d < 12) open(c.id); else goTo(c, true); } });
  CityHyper.showCard(c.name, c.role.toUpperCase(), '<p class="cz-card"><img src="' + esc(portraitURL(c)) + '" alt=""><span>' + esc(c.name) + ' is ' + esc(doing(c)) + ', near ' + esc(where(c)) + '.</span></p><p class="hc-meta">' + fmtD(d) + ' away \u00b7 ' + dirWord(c.x - p.x, c.z - p.z) + '</p>', btns);
}
function listCard() {
  if (!window.CityHyper || !CityHyper.showCard) return;
  var p = myPos();
  CityHyper.showCard('\uD83D\uDC65 CITIZENS', ROSTER.length + ' citizens \u00b7 nearest first \u00b7 tap one to find, go, watch or talk', '<div class="hc-chips cz-list"></div>', []);
  var box = document.querySelector('#hc-card .cz-list');
  ROSTER.slice().sort(function (a, b) { return Math.hypot(a.x - p.x, a.z - p.z) - Math.hypot(b.x - p.x, b.z - p.z); }).forEach(function (c) {
    var b = document.createElement('button'); b.type = 'button'; b.className = 'hc-chip cz-chip';
    b.innerHTML = '<i style="background:' + hex(c.accent) + '"></i>' + esc(c.name) + ' <small>' + fmtD(Math.hypot(c.x - p.x, c.z - p.z)) + (c.st === 'fly' ? ' \u2708' : c.st ===