<!-- SOLID CITY · part 2639 -->
 one home (the placement law: every house has a driveway)
  function serveHome(rec, roadPt) {
    var fx = Math.sin(rec.ry), fz = Math.cos(rec.ry);            // front dir
    var rx = Math.cos(rec.ry), rz = -Math.sin(rec.ry);           // right dir
    var frontL = rec.padCz + rec.padD / 2;
    var doorX = rec.x + fx * (frontL + 0.9), doorZ = rec.z + fz * (frontL + 0.9);
    // parking pad on the home's right-front side, at terrain height
    var pkX = rec.x + rx * (rec.padW / 2 + 3.6) + fx * (frontL - 3), pkZ = rec.z + rz * (rec.padW / 2 + 3.6) + fz * (frontL - 3);
    var pkY = groundAt(pkX, pkZ);
    BASE = M4(pkX, pkY + 0.06, pkZ, rec.ry, 0, 0);
    box(solid, 5.6, 0.16, 5.6, 0, 0, 0, C.path);
    standQuad(pkX, pkY + 0.06, pkZ, rec.ry, 0, 5.6, 5.6, 0.08);
    BASE = new T.Matrix4();
    if (R() < 0.55) { BASE = M4(pkX, pkY + 0.14, pkZ, rec.ry + (R() - 0.5) * 0.2, 0, 0); car(CAR_COLS[Math.floor(R() * CAR_COLS.length)]); BASE = new T.Matrix4(); }
    // driveway: parking pad -> nearest road (shared/short spurs are fine)
    if (roadPt.dist < 85) {
      // THE DRIVEWAY GRADE LAW: the nearest point of road is not
      // always a lawful target — a pad 3.4 m below a road's edge
      // and 3 m from it can only be reached by a paved wall.
      // Choose, among every station within reach, the gentlest
      // climb: rise over run, the pad's height against the
      // station's. The drive still ends at the road's own
      // surface (endY), on whichever road asks the least of it.
      var tgt = null, tScore = 1e12;
      for (var rn2 in smithRoads) { var ests = smithRoads[rn2].stations; for (var eq = 0; eq < ests.length; eq++) { var es = ests[eq], edx = es.x - pkX, edz = es.z - pkZ, edist = Math.sqrt(edx * edx + edz * edz); if (edist > 40) continue; var esc = Math.abs(es.y - (pkY + 0.06)) / Math.max(edist, 2.5); if (esc < tScore) { tScore = esc; tgt = es; } } }
      if (tgt) steppedPath(pkX, pkZ, tgt.x, tgt.z, 3.4, C.drive, 3.6, tgt.y);
    }
    // walking path: parking pad -> front steps, lined with small yellow stones (the King's photos)
    steppedPath(pkX, pkZ, doorX, doorZ, 1.7, C.path, 2.6);
    var plen = Math.hypot(doorX - pkX, doorZ - pkZ), pn = Math.max(2, Math.floor(plen / 2.2));
    for (var i = 0; i <= pn; i++) {
      var t = i / pn, sx2 = pkX + (doorX - pkX) * t, sz2 = pkZ + (doorZ - pkZ) * t;
      var ox = -(doorZ - pkZ) / (plen || 1) * 1.25, oz = (doorX - pkX) / (plen || 1) * 1.25;
      BASE = M4(sx2 + ox, groundAt(sx2 + ox, sz2 + oz) + 0.02, sz2 + oz, 0, 0, 0);
      rock(solid, 0.3, 0, 0.1, 0, C.yellowStone);
      BASE = new T.Matrix4();
    }
    // a mailbox where the path meets the drive
    BASE = M4(pkX - rx * 2.2, groundAt(pkX - rx * 2.2, pkZ - rz * 2.2), pkZ - rz * 2.2, rec.ry, 0, 0);
    box(solid, 0.14, 1.05, 0.14, 0, 0.52, 0, C.woodDark);
    box(solid, 0.75, 0.4, 0.45, 0, 1.2, 0, R() < 0.5 ? 0x8a3030 : C.stoneDark);
    BASE = new T.Matrix4();
    rec.door = [doorX, doorZ]; rec.park = [pkX, pkZ];
  }
  var CAR_COLS = [0xb8bcc2, 0x7a2e2e, 0x2e4a6a, 0xd8d3c3, 0x3a3a3e, 0x6a7a4a];

  /* ================= CLUSTERS — homes for Hawaiian families ================= */
  var plates = [];   // {x, z, ry, name}
  function plateFor(name, x, z) {
    var rp = nearestRoad(x, z);
    plates.push({ x: x, z: z, ry: faceAngle(x, z, rp.x, rp.z), name: name });
  }
  function compound(cx, cz, name, mainStyle, satStyles, opts) {
    opts = opts || {};
    var main = findSpot(cx, cz, 45, 13);
    if (!main) return 0;
    var rp = nearestRoad(main[0], main[1]);
    var mrec = buildHome(mainStyle, main[0], main[1], faceAngle(main[0], main[1], rp.x, rp.z), 1.12, 'full');
    placed.push([main[0], main[1], 17]);
    serveHome(mrec, rp);
    var count = 1;
    for (var i = 0; i < satStyles.length; i++) {
      var a = (i / satStyles.length) * Math.PI * 2 + R() * 0.8;
      var sx = cx + Math.cos(a) * (opts.ring || 30), sz = cz + Math.sin(a) * (opts.ring || 30);
      var sp = findSpot(sx, sz, 30, 11);
      if (!sp) continue;
      var srec = buildHome(satStyles[i], sp[0], sp[1], faceAngle(sp[0], sp[1], cx, cz), 0.82 + R() * 0.2, i === 0 ? 'full' : 'simple');
      placed.push([sp[0], sp[1], 15]);
      // satellites share the compound's parking — a path of yellow stones to the yard instead of a second drive
      var fx = Math.sin(srec.ry), fz = Math.cos(srec.ry), frontL = srec.padCz + srec.padD / 2;
      steppedPath(sp[0] + fx * (frontL + 1), sp[1] + fz * (frontL + 1), cx, cz, 1.6, C.path, 2.6);
      srec.door = [sp[0] + fx * (frontL + 0.9), sp[1] + fz * (frontL + 0.9)];
      count++;
    }
    // the shared yard: a great monkeypod, taro at the low side, lava-rock edging
    plantAt(cx, cz, function () { monkeypod(1.25); });
    plantAt(cx + (R() - 0.5) * 16, cz + (R() - 0.5) * 16, function () { taroPatch(7.5, 5); });
    dressHome(mrec, { taro: null, palm: true });
    homes.slice(-count).forEach(function (r2) { if (r2 !== mrec) dressHome(r2, {}); });
   