<!-- SOLID CITY · part 2528 -->
egDistToRoad(taroX, taroZ).d < 8) { taroX = yardX * 2 - taroX; taroZ = yardZ * 2 - taroZ; }   // the patch keeps the verge too: mirrored across the yard's heart, same seeds
    var tSteps = 0;
    while (segDistToRoad(taroX, taroZ).d < 8 && tSteps < 40) { var tn = segDistToRoad(taroX, taroZ); var tux = taroX - tn.x, tuz = taroZ - tn.z, tul = Math.hypot(tux, tuz) || 1; taroX += (tux / tul) * 0.5; taroZ += (tuz / tul) * 0.5; tSteps++; }
    plantAt(taroX, taroZ, function () { taroPatch(7.5, 5); });
    dressHome(mrec, { taro: null, palm: true });
    homes.slice(-count).forEach(function (r2) { if (r2 !== mrec) dressHome(r2, {}); });
    plateFor(name, cx, cz);
    return count;
  }
  function singles(cx, cz, name, n, styles, rad) {
    var count = 0;
    for (var i = 0; i < n; i++) {
      var sp = findSpot(cx, cz, rad || 95, 12);
      if (!sp) continue;
      var rp = nearestRoad(sp[0], sp[1]);
      var style = styles[i % styles.length];
      var rec = buildHome(style, sp[0], sp[1], faceAngle(sp[0], sp[1], rp.x, rp.z), 0.92 + R() * 0.3, R() < 0.5 ? 'full' : 'simple');
      placed.push([sp[0], sp[1], 16]);
      serveHome(rec, rp);
      dressHome(rec, { palm: R() < 0.4 });
      count++;
    }
    if (count) plateFor(name, cx, cz);
    return count;
  }
  function scatterTrees(n) {
    var tries = 0, done = 0;
    while (done < n && tries < n * 6) {
      tries++;
      var x = -1050 + R() * 2450, z = -2650 + R() * 1900;
      if (!clearOf(x, z, 13)) continue;
      var h = groundAt(x, z);
      if (h < 9 || h > 420) continue;
      if (slopeAt(x, z, 9) > 0.5) continue;
      var rp = nearestRoad(x, z);
      if (rp.dist < 10) continue;
      var kind = R();
      plantAt(x, z, function () {
        if (kind < 0.34) monkeypod(0.8 + R() * 0.5);
        else if (kind < 0.58) ohia(0.9 + R() * 0.5);
        else if (kind < 0.78) palm(0.85 + R() * 0.4);
        else breadfruit(0.9 + R() * 0.4);
      });
      placed.push([x, z, 7]);
      done++;
    }
    return done;
  }

  /* ================= NAME PLATES — one quiet plate per cluster ================= */
  var extras = [];
  function buildPlates() {
    plates.forEach(function (p) {
      var gy = groundAt(p.x, p.z);
      BASE = M4(p.x, gy, p.z, p.ry, 0, 0);
      box(solid, 0.22, 2.6, 0.22, -4.6, 1.3, 0, C.woodDark);
      box(solid, 0.22, 2.6, 0.22, 4.6, 1.3, 0, C.woodDark);
      /* the posts take bodies too — a sign you can read is a sign
       * you can bump (same finding, same law) */
      for (var ps = -1; ps <= 1; ps += 2) foot.push([p.x + ps * 4.6 * Math.cos(p.ry), p.z - ps * 4.6 * Math.sin(p.ry), 0.34, 0.34, gy - 0.1, gy + 2.6, 0]);
      BASE = new T.Matrix4();
      if (typeof document === 'undefined' || !document.createElement) return;
      try {
        var cv = document.createElement('canvas'); cv.width = 1024; cv.height = 160;
        var cx2 = cv.getContext('2d');
        cx2.fillStyle = '#23272e'; cx2.fillRect(0, 0, 1024, 160);
        cx2.strokeStyle = '#c9a44a'; cx2.lineWidth = 5; cx2.strokeRect(10, 10, 1004, 140);
        cx2.fillStyle = '#e8c86a'; cx2.textAlign = 'center'; cx2.textBaseline = 'middle';
        var fs = 64; cx2.font = 'bold ' + fs + 'px Georgia, serif';
        while (cx2.measureText(p.name).width > 940 && fs > 26) { fs -= 4; cx2.font = 'bold ' + fs + 'px Georgia, serif'; }
        cx2.fillText(p.name, 512, 84);
        var tex = new T.CanvasTexture(cv);
        var m = new T.Mesh(new T.PlaneGeometry(11, 1.72), new T.MeshBasicMaterial({ map: tex, side: T.DoubleSide }));
        m.position.set(p.x, gy + 2.45, p.z); m.rotation.y = p.ry;
        extras.push(m);
      } catch (e) {}
    });
  }

  /* ================= BUILD ================= */
  /* THE RAIL TAKES A BODY (the King's finding, 2026-10-13 ~23:34).
   * The smith draws a silver rail on the downhill edge of every
   * bench road and registers nothing for it — the King walked
   * through it at 189 m. Now each station-to-station run of rail
   * registers a body in the footprint list: 0.3 m thick, from just
   * under the edge to just above the rail band, on the side the
   * smith's own upSide verdict names. Lawful gaps are kept: the
   * junction braids (11 m from a junction heart, both ends
   * judged), every driveway mouth (4.5 m), and anywhere a chord
   * would cut inside 3.95 m of any built centreline. */
  function railBodies() {
    var JX = [[945, -1487.5], [915, -1285], [430, -1065]];
    ['B1', 'B2', 'C', 'S'].forEach(function (nm) {
      var rd = smithRoads[nm]; if (!rd || !rd.stations) return;
      var sd = -(rd.upSide || 1), sts = rd.stations;
      var run = null; // a straight run of rail, merged into one body
      function flush() {
        if (!run) return;
        var dx = run.x1 - run.x0, dz = run.z1 - run.z0;
        foot.push([(run.x0 + run.x1) / 2, (run.z0 + run.z1) / 2, Math.hypot(dx, dz) + 0.25, 0.3, run.y0, run.y1, Math.atan2(-dz, dx)]);
        run = null;
      }
      for (var i = 0; i < sts