<!-- NEW1 — TEST 1 · THE LIVING DISTRICT · part 6 -->
 housePalette = [0xd8cbb2, 0xc4d2dc, 0xe0d4e2, 0xd2dec8, 0xe8dcc4, 0xccd8e4]
    .map(c => new THREE.MeshStandardMaterial({ color: c, roughness: 1, flatShading: true }));

  /* ---------- shared geometries ---------- */
  const G = {
    unit: new THREE.BoxGeometry(1, 1, 1),
    pole: new THREE.CylinderGeometry(0.09, 0.11, 3.4, 6),
    lampBall: new THREE.SphereGeometry(0.16, 8, 6),
    trunk: new THREE.CylinderGeometry(0.14, 0.2, 1.4, 6),
    canopy: new THREE.ConeGeometry(1.5, 3.0, 7),
    roofPrism: new THREE.ConeGeometry(1, 1, 4),
  };
  function box(mat, sx, sy, sz, x, y, z, parent, ry) {
    const m = new THREE.Mesh(G.unit, mat);
    m.scale.set(sx, sy, sz);
    m.position.set(x, y, z);
    if (ry) m.rotation.y = ry;
    (parent || group).add(m);
    return m;
  }

  /* ---------- the map floor (y=0, everything stands on it) ---------- */
  const floor = new THREE.Mesh(new THREE.PlaneGeometry(W + 40, D + 40), M.ground);
  floor.rotation.x = -Math.PI / 2;
  floor.position.y = 0;
  group.add(floor);

  /* ---------- roads: flat, between the blocks ---------- */
  for (let i = 0; i <= COLS; i++) {                       // vertical streets
    const p = new THREE.Mesh(new THREE.PlaneGeometry(STREET, D), M.road);
    p.rotation.x = -Math.PI / 2;
    p.position.set(streetX(i), 0.02, 0);
    group.add(p);
  }
  for (let j = 0; j <= ROWS; j++) {                       // horizontal streets
    if (j === RIVER_ROW || j === RIVER_ROW + 1) continue; // river band has its own banks
    const p = new THREE.Mesh(new THREE.PlaneGeometry(W, STREET), M.road);
    p.rotation.x = -Math.PI / 2;
    p.position.set(0, 0.02, streetZ(j));
    group.add(p);
  }

  /* ---------- sidewalks: a pale frame around every block ---------- */
  for (const c of COMPLEXES) {
    const { x, z } = blockCenter(c.col, c.row);
    box(M.walk, BLOCK + 3, 0.12, BLOCK + 3, x, 0.06, z);
  }

  /* ---------- parking stalls: marked bays along street edges
   * downtown + parking + market blocks get stalls on their streets */
  function stalls(cx, cz, horizontal, count, side) {
    for (let i = 0; i < count; i++) {
      const off = (i - (count - 1) / 2) * 5.2;
      if (horizontal) box(M.stall, 4.6, 0.03, 0.28, cx + off, 0.05, cz + side * (STREET / 2 - 2.4));
      else box(M.stall, 0.28, 0.03, 4.6, cx + side * (STREET / 2 - 2.4), 0.05, cz + off);
    }
  }
  for (const c of COMPLEXES) {
    if (!['downtown', 'parking', 'market'].includes(c.kind)) continue;
    const { x, z } = blockCenter(c.col, c.row);
    stalls(x, z - BLOCK / 2 - STREET / 2, true, 9, -1);
    if (c.row !== RIVER_ROW - 1) stalls(x, z + BLOCK / 2 + STREET / 2, true, 9, 1);
  }

  /* ---------- stop lights at intersections (static lamps for now;
   * behavior gets wired by the traffic layer later) ---------- */
  const lampHeads = [];
  function stopLight(x, z) {
    const g = new THREE.Group();
    g.position.set(x, 0, z);
    const pole = new THREE.Mesh(G.pole, M.pole);
    pole.position.y = 1.7; g.add(pole);
    const head = box(M.pole, 0.42, 1.15, 0.42, 0, 3.85, 0, g);
    head.name = 'stoplight-head';
    const lamps = [M.lampR, M.lampY, M.lampG];
    for (let k = 0; k < 3; k++) {
      const b = new THREE.Mesh(G.lampBall, lamps[k]);
      b.position.set(0, 4.22 - k * 0.37, 0.24);
      g.add(b);
    }
    group.add(g);
    lampHeads.push(g);
    return g;
  }
  for (let i = 0; i <= COLS; i++) {
    for (let j = 0; j <= ROWS; j++) {
      if (j >= RIVER_ROW && j <= RIVER_ROW + 1) continue; // river crossings handled at bridges
      stopLight(streetX(i) + STREET / 2 - 1.2, streetZ(j) + STREET / 2 - 1.2);
    }
  }

  /* ---------- the river: a recessed channel across the district,
   * water below street level, stone banks, bridges over it ---------- */
  const riverZ = blockCenter(0, RIVER_ROW).z;
  const riverW = BLOCK + STREET; // channel occupies the river row's band
  const channel = box(M.bank, W, 0.5, riverW + 6, 0, -0.28, riverZ);
  channel.name = 'river-channel';
  const water = new THREE.Mesh(new THREE.PlaneGeometry(W, riverW), M.water);
  water.rotation.x = -Math.PI / 2;
  water.position.set(0, -0.55, riverZ);
  water.name = 'river-water';
  group.add(water);
  // bank lips along both edges
  box(M.bank, W, 0.5, 1.6, 0, 0.25, riverZ - riverW / 2 - 0.8);
  box(M.bank, W, 0.5, 1.6, 0, 0.25, riverZ + riverW / 2 + 0.8);

  for (const i of BRIDGE_STREETS) {
    const bx = streetX(i);
    const bg = new THREE.Group();
    bg.name = 'bridge-' + i;
    // deck: three slabs rising gently to mid-span
    const seg = riverW / 3 + 4;
    const d1 = box(M.deck, STREET - 2, 0.5, seg, 0, 0.55, -seg, bg); d1.rotation.x = 0.10;
    box(M.deck, STREET - 2, 0.5, seg, 0, 0.95, 0, bg);
    const d3 = box(M.deck, STREET - 2, 0.5, seg, 0, 0.55, seg, bg); d3.rotation.x = -0.10;
    // rails
    for (const s of [-1, 1]) {
      box(M.pole, 0.18, 0.9, riverW + 6, s * (STREET / 2 - 1.2), 1.45, 0, bg);
    }
    // stop lights where br