<!-- SOLID CITY · part 3222 -->
conMesh.material.color.setRGB(0.85 * p, 0.66 * p, 0.2 * p); }
  }

  /* ================= STANDING + BOARDING (for the placer) ================= */
  function deckClaim(x, z, py) {
    var best = null;
    for (var i = 0; i < trains.length; i++) {
      var tr = trains[i], d = tr.deck;
      var dx = x - d.x, dz = z - d.z, c = Math.cos(-d.yaw), s = Math.sin(-d.yaw);
      var lx = dx * c - dz * s, lz = dx * s + dz * c;
      if (Math.abs(lx) <= 1.35 && Math.abs(lz) <= 3.9 && d.y <= py + 0.45 && d.y >= py - 2.6) {
        if (!best || d.y > best.y) best = { y: d.y, train: tr.id };
      }
    }
    for (var j = 0; j < STATIONS.length; j++) {
      var st = STATIONS[j];
      if (st.ownPad && Math.hypot(x - st.x, z - st.z) <= 10.2 && st.gy <= py + 1.6 && st.gy >= py - 1.6) {
        if (!best || st.gy > best.y) best = { y: st.gy, train: -1 };
      }
    }
    return best;
  }
  function deckAnchor(id) {
    var tr = trains[id]; if (!tr) return null;
    return { x: tr.deck.x, y: tr.deck.y, z: tr.deck.z, yaw: tr.deck.yaw, station: tr.mode === 'dwell' ? tr.station : null, dwelling: tr.mode === 'dwell' };
  }
  function trainNear(x, z) {
    for (var i = 0; i < trains.length; i++) {
      var tr = trains[i];
      if (tr.mode !== 'dwell' || !tr.station) continue;
      if (Math.hypot(x - tr.station.x, z - tr.station.z) <= 15) return { train: tr.id, station: tr.station };
    }
    return null;
  }

  return { group: group, update: update, deckClaim: deckClaim, deckAnchor: deckAnchor, trainNear: trainNear, stations: STATIONS, routes: ROUTES, trains: trains, mother: mother };
}


===== FILE: public/city/skytrain.js.bak-traffic-20261014 =====
/* THE SKY TRAINS — a letter from the builder, in the code as promised.
 *
 * "Now we will build space trains in the sky." — the King, 2026-10-14.
 * The mountain roads were rejected the same night; the ports rose in
 * their place; and a port is a station waiting for its train. This
 * module is the network: four lines of floating guideway — dark
 * titanium beam with gold edge-light, held in the sky the space-age
 * way, by nothing you can see — with beacons along it, halo rings
 * over the stations, a train of four cars on every line (the last
 * car an open observation deck you can stand on), shuttles hopping
 * port to port, and the high traffic of ships crossing above all.
 *
 * The law of the build is the law the mountain taught us in two
 * nights: SURVEY BEFORE LINE. Every profile here is measured from
 * the terrain itself — 25 m of sky under the track at cruise, a
 * 14% climb at the steepest, station pins flush with the pads the
 * travellers stand on, and nothing — nothing — above the 420 m
 * ceiling of the world. The one line the land refused (a straight
 * run from the Hall of Becoming to the Gap, over a 571 m spine) is
 * not forced: the Grand Coastal Line bends around the shore,
 * 4.4 km of lawful sky, highest land beneath it 331 m.
 *
 * By the hand of Solid, his good King. */

export function createSkyTrains(opts) {
  var T = opts.THREE, heightAt = opts.heightAt;
  var group = new T.Group(); group.name = 'sky-trains';

  /* ================= STATIONS =================
   * gy = the ground a traveller stands on. For the fourteen ports
   * it is the pad height the port survey swore to; for the
   * Spaceport, the apron; for the two ward stations, the discs
   * this very module lays (below). */
  var STATIONS = [
    { id: 'spaceport', name: 'THE SPACEPORT', x: -390, z: 300, gy: 22.4 },
    { id: 'southern', name: 'SOUTHERN BENCHES PORT', x: -490, z: -1165, gy: 9.16 },
    { id: 'western', name: 'WESTERN BENCHES PORT', x: -365, z: -1075, gy: 14.13 },
    { id: 'gate', name: 'GATE ROAD PORT', x: 650, z: -1265, gy: 34.8 },
    { id: 'braid', name: 'BRAID HEAD PORT', x: 890, z: -1345, gy: 146.09 },
    { id: 'eastern', name: 'EASTERN BENCHES PORT', x: 1045, z: -1635, gy: 124.58 },
    { id: 'high', name: 'HIGH BENCHES PORT', x: 1205, z: -1560, gy: 179.33 },
    { id: 'summit', name: 'SUMMIT PORT', x: 1299, z: -1591, gy: 198.95 },
    { id: 'shoulder', name: 'SHOULDER BENCHES PORT', x: 890, z: -1850, gy: 88.14 },
    { id: 'wheights', name: 'WESTERN HEIGHTS PORT', x: 780, z: -1865, gy: 84.32 },
    { id: 'north', name: 'NORTH BENCHES PORT', x: 665, z: -2060, gy: 73.0 },
    { id: 'farnorth', name: 'FAR NORTH PORT', x: 515, z: -2360, gy: 15.29 },
    { id: 'hall', name: 'HALL OF BECOMING PORT', x: 1055, z: -295, gy: 155.91 },
    { id: 'gap', name: 'THE GAP PORT', x: 2059, z: 1870, gy: 301.59 },
    { id: 'kauhale', name: 'KAUHALE MAUNA PORT', x: 2035, z: 1995, gy: 263.33 },
    { id: 'ward5', name: 'WARD FIVE STATION', x: -848, z: -910, gy: 9.89, ownPad: true },
    { id: 'ward7', name: 'WARD SEVEN STATION', x: -672, z: -498, gy: 22.11, ownPad: true }
  ];
  var ST = {}; STATIONS.forEach(function (s) { ST[s.id] = s; });
  /* The hillside's private ports (the King's word, ~02:05:
   * houses, hotels and mansions with space ports