<!-- SOLID CITY · part 2198 -->
---- per-frame (called by city.js) ---------------- */
function update(dt) {
  var fl = flying(), vis = S.mode === 'walk' || fl;
  if (hc.hidden === vis) hc.hidden = !vis;
  root.classList.toggle('hc-fly', fl);
  if (fl !== H.wasFly) { // walk <-> flight: switch the button set
    H.wasFly = fl; H.nav = null; H.tour = null; H.armed = false; S.ai = null; clearPath(); hideCard(); clearScan(); focusG.visible = false; H.focus = null;
    if (H.mode === 'LOOK') { S.camDist = 7.5; S.camPitch = 0.28; }
    if (!H.flyDrive) setMode(fl ? 'FLIGHT' : 'WALK');
    inp.placeholder = fl ? 'Try: "fly me to Mauna Kea"' : PH;
  }
  if (H.flyDrive && S.mode !== 'auto') { // autopilot ended without our arrival callback: user took over, or landed
    var landed = S.mode === 'boarded'; H.flyDrive = null; setMode(baseMode());
    narrate(landed ? 'Landed at the <b>Orbital Spaceport</b>. LAUNCH again, or EXIT SHIP to walk.' : 'Manual control. AI Drive stopped.', 5);
  }
  if (!vis && H.mode !== 'WALK' && S.mode === 'boarded') setMode('WALK');
  if (H.tour && !H.nav) { var I = A.input, k = I.keys; if (Math.abs(I.sx) + Math.abs(I.sy) > 0.08 || k.KeyW || k.KeyA || k.KeyS || k.KeyD || k.ArrowUp || k.ArrowDown || k.ArrowLeft || k.ArrowRight) { stopAI(true); setMode('WALK'); narrate('Manual control. Tour stopped.', 3); } }
  if (H.tour && !H.nav && H.tour.wait > 0) { H.tour.wait -= dt; if (H.tour.wait <= 0) tourNext(); }
  if (H.scanT > 0) { H.scanT -= dt; if (H.scanT <= 0 && H.mode === 'SCAN') setMode(H.prevMode === 'SCAN' ? baseMode() : H.prevMode); }
  if (H.scan) {
    H.scan.age += dt;
    if (scanRing.visible) { var r = 2 + H.scan.age / 1.6 * 600; scanRing.scale.setScalar(r); matRing.opacity = Math.max(0, 0.8 * (1 - H.scan.age / 1.6)); if (H.scan.age > 1.6) scanRing.visible = false; }
    if (H.scan.fly) { // keep labels on the beam tops despite the planet curvature
      var cx = A.camera.position.x + A.origin.x, cz = A.camera.position.z + A.origin.z;
      scanG.children.forEach(function (o) { if (o.isSprite) { var dd = Math.hypot(o.position.x - cx, o.position.z - cz); o.position.y = o.userData.top - dd * dd / (2 * R_PLANET); } });
    }
    matRed.opacity = 0.35 + 0.25 * Math.sin(S.t * 4);
    if (H.scan.age > H.scan.t) { clearScan(); scanRing.visible = false; }
  }
  if (H.beaconT > 0) { H.beaconT -= dt; fRing.material.opacity = 0.4 + 0.3 * Math.sin(S.t * 6); if (H.beaconT <= 0) focusG.visible = false; }
  if (pathG) matPath.opacity = 0.55 + 0.25 * Math.sin(S.t * 5);
  if (H.mode === 'LOOK' && S.mode === 'walk' && performance.now() - H.lastDrag > 2000) S.camYaw += dt * 0.22;
}
function radar(g, blip, fl, sc, ax, az) {
  if (H.scan && H.scan.fly === fl) {
    if (H.scan.age < 1.8) { var W = g.canvas.width; g.strokeStyle = 'rgba(255,210,122,' + (0.9 - H.scan.age / 2) + ')'; g.lineWidth = 3; g.beginPath(); g.arc(W / 2, W / 2, Math.min(W / 2, H.scan.age / 1.6 * W / 2), 0, Math.PI * 2); g.stroke(); }
    H.scan.marks.forEach(function (m) { blip(m.x, m.z, '#ffd27a', 4, fl ? m.t.split(' ')[0].slice(0, 9) : ''); });
  }
  if (H.focus) blip(H.focus.x, H.focus.z, '#ff4d5e', 5, fl ? '\u25ce' : '');
  var t = H.nav ? H.nav.p : H.flyDrive ? H.flyDrive.t : null; if (t) blip(t.x, t.z, '#ffffff', 4, '\u2605');
}

window.CityHyper = {
  regrid: function () { grid = null; }, update: update, radar: radar, state: H, cmd: handleCommand, stop: function () { stopAI(true); hideCard(); clearNarration(); setMode(baseMode()); }, pois: POIS, landmarks: FLY,
  getMode: function () { return H.mode; }, findPath: function (a, b) { return findPath(a, b); },
  // used by citizens.js (👥 CITIZENS list): same glass card, AI Drive and narration as the rest of the controller
  showCard: showCard, hideCard: hideCard, driveTo: driveTo, narrate: narrate, stopAI: stopAI, flying: flying
};
})();


===== FILE: public/city/imperial-deploy.js =====
const fs = require('fs');
// 1. index.html: script tag
{
  const F = '/opt/epistles/public/city/index.html';
  const lines = fs.readFileSync(F, 'utf8').split('\n');
  const idx = lines.findIndex(l => l.includes('colosseum-wingmates-place.js'));
  if (idx === -1) { console.error('ANCHOR NOT FOUND'); process.exit(1); }
  if (!lines.some(l => l.includes('colosseum-imperial-place.js'))) {
    lines.splice(idx + 1, 0, '<script src="/city/colosseum-imperial-place.js?v=20261011a"></script>');
    fs.writeFileSync(F, lines.join('\n'));
    console.log('IMPERIAL_SCRIPT_TAG_OK');
  } else console.log('IMPERIAL TAG ALREADY PRESENT');
}
// 2. city.js: stand on the imperial terrace
{
  const F = '/opt/epistles/public/city/city.js';
  let s = fs.readFileSync(F, 'utf8');
  const anchor = "  if (window.CityAncestorRoads && CityAncestorRoads.topY) { var ap = CityAncestorRoads.pos; if (Math.hypot(P.x - ap.x, P.z - ap.z) < 560) { var ay = CityAncestorRoads.topY(); if (ay > gy) gy = ay; } }   // stand on the Ancestral Roads terrace\n";
  const add = "  if (window.CityImperialColosseum && CityImperialColosseum.pos)