<!-- SOLID CITY · part 2890 -->
ch(function (i) {
    var c = i.c;
    h += '<div class="rf-row" data-id="' + esc(c.id) + '"><span class="rf-dot" style="background:' + colOf(c) + '"></span><span class="rf-nm">' + esc(c.name) +
      '<small>' + fmtD(i.d) + (c.st === 'fly' ? ' \u00b7 flying' : '') + '</small></span>' +
      '<button type="button" data-a="fly" title="Go there (walk or fly)">\u2708 Go</button><button type="button" data-a="watch" title="Follow (Watch)">\uD83C\uDFAC Follow</button><button type="button" data-a="talk" title="Talk">\uD83D\uDCAC Talk</button></div>';
  });
  el.innerHTML = h; document.body.appendChild(el); picker = el; setBig(big);
  $('rf-x').addEventListener('click', function () { closePicker(); setBig(false); });
  $('rf-big').addEventListener('click', function () { setBig(!big); });
  el.addEventListener('click', function (e) {
    var b = e.target.closest('button[data-a]'); if (!b) return;
    var c = CZ.get(b.closest('.rf-row').getAttribute('data-id')); if (!c) return;
    var a = b.getAttribute('data-a'), d = Math.hypot(c.x - me().x, c.z - me().z);
    closePicker(); setBig(false);
    if (a === 'fly') { if (CZ.find) CZ.find(c); CZ.goTo(c, false); }
    else if (a === 'watch') { if (S0().mode === 'walk' && c.st !== 'fly') followWalk(c); else CZ.watch(c); }
    else if (c.ext || d < 8) CZ.open(c.id); else CZ.goTo(c, true);
  });
}
/* FOLLOW on foot (2026-10-10): auto-walk ~4 m behind the citizen, re-pathing with the Hyper A*, HUD 'FOLLOWING <name>' + STOP.
   Uses the same S.ai hook as Hyper AI Drive, so any stick / key input stops it at once (city.js calls S.ai(null)). */
function S0() { return SC.api.S; }
var FW = null, fwBar = null;
function fwStop(msg) {
  var S = S0(); if (FW && S.ai === FW.step) S.ai = null;
  FW = null; if (fwBar) fwBar.hidden = true;
  if (msg && window.CityHyper && CityHyper.narrate) CityHyper.narrate(msg, 3);
}
function followWalk(c) {
  var S = S0(), HY = window.CityHyper;
  if (!HY || !HY.findPath) { CZ.watch(c); return; }
  try { HY.stopAI(true); } catch (e) {}
  if (CZ.following && CZ.following()) { var ub = document.getElementById('cz-unfollow'); if (ub) ub.click(); }
  if (!fwBar) {
    fwBar = document.createElement('div'); fwBar.id = 'rf-following'; fwBar.setAttribute('role', 'status');
    fwBar.innerHTML = '<span>\uD83D\uDC63 FOLLOWING <b></b></span><button type="button" id="rf-fstop" aria-label="Stop following">\u2715 STOP</button>';
    (document.getElementById('sc-hud') || document.body).appendChild(fwBar);
    fwBar.addEventListener('pointerdown', function (e) { e.stopPropagation(); });
    document.getElementById('rf-fstop').addEventListener('click', function (e) { e.stopPropagation(); fwStop('Stopped following.'); });
  }
  fwBar.querySelector('b').textContent = c.name; fwBar.hidden = false;
  var F = { c: c, path: null, i: 1, t: 9, sp: 2 };
  F.step = function (dt) {
    if (FW !== F) return null;
    if (dt === null) { fwStop('Manual control. Stopped following ' + c.name + '.'); return null; }   // human input
    var P = S.p, cx = c.x, cz = c.z, d = Math.hypot(cx - P.x, cz - P.z);
    if (S.mode !== 'walk' || c.st === 'fly' || d > 700) { fwStop(c.name + (c.st === 'fly' ? ' took off. ' : ' is out of reach. ') + 'Stopped following.'); return null; }
    if (d < 4.6) { F.path = null; F.t = 9; F.sp = 2; return null; }   // close enough: wait ~4 m behind
    F.t += dt;
    if (!F.path || F.t > 1.0 || F.i >= F.path.length) {   // re-path toward a point 4 m short of the citizen
      var tx = cx + (P.x - cx) / d * 4, tz = cz + (P.z - cz) / d * 4;
      F.path = HY.findPath(P, { x: tx, z: tz }) || [{ x: P.x, z: P.z }, { x: tx, z: tz }]; F.i = 1; F.t = 0;
    }
    var w = F.path[Math.min(F.i, F.path.length - 1)], dx = w.x - P.x, dz = w.z - P.z, wd = Math.hypot(dx, dz);
    while (wd < 1.2 && F.i < F.path.length - 1) { F.i++; w = F.path[F.i]; dx = w.x - P.x; dz = w.z - P.z; wd = Math.hypot(dx, dz); }
    if (wd < 0.3) return null;
    F.sp = Math.min(Math.max(2.5, Math.min(14, (d - 4) * 1.2)), F.sp + dt * 9);
    var want = Math.atan2(-dx, -dz), da = ((want - S.camYaw + Math.PI * 3) % (Math.PI * 2)) - Math.PI; S.camYaw += da * Math.min(1, dt * 2.2);
    return { vx: dx / wd * F.sp, vz: dz / wd * F.sp, sp: F.sp };
  };
  FW = F; S.ai = F.step;
  if (HY.narrate) HY.narrate('Following <b>' + esc(c.name) + '</b> on foot, about 4 m behind. Move or tap STOP to take over.', 4);
}
setInterval(function () { if (FW && S0().ai !== FW.step) fwStop(''); }, 500);   // something else took the wheel (AI Drive, Watch, boarding)
radarEl.addEventListener('pointerdown', function (e) { e.stopPropagation(); });
radarEl.addEventListener('click', function (e) {
  e.preventDefault(); e.stopPropagation();
  if (big) {
    var r = radarEl.getBoundingClientRect(), s = radarEl.width / r.width, x = (e.clientX - r.left) * s, y = (e.clientY - r.top) * s, best = null, bd = 30 * s;
    dots.forEach(function (i) { var d = Math.hypot(i.bx - x, i.by - y); if (d < bd) { bd = 