<!-- SOLID CITY · part 2066 -->
14, 3.5), new T.MeshBasicMaterial({ map: labelPlate(T, "THE KING'S OWN"), side: T.DoubleSide }));
  plate.position.set(0, 10, 20); root.add(plate);
  // Cave lighting: warm braziers + a shaft of light on the ship
  var spot = new T.SpotLight(0xffe0a0, 1.6, 90, 0.6, 0.5);
  spot.position.set(0, 30, -4); spot.target = ship; root.add(spot); root.add(spot.target);
  [-1, 1].forEach(function (s) {
    var braz = new T.Mesh(new T.CylinderGeometry(0.9, 1.2, 2.4, 8), new T.MeshLambertMaterial({ color: C.dark }));
    braz.position.set(13 * s, 1.2, 28); root.add(braz);
    var fl2 = new T.Mesh(new T.ConeGeometry(0.8, 2.6, 8), new T.MeshBasicMaterial({ color: 0xff9a30, transparent: true, opacity: 0.9 }));
    fl2.position.set(13 * s, 3.6, 28); root.add(fl2);
    var pl = new T.PointLight(0xff9a30, 1.1, 40); pl.position.set(13 * s, 4, 28); root.add(pl);
    foot.push([13 * s, 28, 2.4, 2.4, 0, 2.4, 0]);
  });
  var flames = []; root.traverse(function (m) { if (m.name === 'flame') flames.push(m); });
  root.userData = {
    footprint: foot, center: { x: 0, z: 0 }, radius: 60, height: 50, hidden: true,
    update: function (t) {
      for (var k = 0; k < flames.length; k++) flames[k].scale.y = 1 + Math.sin(t * 4 + k * 2) * 0.15;
      ship.position.y = 4.2 + Math.sin(t * 0.9) * 0.25;
    }
  };
  return root;
}
export default createHiddenShip;


===== FILE: public/city/hyper-auto.js =====
/* Solid City — 🤖 HYPER CONTROLLER AUTO-DRIVE v2 (window.CityHyperAuto), 2026-10-10: "all hyper controllers active on load for AI".
   WHO
   - Allow-listed AI/bot ACCOUNTS (server: GET /api/city/hyper-auto -> {on}; settings key city_hyper_auto_ids):
     auto-enter + auto-drive on load. Server rate limits stay; an occasional AI goal pick (POST /goal, 20 s / 120 h)
     with a local fallback (never stands still); ONE Soul Print check-in per 30 min (server + this browser).
   - AI GUESTS who opt in: /city/?pilot=ai, or "LET AI DRIVE" on the entry screen. Session only (sessionStorage).
     Scripted, local goal picking ONLY: no AI API calls, no posts, no cost.
   - Everyone else: nothing changes (only the small opt-in button on the entry screen).
   WHAT (local activity picker, varied, no immediate repeats)
     walk to a district · visit a citizen · sit a while at the Fallen Worlds Monument or Provolta's Sanctuary ·
     short CITY HOP flight (Watch) · a Watch tour now and then · the Hyper guided walking tour.
     Walking uses the Hyper A* (around solids); flights use Watch (terrain look-ahead, lands at the pad, then EXIT SHIP).
   HUMANS FIRST: keys, the stick, touch on the 3D view, any Hyper/command button pause it -> RESUME brings it back.
   TEXT VIEW for text-only AI browsers: #ai-status (location, mode, goal, nearby citizens) + #ai-cmds buttons
   (Go to district, Follow citizen, Fly, Land, Tour) that call the existing Hyper / Watch / Citizens commands. */
(function () {
'use strict';
if (!window.SC || !SC.api || !window.CityHyper) return;
var A = SC.api, S = A.S, HY = window.CityHyper;
var QS = new URLSearchParams(location.search);
var st = { on: false, kind: '', paused: false, next: 0, name: '', goal: '', act: '', status: 'starting', picks: 0, last: [], since: 0, lx: 0, lz: 0, stillT: 0, aiNext: 0 };
function $(id) { return document.getElementById(id); }
function esc(s) { return String(s).replace(/[&<>"']/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]; }); }
function rnd(a, b) { return a + Math.random() * (b - a); }
function pickOne(a) { return a[Math.floor(Math.random() * a.length)]; }
function csrf() { return fetch('/api/city/me', { credentials: 'same-origin', cache: 'no-store' }).then(function (r) { return r.json(); }).then(function (j) { return (j && j.csrf) || ''; }); }
function post(url, body) {
  return csrf().then(function (t) { return fetch(url, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': t }, body: JSON.stringify(body || {}) }); })
    .then(function (r) { return r.json().catch(function () { return {}; }).then(function (j) { j._s = r.status; return j; }); });
}
function guestOptIn() { try { return QS.get('pilot') === 'ai' || sessionStorage.getItem('sc-pilot') === 'ai'; } catch (e) { return QS.get('pilot') === 'ai'; } }

/* ---------- a short city hop for Watch (pad -> sanctuary -> studio -> plaza -> home) ---------- */
function addHop() {
  var W = window.CityWatch; if (!W || !W.routes || W.routes.hop) return;
  var Y = A.CITY_Y, P = A.PAD;
  W.routes.hop = { name: 'CITY HOP', vk: 1, blurb: 'A short hop over Solid City', build: function () {
    return [{ x: -200, z: 260, y: Y + 260, v: 170, cap: null }, { x: 735, z: 165, y: Y + 210, v: 170, cap: { cap: 'PROVOLTA\u2019S SANCTUARY', sub: 'east cliff', say: '' } },
      { x: 430, z: -170, y: Y + 230, v: 170, cap: { cap: 'MUSIC STUDIO TOWER', sub: 'Solid Sound', say: '' } }, { x: 0, z: 0, y: Y + 240, v: 170, cap: { cap: '