<!-- SOLID CITY · part 484 -->
back') === 'workshop') { S.p.x = D.workshop.x; S.p.z = D.workshop.z + 62; S.p.face = Math.atan2(-D.workshop.x, -D.workshop.z); } else if (QS.get('back') === 'strike') { S.p.x = D.strike.x; S.p.z = D.strike.z + 58; S.p.face = Math.atan2(-D.strike.x, -D.strike.z); }
      if (QS.has('auto')) enter();
    }, 30);
  }, 30);
}
function enter() {
  if (window.CityPlayer) CityPlayer.gesture();   // the ENTER tap unlocks audio on iOS
  if (QS.get('fs') === '1') { var fe = document.documentElement, rf = fe.requestFullscreen || fe.webkitRequestFullscreen; if (rf) try { var fp = rf.call(fe); if (fp && fp.catch) fp.catch(function () {}); } catch (e) {} }   // from /press-start
  $('sc-boot').hidden = true; $('sc-hud').hidden = false; if (window.AudioDrive) AudioDrive.attach(); running = true; last = performance.now(); requestAnimationFrame(frame);
  help(IS_TOUCH ? 'Left stick to walk, drag to look. Tap SABLE to talk. The spaceport is to the south-west.' : 'WASD to walk, drag to look, E to interact. Tap SABLE to talk. The spaceport is south-west — board a ship and fly.', 8);
}
$('sc-enter').disabled = true; $('sc-enter').addEventListener('click', enter);
S.camYaw = 0;
boot();

/* test / screenshot hooks (read-only debugging; no server effect) */
window.SC = {
  state: S, origin: origin,
  stats: function () { var ft = frameTimes.slice().sort(function (a, b) { return a - b; }); var avg = ft.reduce(function (a, b) { return a + b; }, 0) / (ft.length || 1); var wt = workTimes.slice().sort(function (a, b) { return a - b; }); return { workAvgMs: wt.reduce(function (a, b) { return a + b; }, 0) / (wt.length || 1), workP95Ms: wt[Math.floor(wt.length * 0.95)] || 0, fps: fpsVal, avgMs: avg, p95Ms: ft[Math.floor(ft.length * 0.95)] || 0, calls: renderer.info.render.calls, tris: renderer.info.render.triangles, terrain: terrainStats(), solids: solids.length, mem: renderer.info.memory }; },
  resetFrames: function () { frameTimes.length = 0; workTimes.length = 0; },
  fly: function (x, y, z, heading, pitch) { S.mode = 'fly'; avatar.visible = false; S.ship.x = x; S.ship.y = y; S.ship.z = z; S.ship.q.setFromAxisAngle(UP, heading || 0); rotLocal(AX, -(pitch || 0)); S.ship.speed = 300; S.ship.throttle = 0.6; setFlightUI(true); shipCam = null; },
  walk: function (x, z, yaw) { S.mode = 'walk'; avatar.visible = true; S.p.x = x; S.p.z = z; S.p.y = groundAt(x, z); if (yaw != null) S.camYaw = yaw; setFlightUI(false); },
  board: board, launch: startCountdown, talk: openTalk, soul: openSoul, rtc: returnToCity, heightAt: heightAt
};
window.SC.api = {
  T: T, S: S, D: D, PAD: PAD, SABLE_POS: SABLE_POS, VOLCANOES: VOLCANOES, ISLANDS: ISLANDS, CITY_Y: CITY_Y, IS_TOUCH: IS_TOUCH, MOBILE: MOBILE,
  world: world, scene: scene, camera: camera, renderer: renderer, origin: origin, curve: curve, labelTex: labelTex, groundAt: groundAt, heightAt: heightAt, whereName: whereName,
  solids: solids, portals: portals, RADIO_MASTS: RADIO_MASTS, input: input, help: help, openPortal: openPortal, openTalk: openTalk,
  startAuto: startAuto, shipVectors: shipVectors, maxSpeed: maxSpeed, setFlightUI: setFlightUI, avatar: avatar,
  resetShipCam: function () { shipCam = null; }, solidsTop: function (x, z, pad) { var t = -1e9; for (var k = 0; k < solids.length; k++) { var q = solids[k]; 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; },
  rotLocal: rotLocal, levelShip: levelShip, startCountdown: startCountdown, board: board, R_PLANET: R_PLANET, ship: ship,
  isBuilt: function () { return cityBuilt; }, openSoul: openSoul, SOUL: SOUL, soulTab: spTab
};
})();


===== FILE: public/city/city.js.bak-ceiling-20261014 =====
/* SOLID CITY — epistles.xyz/city (MVP, 2026-10-09)
 * Client-side only: a red/black/gold city on a ~64 km procedural island chain
 * (a nod to the Big Island), chunked quadtree LOD terrain, curved-planet shader,
 * floating origin, sky/stars/galaxy, orbit layer, walking + arcade spaceship,
 * SABLE NPC (login-gated, via /api/city/sable -> existing SABLE route).
 * Solidivism rule kept: everything visible near the ground is solid (colliders).
 */
(function () {
'use strict';
var T = window.THREE;
var $ = function (id) { return document.getElementById(id); };
var IS_TOUCH = ('ontouchstart' in window) || navigator.maxTouchPoints > 0;
var MOBILE = IS_TOUCH && Math.min(screen.width, screen.height) < 820;
var QS = new URLSearchParams(location.search);
var R_PLANET = 1200000;            // curvature radius (m) — horizon & orbit look
var CITY_Y = 22;                   // city ground height
var SEED = 20261009;
var PERF = QS.has('perf');

/* ---------------- seeded simplex noise (2D) ---------------- */
function mulberry(a) { return function () { a |= 0; a = a + 0x6D2B79F5 | 0; var t = Math.imul(a ^ a >>> 15, 1 | a); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }
var rnd = mulberry(SEED);
var perm = new Uint8Array(512), p0 = [];
fo