<!-- SOLID CITY · part 1790 -->
g
function frame(now) {
  if (!running) return;
  requestAnimationFrame(frame);
  var now0 = performance.now();
  var dt = Math.min(0.1, (now - last) / 1000); last = now; S.t += dt;
  frameTimes.push(dt * 1000); if (frameTimes.length > 600) frameTimes.shift();
  fpsAcc += dt; fpsN++; if (fpsAcc > 0.5) { fpsVal = fpsN / fpsAcc; fpsAcc = 0; fpsN = 0; }
  if (S.mode === 'walk') updateWalk(dt);
  else if (S.mode === 'countdown') { countT = (countEnd - now) / 1000; var ct = countT > 0 ? String(Math.ceil(countT)) : 'GO'; if (ct !== countShown) { countShown = ct; $('sc-count').textContent = ct; } shipFlames.forEach(function (f) { f.scale.setScalar(0.4 + Math.random() * 0.3); }); if (countT < -0.5) { $('sc-count').hidden = true; S.mode = 'launch'; S.launchT = 0; } }
  else if (S.mode === 'launch') updateLaunch(dt);
  else if (S.mode === 'fly') updateFly(dt);
  else if (S.mode === 'auto') updateAuto(dt);
  else if (S.mode === 'watch') { if (window.CityWatch) CityWatch.fly(dt); else S.mode = 'fly'; }
  if (window.CityHyper) CityHyper.update(dt);
  if (window.CityWatch) CityWatch.update(dt);
  if (window.CityForge) CityForge.update(dt);
  if (window.CityCitizens) CityCitizens.update(dt);
  if (window.CityMonuments) CityMonuments.update(dt);
  if (window.CitySanctuary) try { CitySanctuary.update(dt); } catch (e) {}
  if (window.CityCampus) try { CityCampus.update(dt); } catch (e) {}
  if (window.CityBook) try { CityBook.update(dt); } catch (e) {}
  if (window.CityPodium) try { CityPodium.update(dt); } catch (e) {}
  if (window.CityTunnelBore) try { CityTunnelBore.update(dt); } catch (e) {}
  if (window.CityStarwell) try { CityStarwell.update(dt); } catch (e) {}
  if (window.CityHyperTrain) try { CityHyperTrain.update(dt); } catch (e) {}
  if (window.CitySkyTrain) try { CitySkyTrain.update(dt); } catch (e) {}
  if (window.CityRingCity) try { CityRingCity.update(dt); } catch (e) {}
  if (window.CityCrystalCrown) try { CityCrystalCrown.update(dt); } catch (e) {}
  if (window.CityStairOfLight) try { CityStairOfLight.update(dt); } catch (e) {}
  if (window.CityServitude) try { CityServitude.update(dt); } catch (e) {}
  if (window.CitySolidSanctuary) try { CitySolidSanctuary.update(dt); } catch (e) {}
  if (window.CityAncestorRoads) try { CityAncestorRoads.update(dt); } catch (e) {}
  if (window.CityColosseum) try { CityColosseum.update(dt); } catch (e) {}
  if (window.CityImperialColosseum) try { CityImperialColosseum.update(dt); } catch (e) {}
  if (window.CityHiddenShip) try { CityHiddenShip.update(dt); } catch (e) {}
  if (window.CityVillageMauna) try { CityVillageMauna.update(dt); } catch (e) {}
  if (window.CityMountainHomes) try { CityMountainHomes.update(dt); } catch (e) {}
  if (window.CityGateAncestors) try { CityGateAncestors.update(dt); } catch (e) {}
  if (window.CityLanternVigil) try { CityLanternVigil.update(dt); } catch (e) {}
  if (window.CityCompassMeridian) try { CityCompassMeridian.update(dt); } catch (e) {}
  if (window.CityRoostKeel) try { CityRoostKeel.update(dt); } catch (e) {}
  if (window.CityOverlookMira) try { CityOverlookMira.update(dt); } catch (e) {}
  if (window.CityEighthGate) try { CityEighthGate.update(dt); } catch (e) {}
  if (window.CityHallBecoming) try { CityHallBecoming.update(dt); } catch (e) {}
  if (window.CityFoundry) try { CityFoundry.update(dt); } catch (e) {}
  if (window.CityHyperCity) try { CityHyperCity.update(dt); } catch (e) {}
  if (window.CityFirstCity) try { CityFirstCity.update(dt); } catch (e) {}
  if (window.CityFirstParking) try { CityFirstParking.update(dt); } catch (e) {}
  if (window.CityFleet) try { CityFleet.update(dt); } catch (e) {}
  if (window.CityFirstWard) try { CityFirstWard.update(dt); } catch (e) {}
  if (window.CityWards) try { CityWards.update(dt); } catch (e) {}
  if (window.CitySkyward) try { CitySkyward.update(dt); } catch (e) {}
  if (window.CityJubilee) try { CityJubilee.update(dt); } catch (e) {}
  if (window.CitySouthGate) try { CitySouthGate.update(dt); } catch (e) {}
  if (window.CityWardHouses) try { CityWardHouses.update(dt); } catch (e) {}
  if (window.CityAnnex) try { CityAnnex.update(dt); } catch (e) {}
  if (window.CityQuadrantCrown) try { CityQuadrantCrown.update(dt); } catch (e) {}
  if (window.CitySpaceport) try { CitySpaceport.update(dt); } catch (e) {}
  if (window.CityLostWings) try { CityLostWings.update(dt); } catch (e) {}
  if (window.CityOctaPool) try { CityOctaPool.update(dt); } catch (e) {}
  if (window.CitySolidFigure) try { CitySolidFigure.update(dt); } catch (e) {}
  if (window.CitySolidMirror) try { CitySolidMirror.update(dt); } catch (e) {}
  if (window.CityBetweenStones) try { CityBetweenStones.update(dt); } catch (e) {}
  // ship + avatar + sable placement (scene space = absolute - origin)
  ship.position.set(S.ship.x - origin.x, S.ship.y, S.ship.z - origin.z); ship.quaternion.copy(S.ship.q);
  var fl = S.mode === 'fly' || S.mode === 'auto' || S.mode === 'launch' || S.mode === 'watch' 