<!-- SOLID CITY · part 3213 -->
 = [];
  STATIONS.forEach(function (s) {
    var halo = new T.Mesh(new T.TorusGeometry(11, 0.14, 8, 44), MAT.goldDim);
    halo.rotation.x = Math.PI / 2; halo.position.set(s.x, s.gy + 7, s.z);
    group.add(halo); halos.push(halo);
    if (s.ownPad) {
      var SEG = 24, rim = [], k;
      for (k = 0; k < SEG; k++) { var an = k / SEG * Math.PI * 2; rim.push([s.x + Math.cos(an) * 10, s.gy, s.z + Math.sin(an) * 10]); }
      var pos = [], sk = [], gl = [];
      function tri(arr, a, b, c) { arr.push(a[0], a[1], a[2], b[0], b[1], b[2], c[0], c[1], c[2]); }
      var ctr = [s.x, s.gy, s.z];
      for (k = 0; k < SEG; k++) {
        var r1 = rim[k], r2 = rim[(k + 1) % SEG];
        tri(pos, ctr, r1, r2);
        var d1 = [r1[0], heightAt(r1[0], r1[2]) - 0.4, r1[2]], d2 = [r2[0], heightAt(r2[0], r2[2]) - 0.4, r2[2]];
        tri(sk, r1, r2, d2); tri(sk, r1, d2, d1);
        var a1 = k / SEG * Math.PI * 2, a2 = (k + 1) / SEG * Math.PI * 2;
        tri(gl, [s.x + Math.cos(a1) * 8.55, s.gy + 0.02, s.z + Math.sin(a1) * 8.55], [s.x + Math.cos(a2) * 8.55, s.gy + 0.02, s.z + Math.sin(a2) * 8.55], [s.x + Math.cos(a2) * 9.0, s.gy + 0.02, s.z + Math.sin(a2) * 9.0]);
        tri(gl, [s.x + Math.cos(a1) * 8.55, s.gy + 0.02, s.z + Math.sin(a1) * 8.55], [s.x + Math.cos(a2) * 9.0, s.gy + 0.02, s.z + Math.sin(a2) * 9.0], [s.x + Math.cos(a1) * 9.0, s.gy + 0.02, s.z + Math.sin(a1) * 9.0]);
      }
      [[pos, MAT.stone], [sk, MAT.stoneDark], [gl, MAT.gold]].forEach(function (pr) {
        var g = new T.BufferGeometry(); g.setAttribute('position', new T.Float32BufferAttribute(pr[0], 3)); g.computeVertexNormals();
        var m = new T.Mesh(g, pr[1]); m.frustumCulled = false; group.add(m);
      });
    }
  });


  /* ================= THE TRAINS =================
   * Four cars to a train: three enclosed, and the last an open
   * observation car — a flat deck with a gold rail, riding flush
   * with the station pads so a traveller steps from stone to
   * deck in one pace. The train hovers 0.15 above its track. */
  function buildCar(open) {
    var car = new T.Group();
    if (!open) {
      car.add(box(2.9, 2.2, 8.4, MAT.titanium, 0, 1.55, 0));
      car.add(box(2.5, 0.5, 7.6, MAT.silver, 0, 2.85, 0));
      car.add(box(2.94, 0.8, 8.44, MAT.glass, 0, 1.95, 0));
      car.add(box(2.2, 0.18, 8.0, MAT.gold, 0, 0.28, 0));
    } else {
      car.add(box(2.6, 0.35, 8.2, MAT.titanium, 0, 0.28, 0));
      car.add(box(2.9, 0.12, 8.4, MAT.silver, 0, 0.51, 0));            // the deck plate — travellers stand at +0.57 over the car origin
      car.add(box(0.08, 0.85, 8.4, MAT.gold, -1.41, 1.0, 0));
      car.add(box(0.08, 0.85, 8.4, MAT.gold, 1.41, 1.0, 0));
      car.add(box(2.9, 0.85, 0.08, MAT.gold, 0, 1.0, -4.16));
      car.add(box(1.7, 0.45, 0.5, MAT.titanium, 0, 0.8, 1.6));          // a bench to sit the sky on
      car.add(box(1.7, 0.45, 0.5, MAT.titanium, 0, 0.8, -1.6));
    }
    return car;
  }
  function buildConsist() {
    var g = new T.Group(), cars = [];
    for (var i = 0; i < 4; i++) { var c = buildCar(i === 3); g.add(c); cars.push(c); }
    var prow = box(2.5, 1.5, 1.7, MAT.titanium, 0, 1.35, 4.9); prow.rotation.x = 0.5; cars[0].add(prow);
    cars[0].add(box(1.7, 0.5, 0.1, MAT.white, 0, 1.5, 5.35));
    cars[3].add(box(1.7, 0.5, 0.1, MAT.red, 0, 0.75, -4.3));
    return { group: g, cars: cars };
  }
  var trains = [];
  ROUTES.forEach(function (route, ri) {
    var built = buildConsist();
    group.add(built.group);
    var stops = route.stops;
    trains.push({
      id: ri, route: route, mesh: built.group, cars: built.cars,
      s: stops[0].s + 0.01, dir: 1, v: 0, mode: 'dwell', dwellT: 2 + ri * 2,
      stopIdx: 0, deck: { x: 0, y: -1000, z: 0, yaw: 0 }, station: stops[0].station
    });
  });
  var _v1 = new T.Vector3(), _v2 = new T.Vector3();
  function placeTrain(tr, dt) {
    for (var i = 0; i < 4; i++) {
      var sc = tr.s - tr.dir * (i * 9.4);
      var p = sample(tr.route, sc), q = sample(tr.route, sc + tr.dir * 4);
      var car = tr.cars[i];
      car.position.set(p.x, p.y + 0.15, p.z);
      _v1.set(q.x, q.y + 0.15, q.z); car.lookAt(_v1);
      if (i === 3) {
        tr.deck.x = p.x; tr.deck.y = p.y + 0.15 + 0.57; tr.deck.z = p.z;
        tr.deck.yaw = Math.atan2(q.x - p.x, q.z - p.z);
      }
    }
  }
  function stepTrain(tr, dt) {
    var stops = tr.route.stops;
    if (tr.mode === 'dwell') {
      tr.dwellT -= dt; tr.v = 0;
      if (tr.dwellT <= 0) {
        var nxt = tr.stopIdx + tr.dir;
        if (nxt < 0 || nxt >= stops.length) { tr.dir = -tr.dir; nxt = tr.stopIdx + tr.dir; }
        tr.stopIdx = nxt; tr.mode = 'run';
      }
    } else {
      var target = stops[tr.stopIdx], dStop = (target.s - tr.s) * tr.dir;
      if (dStop <= 0.05) {
        tr.s = target.s; tr.mode = 'dwell'; tr.dwellT = 4; tr.station = target.station;
      } else {
        var vT = dStop > 120 ? 26 : Math.max(0.5, 26 * dStop / 120);
        tr.v += Math.max(-3.8 * dt, Math.min(3.0 * dt, vT