<!-- SOLID CITY · part 3421 -->
h === 'climb'); H['b-fire'].hidden = ctl; H['b-boost'].hidden = ctl;
  H.stick.hidden = !IS_TOUCH || !(ph === 'battle' || ph === 'return' || ph === 'climb' || ph === 'landing');
  if (PERF.show) H['h-perf'].textContent = PERF.fps.toFixed(0) + ' FPS · ' + pixelRatio.toFixed(2) + 'x · ' + renderer.info.render.calls + ' calls';
}

/* ======================= RESULT + LEADERBOARD ======================= */
function callsign() { try { return (localStorage.getItem('space-callsign') || '').slice(0, 16); } catch (e) { return ''; } }
function rankTitle(r) { const s = r.timeMs / 1000; return r.kills < TOTAL_ALIENS ? 'CADET' : s < 150 ? 'ACE OF SOLID CITY' : s < 210 ? 'SKY KNIGHT' : s < 300 ? 'WING COMMANDER' : 'PILOT'; }
const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
async function showResult(r) {
  if (S.phase !== 'parked') return;
  setPhase('result'); $('hud').hidden = true; $('result').hidden = false;
  $('r-time').textContent = fmtT(r.timeMs / 1000); $('r-kills').textContent = r.kills + ' / ' + TOTAL_ALIENS; $('r-hits').textContent = String(r.hits); $('r-top').textContent = r.top + ' km/h'; $('r-title').textContent = rankTitle(r);
  $('r-board').innerHTML = '<li class="dim">Saving your result…</li>'; $('r-save').textContent = '';
  let mine = null;
  try {
    const res = await fetch('/api/city/space/score', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: callsign() || 'PILOT', timeMs: r.timeMs, kills: r.kills, hits: r.hits }) });
    const j = await res.json(); S.saved = j;
    if (j.ok) { mine = j.row; $('r-save').textContent = 'Saved · rank #' + j.rank + ' of ' + j.total; } else $('r-save').textContent = 'Not saved: ' + (j.error || res.status);
  } catch (e) { $('r-save').textContent = 'Not saved (offline?)'; S.saved = { ok: false }; }
  try {
    const b = await (await fetch('/api/city/space/board', { cache: 'no-store' })).json(); S.board = b;
    $('r-board').innerHTML = (b.top || []).map((x, i) => `<li class="${mine && x.at === mine.at && x.name === mine.name ? 'me' : ''}"><b>${i + 1}</b><span>${esc(x.name)}</span><i>${fmtT(x.timeMs / 1000)}</i><em>${x.kills}</em></li>`).join('') || '<li class="dim">No results yet.</li>';
  } catch (e) { $('r-board').innerHTML = '<li class="dim">Leaderboard unavailable.</li>'; }
}
$('r-again').addEventListener('click', () => { auInit(); reset(); });
$('h-mute').addEventListener('click', () => { auInit(); setMuted(!AU.muted); });
H['b-launch'].addEventListener('click', launch);

/* ======================= TEST PILOT (scripted input for automated checks) ======================= */
function botInput() {
  const o = { x: 0, y: 0, fire: false, boost: false }, ph = S.phase;
  if (ph === 'pad') { launch(); return o; }
  const steerTo = (p) => { tv.copy(p).sub(S.pos); const d = tv.length(), wy = Math.atan2(-tv.x, -tv.z), wp = Math.asin(clamp(tv.y / d, -1, 1)); o.x = clamp(-wrapA(wy - S.yaw) * 2.5, -1, 1); o.y = clamp((wp - S.pitch) * 2.5, -1, 1); return { d, err: Math.abs(wrapA(wy - S.yaw)) + Math.abs(wp - S.pitch) }; };
  if (ph === 'battle') {
    const t = aliens.filter((a) => a.alive).sort((a, b) => a.pos.distanceToSquared(S.pos) - b.pos.distanceToSquared(S.pos))[0];
    if (t) { const r = steerTo(tv3.copy(t.pos).addScaledVector(t.vel, t.pos.distanceTo(S.pos) / 1600)); o.fire = r.err < 0.45 && r.d < 1500; o.boost = r.d > 1100 && S.boostMeter > 0.3; }
  } else if (ph === 'return') { const hd = Math.hypot(S.pos.x, S.pos.z); steerTo(tv3.set(0, Math.min(S.pos.y, 380 + hd * 0.12), 0)); o.boost = S.boostMeter > 0.3 && altOf(S.pos.y) < 1500; }
  return o;
}

/* ======================= LOOP + ADAPTIVE QUALITY ======================= */
const PERF = { show: QS.has('perf'), frames: [], fps: 60, lastAdj: 0, log: [], tier: 2, sumMs: 0, n: 0 };
if (PERF.show) H['h-perf'].hidden = false;
function adapt(now) {
  if (now - PERF.lastAdj < 1500 || PERF.frames.length < 40) return; PERF.lastAdj = now;
  const fr = PERF.frames.slice(-60), avg = fr.reduce((a, b) => a + b, 0) / fr.length, fps = 1000 / avg; PERF.fps = fps;
  if (QS.get('dpr')) return;
  let changed = false;
  if (fps < 47 && pixelRatio > MIN_PR) { pixelRatio = Math.max(MIN_PR, pixelRatio - (fps < 35 ? 0.2 : 0.1)); changed = true; }
  else if (fps < 40 && PERF.tier > 0) { PERF.tier--; clouds.mesh.geometry.instanceCount = Math.round(clouds.n * (PERF.tier ? 0.65 : 0.4)); dust.visible = PERF.tier > 0; changed = true; }
  else if (fps > 58.5 && pixelRatio < MAX_PR - 0.01) { pixelRatio = Math.min(MAX_PR, pixelRatio + 0.1); changed = true; }
  if (changed) { renderer.setPixelRatio(pixelRatio); fit(); PERF.log.push({ t: Math.round(S.t), fps: Math.round(fps), pr: +pixelRatio.toFixed(2), tier: PERF.tier }); if (PERF.log.length > 40) PERF.log.shift(); }
}
let last = 0, substeps = 1;
function frame(now) {
  requestAnimationFrame(frame);
  if (!last) last = now; const ms = now - la