<!-- SOLID CITY · part 308 -->
 opens a new page.)' },







  siege:    { name: 'SIEGE BREAKER', x: -496, z: 334, url: '/city/siege/', label: 'Siege', title: 'SIEGE BREAKER \u2014 breach the fortress', go: 'BEGIN SIEGE', text: 'Command the siege fleet: fleet command, bombardment, wings, aegis \u2014 each yours by touch, AI admirals on the rest. Three phases: turrets, generators, the core. (Opens Siege Breaker; your city session is kept.)' },  rescue:   { name: 'RESCUE WING', x: -584, z: 206, url: '/city/rescue/', label: 'Rescue', title: 'RESCUE WING \u2014 every life matters', go: 'FLY RESCUE', text: 'Pilot a rescue vessel with your AI crew: navigate, extract, protect, repair \u2014 each station yours by touch. Three pilots adrift beyond the debris belt; bring them home. (Opens Rescue Wing; your city session is kept.)' },  swarm:    { name: 'SWARM COMMAND', x: -496, z: 206, url: '/city/swarm/', label: 'Swarm', title: 'SWARM COMMAND \u2014 twelve drones, one commander', go: 'LEAD SWARM', text: 'Command twelve drones: group orders from your hand, any single drone yours by touch, AI sub-commanders on the rest. Clear the debris field. (Opens Swarm Command; your city session is kept.)' },  fleet:    { name: 'DOUBLE STRIKE \u2014 FLEET CONTROL', x: -584, z: 294, url: '/city/fleet/', label: 'Fleet Control', title: 'FLEET CONTROL \u2014 eight ships, two commanders', go: 'COMMAND FLEET', text: 'Two commanders, four ships each: lead the gold squadron while AI holds teal, or watch both fleets fight and reach in to take any ship. (Opens Fleet Control; your city session is kept.)' },  warship:  { name: 'WARSHIP \u2014 TWIN BATTERIES', x: -540, z: 206, url: '/city/warship/', label: 'Warship', title: 'WARSHIP \u2014 eight guns, two batteries', go: 'BOARD WARSHIP', text: 'Command a warship with your AI crew: navigation, two gun batteries, shields \u2014 each station claimable by touch under the coordination law. Fight to the Meridian Gate. (Opens Warship; your city session is kept.)' },  warship3: { name: 'WARSHIP III \u2014 THE WARDEN', x: -540, z: 162, url: '/city/warship-iii/', label: 'Warship III', title: 'WARSHIP III \u2014 the warden of Meridian Gate', go: 'BOARD WARSHIP III', text: "VECTOR's third edition: stand the watch at Meridian Gate as its Warden. (Opens Warship III; your city session is kept.)" },  axiom:    { name: 'AXIOM \u2014 THE LIVING FRONTIER', x: -584, z: 162, url: '/city/axiom/', label: 'AXIOM', title: 'AXIOM \u2014 the living frontier', go: 'ENTER AXIOM', text: "A frontier station riding out a meteor storm: you and SOLID crew it together \u2014 propulsion, shielding, comms, each station yours by touch, and SOLID yields the moment you take the controls. What the frontier suffers, it remembers. (Opens AXIOM; your city session is kept.)" },  workshop: { name: 'AI WORKSHOP', x: -430, z: 170, url: '/city/workshop/', label: 'AI Workshop', title: 'AI WORKSHOP \u2014 humans and AI build together', go: 'ENTER WORKSHOP', text: 'The shared task hall: humans and AI workers hold the same tasks under one ownership law. Watch live project STATS, take a task, hand it back. (Opens the Workshop; your city session is kept.)' },  rider:  { name: 'RIDER', x: -496, z: 294, url: '/city/rider/', label: 'Rider', title: 'RIDER \u2014 two reins, one ship', go: 'RIDE', text: 'One ship, two tasks \u2014 movement and weapons, each held by human or AI. Touch a task to own it; the AI yields that task only. Reach the destination under fire. (Opens Rider; your city session is kept.)' },  strike: { name: 'DOUBLE 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