<!-- SOLID CITY · part 931 -->
ountShown = ''; $('sc-launch').hidden = true; $('sc-act').hidden = true; $('sc-count').hidden = false;
}
function setFlightUI(on) { $('sc-thr').hidden = !on; $('sc-fbtns').hidden = !on; }
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 ---------------- */
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;
  if (mag > 0.08) {
    var sp = (k.ShiftLeft || k.ShiftRight || mag > 0.95 && IS_TOUCH ? 15 : 8) * mag;
    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) { // 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; } }
  }
  // everything visible is solid
  collideXZ(P, 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 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 (gy < 1 && heightAt(P.x, P.z) < -1.5) { P.x -= (P.x - (P.lx || P.x)); P.z -= (P.z - (P.lz || P.z)); gy = groundAt(P.x, P.z); help('The sea is too deep to walk — take a ship from the spaceport.', 3); }
  P.lx = P.x; P.lz = P.z;
  P.y += (gy - P.y) * Math.min(1, dt * 12);
  if (P.y < Y0 + 0.5 && Math.hypot(P.x, P.z) < 800 && gy < Y0 + 0.05) P.y = Y0 + (Math.hypot(P.x, P.z) < 110 ? 0.12 : 0.02);
  avatar.rotation.y = P.face;
  var sw = Math.sin(P.walk || 0) * (mag > 0.08 ? 0.5 : 0);
  avatar.children.forEach(function (c) { if (c.name === 'leg1' || c.name === 'arm-1') c.rotation.x = sw; if (c.name === 'leg-1' || c.name === 'arm1') c.rotation.x = -sw; });
  // context action
  actTarget = null;
  var dS = Math.hypot(P.x - SABLE_POS.x, P.z - SABLE_POS.z);
  if (dS < 7) actTarget = { kind: 'sable', label: 'TALK TO SABLE' };
  if (!actTarget && Math.hypot(P.x - SOUL.door.x, P.z - SOUL.door.z) < 8) actTarget = { kind: 'soul', label: 'ENTER SOUL PRINT TOWER' };
  if (!actTarget && window.CitySanctuary && window.CityCitizens && CitySanctuary.talkHere(P.x, P.z)) actTarget = { kind: 'citizen', id: 'provolta', label: 'TALK TO PROVOLTA' };   // the sanctuary's talk point
  if (!actTarget && window.CityPodium && CityPodium.actHere(P.x, P.z)) actTarget = { kind: 'podium', label: 'READ' };   // ALL THAT WE CAN DO! podium
  if (!actTarget && window.CityBook && CityBook.actHere(P.x, P.z)) actTarget = { kind: 'book', label: 'READ THE BOOK' };   // the King's Book of Law (Forum Plaza)
  if (!actTarget && window.CityCampus) { var cpt = CityCampus.actHere(P.x, P.z); if (cpt) actTarget = { kind: 'campus', id: cpt, label: 'OPEN COURSE CATALOG' }; }   // a campus hall's forecourt
  if (!actTarget && window.CityCitizens) { var cz = CityCitizens.near(P.x, P.z); if (cz) actTarget = { kind: 'citizen', id: cz.id, label: 'TALK TO ' + cz.name.toUpperCase() }; }
  if (!actTarget) for (var i = 0; i < portals.length; i++) {
    var po = portals[i], dp = Math.hypot(P.x - po.x, P.z - po.z);
    if (dp < 9) actTarget = { kind: 'portal', d: po.d, label: po.d.go ? po.d.go + ' →' : 'ENTER ' + po.d.label.toUpperCase() + ' →' };
    if (dp < 2.2 && S.portalCool <= 0) { openPortal(po.d); S.portalCool = 4; }
  }
  if (!actTarget && Math.hypot(P.x - PAD.x, P.z - PAD.z) < 34) actTarget = { kind: 'board', label: 'BOARD SHIP' };
  S.portalCool -= dt;
}

/* ---------------- flight ---------------- */
function shipVectors() { var f = new V3(0, 0, 1).applyQuaternion(S.ship.q), u = new V3(0, 1, 0).applyQuaternion(S.ship.q), r = new V3(-1, 0, 0).applyQuaternion(S.ship.q); return { f: f, u: u, r: r }; }
function rotLocal(axis, ang) { tq.setFromAxisAngle(