<!-- SOLID CITY · part 2108 -->
 Math.max(w,d)*1.04, lift, h+lift, 0]);
  } else if (tp === 2) {
    var seg = h/5;
    for (var k = 0; k < 5; k++) {
      var sw = w*(1-k*0.09), sd = d*(1-k*0.09);
      var sgm = new T.Mesh(new T.BoxGeometry(sw, seg, sd), mat);
      sgm.position.y = seg*(k+0.5); sgm.rotation.y = k*0.16; g.add(sgm);
    }
    hw = w*0.6;
    foot.push([x, z, w*1.2, w*1.2, lift, h+lift, 0]);
  } else {
    var bd = new T.Mesh(new T.BoxGeometry(w, h*0.82, d), mat); bd.position.y = h*0.41; g.add(bd);
    var ring = new T.Mesh(new T.TorusGeometry(Math.max(w,d)*0.42, 1.6, 8, 24),
      new T.MeshBasicMaterial({color: 0xd8aa48}));
    ring.rotation.x = Math.PI/2; ring.position.y = h*0.86; g.add(ring);
    hw = Math.max(w,d)*0.52;
    foot.push([x, z, Math.max(w,d)*1.04, Math.max(w,d)*1.04, lift, h+lift, 0]);
  }
  root.add(g);
  // portholes on the new tower
  if (!window._hyperPortholes) window._hyperPortholes = [];
  var pc = document.createElement('canvas'); pc.width = pc.height = 128;
  var px = pc.getContext('2d');
  var pgr = px.createRadialGradient(64,64,4,64,64,64);
  pgr.addColorStop(0,'#fff8e8'); pgr.addColorStop(0.25,'#f2c86a');
  pgr.addColorStop(0.5,'#9a6ae8'); pgr.addColorStop(0.75,'#4a6ae8'); pgr.addColorStop(1,'#1a1a3a');
  px.fillStyle = pgr; px.beginPath(); px.arc(64,64,64,0,7); px.fill();
  px.strokeStyle = '#f2c86a'; px.lineWidth = 3;
  for (var pr = 12; pr < 64; pr += 13) { px.beginPath(); px.arc(64,64,pr,0,7); px.stroke(); }
  var ptex = new T.CanvasTexture(pc); ptex.encoding = T.sRGBEncoding;
  var pmat = new T.MeshBasicMaterial({ map: ptex, transparent: true, side: T.DoubleSide });
  for (var py = 50; py < h; py += 36) {
    for (var f = 0; f < 4; f++) {
      var ang = f*Math.PI/2 + 0.3;
      var hx = x + Math.cos(ang)*hw, hz = z + Math.sin(ang)*hw;
      var hole = new T.Mesh(new T.CircleGeometry(2.8, 20), pmat);
      hole.position.set(hx, py+lift, hz);
      hole.lookAt(hx*2-x, py+lift, hz*2-z);
      root.add(hole);
      window._hyperPortholes.push(hole);
    }
  }
  return {x:x, z:z, h:h, g:g};
}

function buildWesternExpansion(T, root, foot, o) {
  var R = mulberry(777);
  var M = {
    glassA: o.glassA, glassB: o.glassB, gold: o.gold, steel: o.steel,
    textPlate: o.textPlate
  };

  // ---- west podium extension ----
  var podM = new T.MeshLambertMaterial({color: 0x2c313a});
  var podW = new T.Mesh(new T.BoxGeometry(260, 10, 380), podM);
  podW.position.set(-340, -5, 0); root.add(podW);
  var rimW = new T.Mesh(new T.BoxGeometry(264, 1.2, 384), M.steel);
  rimW.position.set(-340, 0.1, 0); root.add(rimW);
  foot.push([-340, 0, 264, 384, -10, 0.5, 0]);

  // ---- 12 western megatowers: a wall on the horizon ----
  var cols = [-400, -340, -280], rows = [-120, -40, 40, 120];
  var types = [0, 1, 2, 3], ti = 0;
  cols.forEach(function(cx) {
    rows.forEach(function(cz) {
      var x = cx + (R()-0.5)*18, z = cz + (R()-0.5)*18;
      var h = 250 + R()*150; // 250-400m, tall for the wall effect
      // boost center columns
      if (cx === -340) h = Math.min(400, h + 40);
      westTower(T, root, foot, M, x, z, h, types[ti % 4], R, 24);
      buildCompactPodium(T, root, foot, o.elevators, o.standFloors, {
        R:R, glassA:o.glassA, gold:o.gold, steel:o.steel, textPlate:o.textPlate,
        x:x, z:z, w:36, d:36, h:24, name:'WEST TOWER '+(ti+1)
      });
      if (o.p2gates) o.p2gates.push({x:x+24, z:z, y:0, name:'WEST TOWER '+(ti+1)});
      ti++;
    });
  });

  // ---- 6 BUSINESSES: the working city ----
  // (fit functions defined below; uses buildStore)
  var biz = [
    {x:-230, z:-100, ry:Math.PI/2, name:'THE MERIDIAN BANK', fit:bankFit},
    {x:-230, z:-60, ry:Math.PI/2, name:'PACIFIC RIM SHIPPING', fit:shipFit},
    {x:-230, z:-20, ry:Math.PI/2, name:'HALE & ASSOCIATES', fit:lawFit},
    {x:-230, z:20, ry:Math.PI/2, name:'THE COPPER ANCHOR', fit:restFit},
    {x:-230, z:60, ry:Math.PI/2, name:'THE DAILY MERIDIAN', fit:newsFit},
    {x:-230, z:100, ry:Math.PI/2, name:'ATELIER KAI', fit:archFit}
  ];
  // business materials (reuse retail material factory via a light shim)
  // buildStore needs M with wall/floor/ceil/glass/awn/strip/shelf/counter/etc + sign()
  // We'll construct a minimal M here.
  var BM = makeBizMaterials(T, o);
  biz.forEach(function(b) { buildStore(T, root, foot, BM, b); });

  // ---- HARBOR ----
  buildHarbor(T, root, foot, o, BM);
}

function makeBizMaterials(T, o) {
  return {
    gold: o.gold,
    wall: new T.MeshLambertMaterial({color: 0x2e3238}),
    floor: new T.MeshLambertMaterial({color: 0x3d4149}),
    ceil: new T.MeshLambertMaterial({color: 0x22262c}),
    glass: new T.MeshLambertMaterial({color: 0x9fc4e8, transparent: true, opacity: 0.4}),
    awn: new T.MeshLambertMaterial({color: 0x2a4a6a}),
    strip: new T.MeshBasicMaterial({color: 0xffe8c0}),
    shelf: new T.MeshLambertMaterial({color: 0x5a4028}),
    counter: new T.MeshLambertMaterial({color: 0x3a3f47}),
    register: new T.MeshLambertMaterial({color: 0x1a1c20}),
   