<!-- SOLID CITY · part 1537 -->
x / innerWidth * 2 - 1, -(y / innerHeight) * 2 + 1); ray.setFromCamera(ndc, camera);
  if (window.CityForge && CityForge.tap(x, y)) return;
  if (ray.intersectObject(sable, true).length) openTalk();
  else if (window.CityCitizens && CityCitizens.tap(ray)) { /* 👥 citizen talk panel opened */ }
  else if (SOUL.group && Math.hypot(S.p.x - D.soul.x, S.p.z - D.soul.z) < 140 && ray.intersectObject(SOUL.group, true).length) openSoul();
}
function board() { S.mode = 'boarded'; avatar.visible = false; S.ship.x = PAD.x; S.ship.y = PAD.y + 2.4; S.ship.z = PAD.z; S.ship.speed = 0; levelShip(Math.PI); help('Ship boarded. Hit the big red LAUNCH button. Warp Speed mini-game: walk through the spaceport gate.'); }
function disembark() { S.mode = 'walk'; avatar.visible = true; S.p.x = PAD.x; S.p.z = PAD.z - 52; S.p.y = groundAt(S.p.x, S.p.z); S.p.face = Math.PI; setFlightUI(false); help('Clear of the pad — the hangar is behind you, the city ahead.'); }
function levelShip(heading) { S.ship.q.setFromAxisAngle(UP, heading); }
var countT = 0, countEnd = 0, countShown = '';
function startCountdown() {
  if (S.mode !== 'boarded') return; S.mode = 'countdown'; countT = 3.0; countEnd = performance.now() + 3000; countShown = ''; $('sc-launch').hidden = true; $('sc-act').hidden = true; $('sc-count').hidden = false;
}
function setFlightUI(on) { $('sc-thr').hidden = !on; $('sc-fbtns').hidden = !on; }
/* ROYAL FLIGHT (2026-10-12) — the King's sole power. Hyper calls this ONLY
   after the server-side Royal Rights unlock; nothing here self-grants. */
window.CityRoyal = {
  setFly: function (on) {
    if (S.mode !== 'walk') return false;
    if (on && !S.rfly) { S.rfly = true; S.rflyLand = false; S.rflyT = 0; S.rflyV = 0; help('You rise. Climb and sink with the gold arrows (or R / F). Tap WALK to land.', 6); }
    else if (!on && S.rfly) { S.rflyLand = true; }
    return true;
  },
  flying: function () { return !!S.rfly; },
  setRise: function (v) { S.rflyRise = v; },
  onLand: null
};
/* FIRST DRIVES (2026-10-13) — the South Lot rides. The avatar is the real
   human of this world; the rides were built for him to roll in. CityDrive
   owns the drive state and updateWalk integrates it (driveStep), exactly
   the way royal flight is integrated: one sanctioned hook in the physics.
   Any visitor may roll a lot ride, as any visitor may ride the train. */
window.CityDrive = {
  active: function () { return !!S.drive; },
  state: function () { return S.drive || null; },
  begin: function (mesh, yaw, ox, oz, carId) {
    if (S.mode !== 'walk' || S.rfly || S.drive) return false;
    S.drive = { mesh: mesh, yaw: yaw || 0, speed: 0, _thr: 0, _str: 0, ox: ox || 0, oz: oz || 0, carId: carId || null };
    if (avatar) avatar.visible = false;
    help('You roll. Push the stick up to drive, sideways to steer. The action button steps you out.', 7);
    return true;
  },
  end: function () {
    var D = S.drive; if (!D) return false;
    var P = S.p;
    if (D.mesh) { D.mesh.position.set(P.x - D.ox, P.y, P.z - D.oz); D.mesh.rotation.y = D.yaw; }
    var rx = Math.cos(D.yaw), rz = -Math.sin(D.yaw);
    P.x -= rx * 2.6; P.z -= rz * 2.6;
    P.face = D.yaw;
    S.drive = null;
    if (avatar) avatar.visible = true;
    help('You step out. The ride stays where you left it.', 5);
    return true;
  }
};
function driveStep(dt) {
  var D = S.drive, P = S.p; if (!D) return;
  var thr = D._thr || 0, str = D._str || 0;
  var target = thr * (thr >= 0 ? 26 : 9);
  var rate = Math.abs(target) > Math.abs(D.speed) ? 1.9 : 3.4;
  D.speed += (target - D.speed) * Math.min(1, dt * rate);
  if (thr === 0 && Math.abs(D.speed) < 0.03) D.speed = 0;
  var sf = Math.min(1, Math.abs(D.speed) / 7);
  D.yaw -= str * sf * dt * 1.75 * (D.speed < 0 ? -1 : 1);
  P.x += Math.sin(D.yaw) * D.speed * dt;
  P.z += Math.cos(D.yaw) * D.speed * dt;
  P.face = D.yaw;
  if (D.mesh) { D.mesh.position.set(P.x - D.ox, P.y, P.z - D.oz); D.mesh.rotation.y = D.yaw; }
}

function returnToCity() { if (S.mode === 'fly' || S.mode === 'auto') startAuto(false); }
function startAuto(landNow) { S.mode = 'auto'; S.auto = { phase: 'cruise', land: true }; help('Autopilot: returning to the Orbital Spaceport. Move the stick to take back control.'); }
var helpTimer = 0;
function help(t, sec) { var h = $('sc-help'); h.textContent = t; h.style.opacity = 1; helpTimer = sec || 6; }

/* ---------------- walking ---------------- */

/* THE SUPPORT CLASSES (VECTOR's taxonomy, entered on the King's fix commission, 2026-10-13).
 * The morning's law made every solid's top standing ground. The review's correction: not every
 * solid is a floor. A solid now carries a MEANING, read from its maker's mark:
 *   BLOCK_ONLY      stops movement, never stands (walls, shells, columns, furniture, figures)
 *   SUPPORT         a floor by design (building roofs, monument pedestals, plinths, podia)
 *   LANDING         arrival from the air is welcome (gate lintels, dwelling roofs, the train)
 *   MOVING_S