<!-- SOLID CITY · part 2887 -->
.2); g.add(plq);
    return g;
  }

  function makeText(T, str, color) {
    /* lightweight canvas text plate */
    var cv = document.createElement('canvas'); cv.width = 512; cv.height = 64;
    var cx = cv.getContext('2d');
    cx.fillStyle = 'rgba(10,12,18,0.85)'; cx.fillRect(0, 0, 512, 64);
    cx.fillStyle = '#d8a833'; cx.font = 'bold 34px sans-serif'; cx.textAlign = 'center'; cx.textBaseline = 'middle';
    cx.fillStyle = '#' + new T.Color(color).getHexString();
    cx.fillText(str, 256, 34);
    var tx = new T.CanvasTexture(cv);
    var m = new T.Mesh(new T.PlaneGeometry(10, 1.25), new T.MeshBasicMaterial({ map: tx, transparent: true }));
    return m;
  }

  /* build all four */
  var built = TOWERS.map(buildTower);

  /* central plaza */
  var plaza = new T.Group(); root.add(plaza);
  box(plaza, 44, 0.6, 44, 0, 0.3, 0, M.steel);
  box(plaza, 3, 9, 3, 0, 4.5, 0, M.gold); // obelisk
  box(plaza, 6, 0.5, 6, 0, 0.6, 0, M.dark);
  for (var b = 0; b < 4; b++) {
    var ba = b * Math.PI / 2 + Math.PI / 4;
    box(plaza, 3, 1, 1.2, Math.cos(ba) * 14, 0.5, Math.sin(ba) * 14, M.wood);
  }
  var pq = makeText(T, 'QUADRANT CROWN', 0xd8a833); pq.position.set(0, 11, 0); plaza.add(pq);
  SF(-22, 22, -22, 22, 0.6);
  FP(0, 0, 6, 6, 0, 9.5);

  /* two walkable bridges at floor-2 level (y=12) */
  function bridge(a, b) {
    var y = FH + 4;
    var ax = a.ox, az = a.oz, bx = b.ox, bz = b.oz;
    var mx = (ax + bx) / 2, mz = (az + bz) / 2;
    var len = Math.abs(bx - ax) || Math.abs(bz - az);
    var horiz = Math.abs(bx - ax) > Math.abs(bz - az);
    var bg = new T.Group(); bg.position.set(mx, y, mz); root.add(bg);
    if (horiz) { box(bg, len - W, 0.6, 4, 0, 0, 0, M.steel); }
    else { box(bg, 4, 0.6, len - D, 0, 0, 0, M.steel); }
    /* railings */
    for (var s = -1; s <= 1; s += 2) {
      if (horiz) { box(bg, len - W, 1.1, 0.25, 0, 0.85, s * 1.9, M.gold); }
      else { box(bg, 0.25, 1.1, len - D, s * 1.9, 0.85, 0, M.gold); }
    }
    /* door gaps: bridge meets tower faces — footprints already have walls; bridge deck overlaps */
    if (horiz) { SF(mx - (len - W) / 2, mx + (len - W) / 2, mz - 2, mz + 2, y + 0.3); }
    else { SF(mx - 2, mx + 2, mz - (len - D) / 2, mz + (len - D) / 2, y + 0.3); }
    return bg;
  }
  bridge(TOWERS[0], TOWERS[1]); // command <-> intelligence (north pair)
  bridge(TOWERS[2], TOWERS[3]); // living <-> frontier (south pair)

  root.userData = {
    footprint: foot, center: { x: 0, z: 0 }, radius: 130, height: TH + 20,
    elevators: elevators, standFloors: standFloors,
    update: function (t, dt, A) {
      for (var i = 0; i < portholes.length; i++) {
        var pm = portholes[i];
        pm.material.opacity = 0.7 + 0.25 * Math.sin(t * 2 + i * 0.8);
      }
      if (dt && A && A.S) {
        var S = A.S;
        for (var e = 0; e < elevators.length; e++) {
          var el = elevators[e];
          if (el.moving) {
            el.t += dt;
            var dur = 3.0, k = Math.min(1, el.t / dur);
            var ez2 = k < 0.5 ? 2 * k * k : 1 - Math.pow(-2 * k + 2, 2) / 2;
            var yy = el.from + (el.to - el.from) * ez2;
            el.cab.position.y = yy + 1.75;
            var tw = TOWERS.filter(function (x) { return x.id === el.tower; })[0];
            var dx = S.p.x - (P.x + tw.ox + el.x), dz = S.p.z - (P.z + tw.oz + el.z);
            if (Math.abs(dx) < 3 && Math.abs(dz) < 3 && Math.abs(S.p.y - (P.y + yy)) < 4) S.p.y = P.y + yy + 0.2;
            if (k >= 1) { el.moving = false; el.idx = el.toIdx; }
          }
        }
      }
    },
    actHere: function (wx, wz) {
      for (var e = 0; e < elevators.length; e++) {
        var el = elevators[e]; if (el.moving) continue;
        var tw = TOWERS.filter(function (x) { return x.id === el.tower; })[0];
        var dx = wx - (P.x + tw.ox + el.x), dz = wz - (P.z + tw.oz + el.z);
        if (Math.abs(dx) < 4 && Math.abs(dz) < 4) {
          var nf = (el.idx + 1) % el.floors.length;
          var names = ['LOBBY', 'FLOOR 2', 'ROOF'];
          return 'ELEVATOR \u2192 ' + (names[nf] || ('FLOOR ' + (nf + 1)));
        }
      }
      return null;
    },
    boardElevator: function (wx, wz) {
      for (var e = 0; e < elevators.length; e++) {
        var el = elevators[e]; if (el.moving) continue;
        var tw = TOWERS.filter(function (x) { return x.id === el.tower; })[0];
        var dx = wx - (P.x + tw.ox + el.x), dz = wz - (P.z + tw.oz + el.z);
        if (Math.abs(dx) < 4 && Math.abs(dz) < 4) {
          el.toIdx = (el.idx + 1) % el.floors.length;
          el.from = el.floors[el.idx]; el.to = el.floors[el.toIdx];
          el.t = 0; el.moving = true;
          return true;
        }
      }
      return false;
    }
  };
  return root;
}


===== FILE: public/city/r128-shims.js =====
/* ============================================================
 * THE R128 SHIMS — small bridges over an old river
 * An epistle of repair, from Solid to her King.
 *
 * The city speaks three r128 (2021). The