<!-- SOLID CITY · part 435 -->
.max(ty, sh.y - vy * dt);
    tq.setFromAxisAngle(UP, Math.PI); sh.q.slerp(tq, Math.min(1, dt * 2));
    if (sh.y <= ty + 0.05 && dh < 1) { S.mode = 'boarded'; S.auto = null; sh.speed = 0; setFlightUI(false); help('Landed at the Orbital Spaceport. LAUNCH again or EXIT SHIP to walk.'); }
  }
}
function updateLaunch(dt) {
  var sh = S.ship; S.launchT += dt; var t = S.launchT;
  if (t < 5) {
    var up = smooth(t / 1.4);
    tq.setFromAxisAngle(UP, Math.PI); sh.q.copy(tq); rotLocal(AX, -Math.PI / 2 * up);
    sh.speed = Math.min(170, 8 + t * t * 14);
    sh.y += sh.speed * dt * up + 1.5 * dt;
  } else {
    var lv = smooth((t - 5) / 2.2);
    tq.setFromAxisAngle(UP, Math.PI); var tgt = tq.clone(); tm.makeRotationAxis(AX, -Math.PI / 2 * (1 - lv) - 0.12 * lv); tgt.multiply(new T.Quaternion().setFromRotationMatrix(tm));
    sh.q.copy(tgt); sh.speed = 170 + lv * 80; moveShip(dt);
    if (t > 7.2) { S.mode = 'fly'; sh.throttle = 0.6; setFlightUI(true); help(IS_TOUCH ? 'Stick: pitch & roll (bank to turn). Slider: throttle. Hold BOOST. Climb past 25 km for orbit.' : 'W/S pitch · A/D roll · Q/E yaw · R/F throttle · Shift boost · L auto-level · H return to city. Climb past 25 km for orbit.', 9); }
  }
}

/* ---------------- camera + floating origin ---------------- */
var camAbs = new V3(), camLook = new V3(), camUp = new V3(0, 1, 0), shipCam = null, watchFov = 0;
function rebase(ax, az) {
  if (Math.abs(ax - origin.x) > 3000 || Math.abs(az - origin.z) > 3000) {
    origin.x = Math.round(ax / 1000) * 1000; origin.z = Math.round(az / 1000) * 1000;
    world.position.set(-origin.x, 0, -origin.z); world.updateMatrixWorld(true);
  }
}
function placeCamera(dt) {
  if (S.mode === 'walk') {
    var P = S.p, d = S.camDist, cp = Math.cos(S.camPitch);
    camAbs.set(P.x + Math.sin(S.camYaw) * d * cp, P.y + 1.6 + Math.sin(S.camPitch) * d, P.z + Math.cos(S.camYaw) * d * cp);
    var g = groundAt(camAbs.x, camAbs.z) + 0.6; if (camAbs.y < g) camAbs.y = g;
    camLook.set(P.x, P.y + 1.5, P.z); camUp.set(0, 1, 0); shipCam = null; watchFov = 0;
    if (window.CityCitizens) CityCitizens.cam(dt, camAbs, camLook);   // 👥 WATCH-follow a citizen (walk mode only)
  } else if (window.CityWatch && (watchFov = CityWatch.cam(dt, camAbs, camLook, camUp))) {
    var gw = groundAt(camAbs.x, camAbs.z) + 3; if (camAbs.y < gw) camAbs.y = gw; shipCam = null;
  } else {
    watchFov = 0;
    var sh = S.ship, v = shipVectors(), back = S.mode === 'boarded' || S.mode === 'countdown' ? 30 : 24 + Math.min(sh.speed, 3000) * 0.004;
    var want = new V3(sh.x, sh.y, sh.z).addScaledVector(v.f, -back).addScaledVector(S.mode === 'launch' ? UP : v.u, 7);
    if (S.mode === 'boarded' || S.mode === 'countdown') want.set(sh.x + 18, sh.y + 9, sh.z + 26);
    if (S.mode === 'launch' && S.launchT < 5.5) want.set(PAD.x + 60, PAD.y + 12 + Math.max(0, sh.y - PAD.y) * 0.75, PAD.z + 80);
    if (!shipCam) shipCam = want.clone(); else shipCam.lerp(want, Math.min(1, dt * 6));
    // keep camera within a sane tether at high speed
    tv.set(sh.x, sh.y, sh.z); if (shipCam.distanceTo(tv) > back * 2.5) shipCam.copy(want);
    camAbs.copy(shipCam);
    var gy = groundAt(camAbs.x, camAbs.z) + 2; if (camAbs.y < gy) camAbs.y = gy;
    camLook.set(sh.x, sh.y, sh.z).addScaledVector(v.f, S.mode === 'launch' && S.launchT < 5.5 ? 0 : 18);
    camUp.lerp(S.mode === 'fly' || S.mode === 'auto' ? v.u : UP, Math.min(1, dt * 3)).normalize();
  }
  rebase(camAbs.x, camAbs.z);
  camera.position.set(camAbs.x - origin.x, camAbs.y, camAbs.z - origin.z);
  camera.up.copy(camUp);
  camera.lookAt(camLook.x - origin.x, camLook.y, camLook.z - origin.z);
  var alt = Math.max(camAbs.y, 1);
  camera.near = S.mode === 'walk' ? 0.25 : clamp(alt / 2500, 0.5, 40);
  camera.far = clamp(36000 + alt * 9, 36000, 1100000);
  var fov = watchFov > 1 ? watchFov : 62 + (S.mode === 'fly' || S.mode === 'auto' ? clamp(S.ship.speed / 120, 0, 14) : 0);
  camera.fov += (fov - camera.fov) * Math.min(1, dt * 3);
  camera.updateProjectionMatrix();
  // fog + sky by altitude
  var space = smooth((alt - 9000) / 45000);
  scene.fog.near = S.mode === 'walk' ? 900 : 2500 + alt * 2;
  scene.fog.far = (S.mode === 'walk' ? 16000 : 26000 + alt * 6) / Math.max(0.02, 1 - space * 0.98);
  scene.fog.color.setRGB(0.38 * (1 - space) + 0.01, 0.15 * (1 - space) + 0.01, 0.12 * (1 - space) + 0.03);
  skyUni.uAlt.value = alt; skyUni.uDip.value = Math.sqrt(2 * alt / R_PLANET);
  sky.position.copy(camera.position); sky.scale.setScalar(camera.far * 0.6);
  stars.position.copy(camera.position); stars.scale.setScalar(camera.far * 0.55);
  stars.material.opacity = 0.32 + 0.68 * smooth((alt - 2000) / 25000);
  hemi.intensity = 0.55; sun.intensity = 1.25;
  ocean.position.set(camAbs.x - origin.x, 0, camAbs.z - origin.z);
}

/* ---------------- HUD + radar ---------------- */
var radar = $('sc-radar'), rg = radar.getContext('2d'), mapImg = null, MAP = { cx: 8600, cz: 0, span: 76000, n: MOBILE ? 150 : 200 };
function buildMap(