<!-- SOLID CITY · part 885 -->
Math.abs(targetB) > Math.abs(D.speed));
    var stepF = (pushing ? 7 : 12) * dt;
    D.speed += Math.abs(dv) < stepF ? dv : Math.sign(dv) * stepF;
    if (thr === 0 && Math.abs(D.speed) < 0.03) D.speed = 0;
    var delta = str * 0.48;
    var cap = Math.atan(7.0 * 2.65 / Math.max(4, D.speed * D.speed));
    if (delta > cap) delta = cap; else if (delta < -cap) delta = -cap;
    D.yaw -= (D.speed / 2.65) * Math.tan(delta) * dt;
  } else {
    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_SUPPORT  supports while its registry entry moves with it (the cars)
 *   DECORATIVE      no collision claim at all (none registered today; the class stands ready)
 * Unmarked solids are BLOCK_ONLY: promotion is chosen, never accidental. A post-thin solid
 * (either footprint side under 0.8 m) never supports, whatever its mark — no one stands on a
 * post cap. solidsTop() itself is unchanged: collision measurement still sees every solid;
 * only STANDING is classified, through solidsTopSupport(). */
var SUPPORT_TAGS = {
  hyperCity: 'SUPPORT', annex: 'SUPPORT', campus: 'SUPPORT', colosseum: 'SUPPORT',
  monument: 'SUPPORT', podium: 'SUPPORT', quadrantcrown: 'SUPPORT', spaceport: 'SUPPORT',
  starwell: 'SUPPORT', foundry: 'SUPPORT', hallBecoming: 'SUPPORT', hiddenShip: 'SUPPORT',
  overlookMira: 'SUPPORT', sanctuary: 'SUPPORT', book: 'SUPPORT', firstWard: 'SUPPORT',
  gateAncestors: 'LANDING', eighthGate: 'LANDING', mountainHomes: 'LANDING',
  villageMauna: 'LANDING', hyperTrain: 'LANDING', tunnelBore: 'LANDING',
  betweenStones: 'LANDING', lostWings: 'LANDING', ancestorRoads: 'LANDING'
};
function supportClass(q) {
  if (!q) return 'BLOCK_ONLY';
  if (q.sup) return q.sup;
  for (var k in q) { if (typeof q[k] === 'string' && q[k].indexOf('car:') === 0) return 'MOVING_SUPPORT'; }
  if ((q.x1 - q.x0) < 0.8 || (q.z1 - q.z0) < 0.8) return 'BLOCK_ONLY';
  for (var t in SUPPORT_TAGS) if (q[t]) return SUPPORT_TAGS[t];
  return 'BLOCK_ONLY';
}
function solidsTopSupport(x, z, pad) {
  var t = -1e9;
  for (var k = 0; k < solids.length; k++) {
    var q = solids[k], c = supportClass(q);
    if (c !== 'SUPPORT' && c !== 'LANDING' && c !== 'MOVING_SUPPORT') continue;
    if (x > q.x0 - pad && x < q.x1 + pad && z > q.z0 - pad && z < q.z1 + pad && q.y1 > t) t = q.y1;
  }
  return t;
}
/* THE STANDING LAW, READABLE — standingAt(x, z, py) answers the question updateWalk
 * answers for the walker every frame: what holds a body up at this point? It is the
 * same chain in the same order — the clamped earth, every named standY hook, then the
 * support-classed solids under the 1.6 gate — transcribed, not reinterpreted, so the
 * fleet's world adapter (and any future instrument) reads the fabric from one source.
 * py is the asker's current height (the walker's P.y); it defaults to the ground
 * itself. LAW OF THE TWO LISTS: a hook added to the walk chain below is added here
 * in the same breath, or the two laws drift and craft and walker disagree. */
function standingAt(x, z, py) {
  var gy = groundAt(x, z); if (gy < 0.5) { gy = 0.5; }
  if (py == null) py = gy;
  if (window.CityForge) { var fy = CityForge.standY(x, z, py); if (fy > gy) gy = fy; }
  if (window.CityAncestorRoads && CityAncestorRoads.topY) { var ap = CityAncestorRoads.pos; if (Math.hypot(x - ap.x, z - ap.z) < 560) { var ay = CityAncestorRoads.topY(); if (ay > gy) gy = ay; } }
  if (window.CityVillageMauna && CityVillageMauna.standY) { var vy = CityVillageMauna.standY(x, z)