<!-- SOLID CITY · part 1984 -->
lots THE DART.' });
    M.state = 'ready';
  }).catch(function (err) { M.state = 'failed'; try { console.warn('[city] Fleet skipped:', err && err.message); } catch (x) {} });
}
function hopButton() {
  if (M.hopBtn) return M.hopBtn;
  var b = document.createElement('button');
  b.textContent = 'HOP ⬆';
  b.style.cssText = 'position:fixed;left:50%;bottom:212px;transform:translateX(-50%);z-index:60;display:none;padding:12px 22px;border-radius:24px;border:2px solid #d8aa48;background:rgba(12,14,20,0.88);color:#ffd873;font:700 15px Georgia,serif;letter-spacing:1px;';
  b.addEventListener('pointerdown', function (ev) { ev.preventDefault(); M.jumpQ = true; });
  document.body.appendChild(b);
  M.hopBtn = b;
  return b;
}
function thrustButtons() {
  if (M.thrUp) return;
  function mk(txt, css) {
    var b = document.createElement('button');
    b.textContent = txt;
    b.style.cssText = 'position:fixed;z-index:60;display:none;padding:12px 18px;border-radius:24px;border:2px solid #d8aa48;background:rgba(12,14,20,0.88);color:#ffd873;font:700 15px Georgia,serif;letter-spacing:1px;' + css;
    document.body.appendChild(b);
    return b;
  }
  M.thrUp = mk('THRUST \u25B2', 'left:50%;bottom:264px;transform:translateX(-50%);');
  M.thrDn = mk('THRUST \u25BC', 'left:calc(50% + 108px);bottom:212px;');
  function bind(b, dir) {
    b.addEventListener('pointerdown', function (ev) { ev.preventDefault(); M.thrDir = dir; });
    ['pointerup', 'pointerleave', 'pointercancel'].forEach(function (t) { b.addEventListener(t, function () { if (M.thrDir === dir) M.thrDir = 0; }); });
  }
  bind(M.thrUp, 1); bind(M.thrDn, -1);
}
var SHIP_LINES = {
  'launch': '\u{1F680} The cradle takes her \u2014 hold on.',
  'airborne': 'Airborne. The stick flies her; THRUST \u25B2\u25BC (or R/F) work the throttle. The action button toggles ASSIST \u2014 and ASSIST lands her: bring her low and slow, and she does the rest.',
  'assist': 'ASSIST on \u2014 she levels herself and minds her speed.',
  'free': 'ASSIST off \u2014 she is yours entirely.',
  'approach': 'On approach \u2014 bring her down gentle.',
  'flare': 'Flare\u2026',
  'touchdown': 'Touchdown.',
  'parked': 'Parked. She waits where she stands \u2014 step out when you are ready.',
  'too-hot': 'Too hot \u2014 going around. Come back slower, gentler.',
  'emergency': 'The ship refused that \u2014 emergency climb.',
  'recovered': 'Recovered. She is flying again.',
  'settled': 'She set herself down where she could. No harm done.',
  'stall': 'She shudders \u2014 the wing is letting go.',
  'stall-recovery': 'Stall recovery \u2014 power off, nose down, let her fall to flying speed.',
  'stall-recovered': 'Recovered \u2014 ASSIST has her while you breathe.',
  'ceiling': 'Thin air up here \u2014 she will not climb past the ceiling.',
  'pull-up': 'Ground rush \u2014 she pulls herself up.'
};
function narrateShip(events) {
  if (!events) return;
  for (var i = 0; i < events.length; i++) {
    var line = SHIP_LINES[events[i]];
    if (line) A.help(line, 5);
  }
}
function nearestParked(x, z) {
  var best = null, bd = 1e9;
  M.crafts.forEach(function (e) {
    if (M.active === e) return;
    var lim = e.key === 'ship' ? 6.0 : 3.4; // her parked solid holds you at her hull — the door is reached from farther out
    var d = Math.hypot(x - e.park.x, z - e.park.z);
    if (d < lim && d < bd) { bd = d; best = e; }
  });
  return best;
}
function actHere(x, z) {
  if (M.active) return { kind: 'fleet', label: 'STEP OFF THE ' + M.active.name };
  if (M.state !== 'ready') return null;
  var e = nearestParked(x, z);
  if (e) return { kind: 'fleet', label: e.icon + ' RIDE THE ' + e.name };
  return null;
}
function act() {
  if (M.active) { stepOff(M.active); return; }
  if (M.state !== 'ready') return;
  var e = nearestParked(S.p.x, S.p.z);
  if (!e || Math.hypot(S.p.x - e.park.x, S.p.z - e.park.z) > (e.key === 'ship' ? 6.2 : 3.6)) return;
  /* ADMISSION FIRST (the Rung 2 law): the ride begins, THEN the parked solid lifts. */
  if (e.key === 'ship') {
    e.machine.board(e.craft, { x: e.park.x, y: supportHere(e.park.x, e.park.z), z: e.park.z }, e.park.yaw);
  } else {
    e.craft.pos.x = e.park.x; e.craft.pos.z = e.park.z;
    e.craft.pos.y = supportHere(e.park.x, e.park.z) + e.clr;
    e.craft.yaw = e.park.yaw; e.craft.mode = 'RIDE';
    if ('vx' in e.craft) { e.craft.vx = 0; e.craft.vz = 0; e.craft.vy = 0; }
    if ('v' in e.craft) { e.craft.v = 0; e.craft.vy = 0; e.craft.lean = 0; e.craft.leanRate = 0; }
  }
  M.active = e;
  removeSolid(e);
  hopButton();
  if (e.key === 'ship') thrustButtons();
  A.help(e.mountHelp, 9);
}
function stepOff(e) {
  var c = e.craft; if (!c) return;
  if (e.key === 'ship' && (!e.machine || e.machine.mode !== 'PARKED')) {
    A.help('You can\u2019t step out in flight \u2014 land her first. ASSIST will bring her down gentle.', 5);
    return;
  }
  var off = e.key === 'ship' ? 3.4 : 1.15;         // the ship's door stands off her hull
  va