<!-- SOLID CITY · part 358 -->
 STRIKE HANGAR', x: -540, z: 250, url: '/city/double-strike/', label: 'Double Strike', title: 'DOUBLE STRIKE \u2014 a SOLIDIVISM game', go: 'LAUNCH DOUBLE STRIKE', text: 'Cooperative AI combat: you fly gold, the AI flies teal. Survive together. Return together. (Opens the full game; your city session is kept.)' },
};
var portals = [];
function portal(dist, x, z, rotY) {
  var gY = CITY_Y, g = new T.Group(); g.position.set(x, gY, z); g.rotation.y = rotY || 0; world.add(g);
  var lp = new T.Mesh(new T.BoxGeometry(1.4, 9, 1.4), M.black); lp.position.set(-4, 4.5, 0); g.add(lp);
  var rp = lp.clone(); rp.position.x = 4; g.add(rp);
  var top = new T.Mesh(new T.BoxGeometry(10, 1.2, 1.6), M.gold); top.position.y = 9.4; g.add(top);
  var ring = new T.Mesh(new T.TorusGeometry(3.1, 0.18, 8, 40), M.red); ring.position.y = 4.6; g.add(ring);
  var disc = new T.Mesh(new T.CircleGeometry(3, 32), curve(new T.MeshBasicMaterial({ color: 0xff2a40, transparent: true, opacity: 0.22, side: T.DoubleSide, depthWrite: false }))); disc.position.y = 4.6; g.add(disc);
  var c = Math.cos(rotY || 0), s = Math.sin(rotY || 0);
  solidBox(x - 4 * c, z + 4 * s, 1.6, 1.6, gY, gY + 9); solidBox(x + 4 * c, z - 4 * s, 1.6, 1.6, gY, gY + 9);
  animated.push(function (t) { ring.rotation.z = t * 0.8; disc.material.opacity = 0.16 + 0.1 * Math.sin(t * 3); });
  portals.push({ d: dist, x: x, z: z });
  var sg = sign(dist.label.toUpperCase(), 'WALK THROUGH →', 0, 12.6, 0, 10); world.remove(sg); g.add(sg);
}

function buildCity() {
  var Y = CITY_Y;
  // city base: a dark disc so the capital sits on solid ground
  var base = new T.Mesh(new T.CircleGeometry(800, 64), M.road); base.rotation.x = -Math.PI / 2; base.position.set(0, Y + 0.02, 0); world.add(base);
  // roads (red-lit) from plaza to districts — dashes instanced (one draw call)
  var dashes = new T.InstancedMesh(new T.PlaneGeometry(0.5, 7), M.red, 200), dashO = new T.Object3D(), nd = 0;
  [D.studio, D.port, { x: 0, z: -700 }, { x: -700, z: -80 }].forEach(function (t) {
    var L = Math.hypot(t.x, t.z), a = Math.atan2(t.x, t.z);
    var road = new T.Mesh(new T.PlaneGeometry(14, L), curve(new T.MeshLambertMaterial({ color: 0x151216 }))); road.rotation.x = -Math.PI / 2; road.rotation.z = a; road.position.set(t.x / 2, Y + 0.06, t.z / 2); world.add(road);
    for (var k = 30; k < L - 10; k += 26) { dashO.position.set(Math.sin(a) * k, Y + 0.1, Math.cos(a) * k); dashO.rotation.set(-Math.PI / 2, 0, a); dashO.updateMatrix(); dashes.setMatrixAt(nd++, dashO.matrix); }
  });
  dashes.count = nd; dashes.instanceMatrix.needsUpdate = true; world.add(dashes);
  // ---- FORUM PLAZA ----
  var floor = new T.Mesh(new T.CircleGeometry(110, 64), M.board); floor.rotation.x = -Math.PI / 2; floor.position.set(0, Y + 0.12, 0); world.add(floor);
  var rim = new T.Mesh(new T.RingGeometry(108, 111, 64), M.glowGold); rim.rotation.x = -Math.PI / 2; rim.position.set(0, Y + 0.15, 0); world.add(rim);
  // open pavilions (Solidivism: no walls — posts + canopy)
  for (var p = 0; p < 6; p++) {
    var a = p / 6 * Math.PI * 2 + 0.26, px = Math.cos(a) * 72, pz = Math.sin(a) * 72;
    for (var q = 0; q < 4; q++) box(0.9, 9, 0.9, M.black, px + (q & 1 ? 6 : -6), Y, pz + (q & 2 ? 6 : -6));
    box(15, 0.7, 15, M.gold, px, Y + 9, pz, false);
    box(15.6, 0.25, 15.6, M.redDim, px, Y + 9.7, pz, false);
  }
  // central monument: the crest obelisk
  box(4, 3, 4, M.gold, 0, Y, 22);
  var ob = cyl(0.2, 1.6, 26, M.black, 0, Y + 3, 22, 4); ob.rotation.y = Math.PI / 4;
  var tip = new T.Mesh(new T.OctahedronGeometry(1.4), M.red); tip.position.set(0, Y + 30.5, 22); world.add(tip);
  animated.push(function (t) { tip.rotation.y = t; tip.position.y = Y + 30.5 + Math.sin(t * 1.4) * 0.4; });
  sign('FORUM PLAZA', 'SOLID CITY · EST. 2026', 0, Y + 16, -60, 26);
  portal(D.plaza, 0, -88, 0);
  // ---- MUSIC STUDIO TOWER ----
  var S = D.studio;
  cyl(26, 32, 6, M.gold, S.x, Y, S.z, 8);
  var tower = cyl(16, 22, 230, curve(new T.MeshLambertMaterial({ map: TEX_BLDG, emissiveMap: TEX_WIN, emissive: 0xffffff, color: 0xffffff })), S.x, Y + 6, S.z, 8);
  tower.material.map = TEX_BLDG.clone(); tower.material.map.needsUpdate = true; tower.material.map.repeat.set(4, 6); tower.material.map.wrapS = tower.material.map.wrapT = T.RepeatWrapping;
  tower.material.emissiveMap = TEX_WIN.clone(); tower.material.emissiveMap.needsUpdate = true; tower.material.emissiveMap.repeat.set(4, 6); tower.material.emissiveMap.wrapS = tower.material.emissiveMap.wrapT = T.RepeatWrapping;
  var rings = [];
  for (var k = 0; k < 7; k++) { var rg = new T.Mesh(new T.TorusGeometry(21 - k * 0.7, 0.6, 6, 48), k % 2 ? M.glowGold : M.red); rg.rotation.x = Math.PI / 2; rg.position.set(S.x, Y + 30 + k * 30, S.z); world.add(rg); rings.push(rg); }
  animated.push(function (t) { var amp = NP.playing ? 0.09 : 0.02, sp = NP.playing ? 6 : 1.5; for (var i = 0; i < rings.length; i++) { var s = 1 + amp * Math.sin(t * sp - i * 0.9); rings[i].scale.set(s, s, 1); } for (var m