<!-- SOLID CITY · part 1538 -->
UPPORT  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;
}
function updateWalk(dt) {
  var k = input.keys, mx = input.sx, my = input.sy;
  if (k.KeyW || k.ArrowUp) my -= 1; if (k.KeyS || k.ArrowDown) my += 1; if (k.KeyA || k.ArrowLeft) mx -= 1; if (k.KeyD || k.ArrowRight) mx += 1;
  var mag = Math.min(1, Math.hypot(mx, my)), P = S.p;
  var DRV = S.drive || null;
  if (DRV) { DRV._thr = clamp(-my, -1, 1); DRV._str = clamp(mx, -1, 1); mx = 0; my = 0; mag = 0; }
  if (mag > 0.08) {
    var sp = (k.ShiftLeft || k.ShiftRight || mag > 0.95 && IS_TOUCH ? 22 : 12) * mag;
    if (S.rfly) sp *= 2.2; // royal flight: the superhero covers ground
    var fx = -Math.sin(S.camYaw), fz = -Math.cos(S.camYaw), rx = Math.cos(S.camYaw), rz = -Math.sin(S.camYaw);
    var vx = (fx * -my + rx * mx) / (Math.hypot(mx, my) || 1), vz = (fz * -my + rz * mx) / (Math.hypot(mx, my) || 1);
    P.x += vx * sp * dt; P.z += vz * sp * dt;
    var want = Math.atan2(vx, vz), dA = ((want - P.face + Math.PI * 3) % (Math.PI * 2)) - Math.PI; P.face += dA * Math.min(1, dt * 10);
    P.walk = (P.walk || 0) + dt * sp * 1.4;
  }
  if (S.ai && !DRV) { // Hyper Controller AI walk: user input always wins
    if (mag > 0.08) S.ai(null);
    else { var st = S.ai(dt); if (st) { P.x += st.vx * dt; P.z += st.vz * dt; var wa = Math.atan2(st.vx, st.vz), da = ((wa - P.face + Math.PI * 3) % (Math.PI * 2)) - Math.PI; P.face += da * Math.min(1, dt * 8); P.walk = (P.walk || 0) + dt * st.sp * 1.1; mag = 1; } }
  }
  if (DRV) driveStep(dt);
  // everything visible is solid
  collideXZ(P, DRV ? 1.15 : 0.45, P.y + 1);
  var sx = SABLE_POS.x - P.x, sz = SABLE_POS.z - P.z, sd = Math.hypot(sx, sz); if (sd < 1.3 && sd > 0) { P.x = SABLE_POS.x - sx / sd * 1.3; P.z = SABLE_POS.z - sz / sd * 1.3; }
  var RF = !!S.rfly;
  if (RF) {
    var rise = S.rflyRise || 0;
    if (k.KeyR || k.Space) rise = 1; if (k.KeyF) rise = -1;
    S.rflyT = (S.rflyT || 0) + dt;
    if (S.rflyLand) rise = -1; else if (rise === 0 && S.rflyT < 1.1) rise = 1; // the lift-off
    S.rflyV = (S.rflyV || 0) + (rise * 14 - (S.rflyV || 0)) * Math.min(1, dt * 6);
    P.y += S.rflyV * dt;
    if (P.y > 420) P.y = 420;
  }
  var gy = groundAt(P.x, P.z); if (gy < 0.5) { gy = 0.5; } // wading at the shore, no deeper
  if (window.CityForge) { var fy = CityForge.standY(P.x, P.z, P.y); if (fy > gy) gy = fy; }   // stand on Forge pieces (steps, slabs)
  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
  if (window.CityVillageMauna && CityVillageMauna.standY) { var vy = CityVillageMauna.standY(P.x, P.z); if (vy > gy) gy = vy; }
  if (window.CityMountainHomes && CityMountainHomes.standY) { var mhy = CityMountainHomes.standY(P.x, P.z, P.y); if (mhy > gy) gy = mhy; }   // stand in the Ancestors' Homes (pads, lanai, floors, stairs)   // stand on Kauhale Mauna terraces
  if (window.CityTunnelBore && CityTunnelBore.standY) { var ty = CityTunnelBore.standY(P.x, P.z); if (ty > gy) gy = ty; }   // stand on the Bore floor
  if (window.CityStarwell && CityStarwell.standY) { var wy = CityStarwell.standY(P.