<!-- SOLID CITY · part 2713 -->
5);
    footprint(w / 2 + 0.4, 0.55, 0.4, d + 3.4, floors * fh, floors * fh + 1.05);
    stand(0, 0.55, w + 1, d + 3.4, floors * fh + 0.32);
    // exterior stair on the east end: one straight flight per floor
    for (var f2 = 0; f2 < floors - 1 + 1; f2++) {
      var y0 = f2 * fh, steps = 12;
      for (var i2 = 0; i2 < steps; i2++) {
        var sy = y0 + (i2 + 1) * (fh / steps), sz = -d / 2 + 0.4 + i2 * ((d + 0.8) / steps);
        box(solid, 2.2, 0.18, 0.8, w / 2 + 1.35, sy - 0.09, sz, C.stone);
        stand(w / 2 + 1.35, sz, 2.2, 0.85, sy);
        if (i2 % 3 === 0) footprint(w / 2 + 2.5, sz, 0.3, 0.8, sy, sy + 1.0);
        if (i2 % 3 === 0) box(solid, 0.08, 1.0, 0.7, w / 2 + 2.5, sy + 0.5, sz, C.stoneDark);
      }
      // top landing ties into the corridor end
      stand(w / 2 + 1.35, d / 2 + 1.15, 2.4, 2.3, y0 + fh + 0.1);
    }
    // ground lobby: bench + mailbox row under the corridor, by the stair
    box(solid, 2.6, 0.09, 0.5, -4, 0.5, d / 2 - 0.7, C.lanaiWood);
    for (var mb = 0; mb < 6; mb++) box(solid, 0.42, 0.55, 0.18, 2.5 + mb * 0.5, 1.35, d / 2 - 0.3, mb % 2 ? C.stoneDark : C.gold);
    // parking strip in front + cars
    box(solid, w, 0.14, 5, 0, 0.03, d / 2 + 5.2, C.road);
    var carCols = [0xb8bcc2, 0x7a2e2e, 0x2e4a6a, 0xd8d3c3];
    for (var ci = 0; ci < 4; ci++) {
      BASE = M4(x, baseY, z, ry, 0, 0);
      var cp = wpt(-w / 2 + 4 + ci * 6.4, d / 2 + 5.2);
      BASE = M4(cp[0], groundAt(cp[0], cp[1]) + 0.12, cp[1], ry + (ci % 2 ? 0.06 : -0.04), 0, 0);
      car(carCols[ci]);
    }
    clearHome();
    var rec = { x: x, z: z, y: baseY, ry: ry, style: 'apartment', w: w, d: d, units: floors * 2 };
    homes.push(rec);
    return rec;
  }

  /* ================= ROADS (the ancestors' road network) ================= */
  var ROAD_DEFS = [
    // A: the south plain road — past the Gate, from the base flats to the shore flats
    [[640, -1005], [430, -1065], [190, -1040], [30, -1022], [-180, -1045], [-420, -1100], [-650, -1155]],
    // B: the east climb — up the green benches toward the Overlook, then the high bench
    [[430, -1065], [560, -1180], [700, -1330], [820, -1470], [900, -1620], [1010, -1700], [1150, -1720], [1250, -1620]],
    // C: the north road — from the east climb up to the colosseum-road homes
    [[860, -1560], [845, -1800], [800, -2030], [720, -2240], [580, -2340], [460, -2385]]
  ];
  var roadPts = [];
  function buildRoads() {
    ROAD_DEFS.forEach(function (line) {
      for (var i = 0; i < line.length - 1; i++) {
        var ax = line[i][0], az = line[i][1], bx = line[i + 1][0], bz = line[i + 1][1];
        var len = Math.hypot(bx - ax, bz - az), n = Math.ceil(len / 7), ang = Math.atan2(bx - ax, bz - az);
        for (var j = 0; j < n; j++) {
          var t = (j + 0.5) / n, px = ax + (bx - ax) * t, pz = az + (bz - az) * t;
          var py = groundAt(px, pz);
          roadPts.push([px, pz]);
          BASE = M4(px, py + 0.1, pz, ang, 0, 0);
          box(solid, 7.6, 0.22, 8.6, 0, 0, 0, C.road);
          if (j % 2 === 0) box(solid, 0.35, 0.05, 2.2, 0, 0.14, 0, C.path);   // center dash
          BASE = new T.Matrix4();
        }
      }
    });
    // street lights along the roads, alternating sides
    for (var k = 6; k < roadPts.length; k += 9) {
      var p = roadPts[k], side = (k % 18 === 6) ? 1 : -1;
      var lx = p[0] + side * 5.2, lz = p[1] + ((k * 7) % 5) - 2;
      BASE = M4(lx, groundAt(lx, lz) - 0.1, lz, R() * 3, 0, 0);
      streetLight();
      BASE = new T.Matrix4();
    }
  }
  function nearestRoad(x, z) {
    var best = null, bd = 1e12;
    for (var i = 0; i < roadPts.length; i++) {
      var dx = roadPts[i][0] - x, dz = roadPts[i][1] - z, dd = dx * dx + dz * dz;
      if (dd < bd) { bd = dd; best = roadPts[i]; }
    }
    return { x: best[0], z: best[1], dist: Math.sqrt(bd) };
  }
  function steppedPath(x0, z0, x1, z1, wdt, col, stepLen) {
    var len = Math.hypot(x1 - x0, z1 - z0), n = Math.max(1, Math.round(len / stepLen)), ang = Math.atan2(x1 - x0, z1 - z0);
    for (var j = 0; j < n; j++) {
      var t = (j + 0.5) / n, px = x0 + (x1 - x0) * t, pz = z0 + (z1 - z0) * t;
      BASE = M4(px, groundAt(px, pz) + 0.08, pz, ang, 0, 0);
      box(solid, wdt, 0.18, stepLen + 0.7, 0, 0, 0, col);
      BASE = new T.Matrix4();
    }
  }

  /* ================= SITING — the placement law, enforced ================= */
  var EXCLUDE = [   // [x, z, radius] — structures and ground that stay untouched
    [0, -1600, 578],      // THE ANCESTRAL ROADS terrace (platform r=560)
    [0, -3200, 430],      // Imperial Colosseum
    [0, 0, 1060],         // the capital
    [0, -950, 205],       // Gate of Ancestors
    [450, -1150, 205],    // Compass Meridian
    [750, -1600, 255],    // Overlook Mira
    [-750, -1600, 255],   // Roost Keel
    [-700, -700, 290]     // Quadrant Crown
  ];
  var placed = [];
  function clearOf(x, z, r) {
    for (var i = 0; i < EXCLUDE.length; i++) {
      var e = EXCLUDE[i];
      if (Mat