<!-- SOLID CITY · part 3422 -->
st; last = now; if (document.hidden) return;
  PERF.frames.push(ms); if (PERF.frames.length > 240) PERF.frames.shift();
  if (S.running && S.t > 2) { PERF.sumMs += ms; PERF.n++; }
  const dt = Math.min(ms / 1000, 0.05);
  if (S.running) for (let i = 0; i < substeps; i++) update(dt);
  renderer.render(scene, camera); adapt(now);
}

/* ======================= BOOT ======================= */
async function boot() {
  progress(0.62, 'Building the island…'); await tick();
  buildIsland(); progress(0.7, 'Raising the capital…'); await tick();
  buildCity(); buildPad(); progress(0.78, 'Fuelling your ship…'); await tick();
  buildShip(); alienGeos(); progress(0.86, 'Compiling shaders…'); await tick();
  placeOnPad(); S.running = false; updateCamera(0.016); updateEnv(0.016);
  const warm = ['scout', 'circler', 'diver', 'mother'].map((t) => makeAlien(t));   // compile every material once: no stutter on first contact
  warm.forEach((a, i) => { a.mesh.position.set(i * 20 - 30, 10, -40); });
  explode(new V3(0, 10, -40), 0.01); flushPool(sparks);
  try { renderer.compile(scene, camera); } catch (e) { /* older drivers */ }
  renderer.render(scene, camera);
  warm.forEach((a) => { scene.remove(a.mesh); a.hull.dispose(); });
  setMuted(AU.muted); setPhase('pad'); S.running = true; requestAnimationFrame(frame);
  progress(1, 'Ready');
}
window.__spaceStart = function () { auInit(); $('hud').hidden = false; reset(); };   // the loader's JUMP IN tap (unlocks audio on iOS)

window.SPACE = {
  state: S, aliens, input, bot, pad,
  stats() { const fr = PERF.frames.slice().sort((a, b) => a - b), avg = fr.reduce((a, b) => a + b, 0) / (fr.length || 1); return { fps: 1000 / avg, avgMs: avg, p95Ms: fr[Math.floor(fr.length * 0.95)] || 0, worstMs: fr[fr.length - 1] || 0, runAvgFps: PERF.n ? 1000 / (PERF.sumMs / PERF.n) : 0, pixelRatio, tier: PERF.tier, calls: renderer.info.render.calls, tris: renderer.info.render.triangles, adapt: PERF.log.slice(), touch: IS_TOUCH, mobile: MOBILE }; },
  test: { autopilot(on = true, god = false) { bot.on = on; bot.god = god; }, speed(n) { substeps = clamp(n | 0, 1, 8); }, launch, reset, phase: () => S.phase, alt: () => altOf(S.pos.y) }
};
boot().catch((e) => { progress(1, 'Error: ' + (e && e.message)); console.error(e); });


===== FILE: public/city/space/src/index.html =====
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
<meta name="theme-color" content="#000000">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>SPACE MISSION · Solid City — epistles</title>
<style>
@font-face{font-family:RC;src:url(/city/fonts/RobotoCondensed-var.woff2) format("woff2");font-weight:100 900;font-display:swap}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;height:100%;background:#000;overflow:hidden;overscroll-behavior:none;touch-action:none;color:#f4ead2;font-family:RC,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
[hidden]{display:none!important}
#gl{position:fixed;inset:0;width:100%;height:100%;display:block}
/* ---------- loader ---------- */
#load{position:fixed;inset:0;z-index:50;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:calc(20px + env(safe-area-inset-top)) 22px calc(20px + env(safe-area-inset-bottom));background:radial-gradient(90% 60% at 50% 30%,#3a0710 0,#0a0204 62%,#000 100%)}
#load .crest{font-size:40px;color:#e8c36a;text-shadow:0 0 22px rgba(232,195,106,.6)}
#load .kicker{font:700 12px/1 RC,system-ui;letter-spacing:.32em;color:#ff4d5e;margin-top:8px}
#load h1{font:800 clamp(38px,12vw,74px)/.95 RC,system-ui;letter-spacing:.06em;color:#e8c36a;margin:8px 0 10px;text-shadow:0 3px 0 #5a0410,0 0 30px rgba(255,140,60,.25)}
#load p{max-width:420px;color:#d9cfd9;line-height:1.45;margin:0 0 16px;font-size:15px}
#load .steps{display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin:0 0 18px;max-width:440px}
#load .steps span{font:700 11px/1 RC,system-ui;letter-spacing:.14em;padding:7px 9px;border-radius:999px;border:1px solid #6b5220;color:#f6dc8e;background:rgba(20,10,12,.7)}
.bar{width:min(320px,80vw);height:8px;border-radius:8px;background:#1a0e10;border:1px solid #6b5220;overflow:hidden}
.bar i{display:block;height:100%;width:0;background:linear-gradient(90deg,#a30d1d,#ff4d5e,#e8c36a);transition:width .25s}
#load-msg{margin-top:9px;font-size:12px;letter-spacing:.14em;color:#a49aa8;min-height:16px}
.big{min-width:250px;min-height:60px;border-radius:16px;border:2px solid #e8c36a;background:linear-gradient(180deg,#ff3048,#a30d1d);color:#fff;font:900 20px/1 RC,system-ui;letter-spacing:.2em;cursor:pointer;box-shadow:0 10px 30px rgba(227,18,44,.45);margin-top:16px}
.big[disabled]{opacity:.35}
.cs{display:flex;gap:8px;align-items:center;margin-top:14px;font-siz