<!-- SOLID CITY · part 2055 -->
 red \u2014 it is not a game. Pass through and it answers with light.' });
  if (H.regrid) H.regrid();
}
function watchStop() {
  var W = window.CityWatch, r = W && W.routes && W.routes.grand; if (!r || r._eighthgate) return;
  var orig = r.build; r._eighthgate = true;
  r.build = function () {
    var w = orig.apply(this, arguments), at = Math.min(2, w.length);
    w.splice(at, 0, { x: POS.x - 60, z: POS.z + 60, y: Y0 + 80, v: 120, cap: { cap: 'THE EIGHTH GATE', sub: 'The missing note in the chord \u00b7 southwest of the hangar', say: 'The eighth gate: seven gates ring the hub, and this one \u2014 gold, not red \u2014 closes the ring.' }, agl: false });
    return w;
  };
}
function load() {
  M.state = 'loading';
  var p; try { p = import('/city/gate-eighth.js?v=20261011d'); } catch (e) { p = Promise.reject(e); }
  p.then(function (mod) {
    var make = mod.createEighthGate || mod.default;
    var g = make({ THREE: A.T, position: { x: POS.x, y: Y0, z: POS.z }, rotY: ROTY });
    A.world.add(g); M.group = g; M.state = 'ready';
    collide(g.userData || {}); hyperStop(); watchStop();
  }).catch(function (e) { M.state = 'failed'; try { console.warn('[city] Eighth gate skipped:', e && e.message); } catch (x) {} });
}
function update(dt) {
  if (M.state === 'idle') { if (A.isBuilt && !A.isBuilt()) return; M.wait -= dt; if (M.wait <= 0) load(); return; }
  if (M.state !== 'ready' || document.hidden) return;
  var c = A.camera.position, d = Math.hypot(c.x + A.origin.x - POS.x, c.z + A.origin.z - POS.z);
  M.group.visible = d < 6000;
  if (d < 1500 && M.group.userData.update) try { M.group.userData.update(S.t, S); } catch (e) {}
  if (S.mode === 'walk') {
    var pd = Math.hypot(S.p.x - POS.x, S.p.z - POS.z);
    if (pd < 30 && !M.said) { M.said = true; if (window.CityHyper && CityHyper.narrate) CityHyper.narrate('<b>The Eighth Gate</b>: the seven game-gates leave one direction open \u2014 southwest \u2014 and this gate closes the ring.', 8); }
    else if (pd > 120) M.said = false;
  }
}
function radar(g, blip, fl) { if (M.state === 'ready') blip(POS.x, POS.z, '#d8aa48', fl ? 3 : 5, fl ? '' : 'VIII'); }
window.CityEighthGate = { update: update, radar: radar, pos: POS, state: function () { return M.state; } };
})();


===== FILE: public/city/gate-eighth.js =====
/* SOLID CITY — THE EIGHTH GATE (2026-10-11, Royal Decree city expansion)
 * The seven game-gates ring the hangar hub in seven directions. The eighth direction —
 * southwest — stood open. This gate closes the ring: the missing note in the chord.
 * It speaks the gates' own language (basalt posts, gold lintel, ring, disc) but in
 * eights: octagonal ring, octagonal disc, eight-sided inlay. Gold, not red — it is
 * not a game. Pass through the disc and it answers with light.
 * True name: THE EIGHTH GATE. Seam: WHERE THE SEVEN END.
 * Self-contained ES module. Local frame: gate faces +Z.
 */
function textPlate(T, text, w) {
  var c = document.createElement('canvas'); c.width = 512; c.height = 128;
  var g = c.getContext('2d');
  g.fillStyle = '#17141a'; g.fillRect(0, 0, 512, 128);
  g.strokeStyle = '#8a6a30'; g.lineWidth = 4; g.strokeRect(8, 8, 496, 112);
  g.fillStyle = '#d8c690'; g.textAlign = 'center'; g.textBaseline = 'middle';
  g.font = 'bold 44px Georgia, serif'; g.fillText(text, 256, 66);
  var t = new T.CanvasTexture(c); t.encoding = T.sRGBEncoding; t.anisotropy = 4; return t;
}
function quietMarker(T, text) {
  // one quiet name-marker: a low dark stone, text tilted toward the reader. Nothing more.
  var grp = new T.Group();
  var stone = new T.Mesh(new T.BoxGeometry(3.4, 0.5, 1.7),
    new T.MeshLambertMaterial({ color: 0x1e1b22 }));
  stone.position.y = 0.55; stone.rotation.x = -0.5; grp.add(stone);
  /* THE BATTENING (2026-10-14): the face was 3.1 x 0.78 at
   * (0, 0.72, 0.42) — measured inside the stone: its plane sat
   * 0.45 along the face normal against the stone's 0.85 front,
   * so the middle of every name was entombed and only slivers
   * read. Now a fitted plaque in the plate's own aspect,
   * PROUD of the front face (0.87 along the normal, sampler-
   * verified: 0 of 7 points inside the stone). */
  var face = new T.Mesh(new T.PlaneGeometry(1.92, 0.48),
    new T.MeshBasicMaterial({ map: textPlate(T, text), transparent: false }));
  face.position.set(0, 0.984, 0.754); face.rotation.x = -0.5; grp.add(face);
  var base = new T.Mesh(new T.BoxGeometry(3.6, 0.25, 1.9),
    new T.MeshLambertMaterial({ color: 0x141218 }));
  base.position.y = 0.12; grp.add(base);
  return grp;
}
export function createEighthGate(o) {
  var T = o.THREE, P = o.position || { x: 0, y: 0, z: 0 };
  var root = new T.Group(); root.name = 'eighth-gate'; root.position.set(P.x, P.y, P.z);
  if (o.rotY) root.rotation.y = o.rotY;
  var foot = [];
  var basalt = new T.MeshLambertMaterial({ color: 0x1a181e });
  var gold = new T.MeshLambertMaterial({ color: 0xd8aa48, emissive: 0x2a1a04 });
  var goldGlow = new T.MeshBasicMaterial({ co