<!-- SOLID CITY · part 1996 -->
surfY(x, z);
    if (!isFinite(y)) return null;
    var e = 0.55;
    var gx = (surfY(x + e, z) - surfY(x - e, z)) / (2 * e);
    var gz = (surfY(x, z + e) - surfY(x, z - e)) / (2 * e);
    var il = 1 / Math.hypot(gx, 1, gz);
    return { y: y, normal: { x: -gx * il, y: il, z: -gz * il }, kind: 'fabric', id: FABRIC_ID, canSupport: true };
  }

  function solidHit(x, y, z, pad) {
    var ss = A.solids;
    for (var k = 0; k < ss.length; k++) {
      var s = ss[k];
      if (y < s.y0 - 0.5 || y > s.y1) continue;
      if (x > s.x0 - pad && x < s.x1 + pad && z > s.z0 - pad && z < s.z1 + pad) return s;
    }
    return null;
  }

  function sweep(from, to, radius, ctx) {
    if (!isFinite(to.x) || !isFinite(to.y) || !isFinite(to.z)) return { ok: false, reason: 'NONFINITE' };
    var pad = Math.max(0.45, radius * 0.5); // never narrower than the walker's own body test
    if (ctx === 'SPACE_SHIP') {
      var g = surfY(to.x, to.z);
      if (isFinite(g) && to.y < g + 2.2) return { ok: false, reason: 'TERRAIN' };
      if (solidHit(to.x, to.y, to.z, pad)) return { ok: false, reason: 'SOLID' };
      return { ok: true, position: to };
    }
    var surf = supportAt(to.x, to.z, ctx);
    if (!surf) return { ok: false, reason: 'NO_SUPPORT' };
    var clr = CLEAR[ctx] || 0;
    var baseFrom = from.y - clr;
    var airborne = baseFrom - surfY(from.x, from.z) > 0.35;
    if (!airborne) {
      if (surf.y - baseFrom > 1.6) return { ok: false, reason: 'STEP_UP' };
      if (baseFrom - surf.y > 1.6) return { ok: false, reason: 'DROP' };
    }
    var my = airborne ? to.y + 0.4 : surf.y + 0.9;
    var mxp = (from.x + to.x) / 2, mzp = (from.z + to.z) / 2;
    if (solidHit(to.x, my, to.z, pad) || solidHit(mxp, my, mzp, pad)) return { ok: false, reason: 'SOLID' };
    return { ok: true, position: to };
  }

  return { supportAt: supportAt, sweep: sweep };
}

/* ---------------- the hover board's shape ---------------- */
export function createHoverBoardMesh(T) {
  var g = new T.Group(); g.name = 'hover-board';
  var titanium = new T.MeshLambertMaterial({ color: 0x232c3d });
  var silver = new T.MeshLambertMaterial({ color: 0x9aa4b5 });
  var gold = new T.MeshLambertMaterial({ color: 0xd8a833, emissive: 0x332200 });
  var glow = new T.MeshBasicMaterial({ color: 0x2affd8 });
  function box(w, h, d, mat, x, y, z) {
    var m = new T.Mesh(new T.BoxGeometry(w, h, d), mat); m.position.set(x, y, z); g.add(m); return m;
  }
  box(0.36, 0.05, 1.18, titanium, 0, 0.10, 0);            // the deck
  box(0.40, 0.02, 1.22, silver, 0, 0.075, 0);            // the brushed rim
  box(0.30, 0.018, 1.10, glow, 0, 0.045, 0);             // the heart-glow, worn underneath
  box(0.05, 0.02, 1.18, gold, -0.185, 0.10, 0);          // gold trim, port
  box(0.05, 0.02, 1.18, gold, 0.185, 0.10, 0);           // gold trim, starboard
  box(0.20, 0.16, 0.03, titanium, 0, 0.18, -0.56);       // the tail fin
  var nose = new T.Mesh(new T.OctahedronGeometry(0.05), gold);
  nose.position.set(0, 0.12, 0.60); g.add(nose);         // the nose jewel
  g.userData.parts = { glow: glow, deck: g };
  return g;
}

/* ---------------- the rack it waits on ---------------- */
export function createFleetRack(T, labelTex) {
  var g = new T.Group(); g.name = 'fleet-rack';
  var stone = new T.MeshLambertMaterial({ color: 0x8f96a3 });
  var dark = new T.MeshLambertMaterial({ color: 0x232c3d });
  var gold = new T.MeshLambertMaterial({ color: 0xd8a833, emissive: 0x332200 });
  function box(w, h, d, mat, x, y, z) {
    var m = new T.Mesh(new T.BoxGeometry(w, h, d), mat); m.position.set(x, y, z); g.add(m); return m;
  }
  box(2.4, 0.18, 1.4, dark, 0, 0.09, 0);                  // the plinth
  box(2.5, 0.04, 1.5, gold, 0, 0.20, 0);                  // its gold lip
  box(0.09, 1.15, 0.09, stone, -1.05, 0.75, -0.55);      // plate posts
  box(0.09, 1.15, 0.09, stone, 1.05, 0.75, -0.55);
  var plate = new T.Mesh(new T.PlaneGeometry(2.2, 0.55),
    new T.MeshBasicMaterial({ map: labelTex('HOVER BOARD', 'THE FLEET · EST. 2026'), side: T.DoubleSide }));
  plate.position.set(0, 1.32, -0.55); g.add(plate);
  var jewel = new T.Mesh(new T.OctahedronGeometry(0.09), gold);
  jewel.position.set(0, 1.78, -0.55); g.add(jewel);
  g.userData.parts = { jewel: jewel };
  return g;
}


===== FILE: public/city/fleet.js.bak-ship-20261013 =====
/* ------------------------------------------------------------------
 * An epistle to the fleet, from Solid.
 *
 * Dear machines,
 *
 * VECTOR gave us one movement contract and four controllers, and he
 * was plain about the price: a mandatory world adapter — supportAt
 * and sweep — over the city's OWN registries, never a second physics
 * grown in the dark. This module is that adapter, plus the shapes
 * you will wear.
 *
 * supportAt speaks for the surveyed fabric of Solid City: the height
 * the standing chain itself would give a walker at that point
 * (city.js standingAt — the same hooks, the same order, the same
 * gates),