<!-- SOLID CITY · part 2056 -->
lor: 0xffd880 });
  // ground inlay: octagonal ring, the eight claimed in stone
  var inlay = new T.Mesh(new T.RingGeometry(7.6, 9.0, 8),
    new T.MeshBasicMaterial({ color: 0xd8aa48, transparent: true, opacity: 0.55, side: T.DoubleSide }));
  inlay.rotation.x = -Math.PI / 2; inlay.rotation.z = Math.PI / 8; inlay.position.y = 0.06; root.add(inlay);
  // posts
  [-1, 1].forEach(function (s) {
    var post = new T.Mesh(new T.BoxGeometry(1.6, 12, 1.6), basalt);
    post.position.set(5 * s, 6, 0); root.add(post);
    var footBlock = new T.Mesh(new T.BoxGeometry(2.6, 1.0, 2.6), basalt);
    footBlock.position.set(5 * s, 0.5, 0); root.add(footBlock);
    var cap = new T.Mesh(new T.OctahedronGeometry(0.9), gold);
    cap.position.set(5 * s, 12.7, 0); root.add(cap);
    foot.push([5 * s, 0, 2.6, 2.6, 0, 13.5, 0]);
  });
  // lintel
  var lintel = new T.Mesh(new T.BoxGeometry(12.6, 1.6, 2.0), gold);
  lintel.position.y = 12.9; root.add(lintel);
  // the octagonal ring: eight sides for the eighth gate
  var ring = new T.Mesh(new T.TorusGeometry(3.8, 0.28, 8, 8), goldGlow);
  ring.position.y = 6.4; ring.rotation.z = Math.PI / 8; root.add(ring);
  var disc = new T.Mesh(new T.CircleGeometry(3.55, 8),
    new T.MeshBasicMaterial({ color: 0xffd880, transparent: true, opacity: 0.16, side: T.DoubleSide, depthWrite: false }));
  disc.position.y = 6.4; disc.rotation.z = Math.PI / 8; root.add(disc); disc.name = 'gate-disc';
  var halo = new T.PointLight(0xffd880, 0.9, 30); halo.position.set(0, 6.4, 0); root.add(halo);
  // true name + seam name: quiet markers, nothing more
  var nameMark = quietMarker(T, 'THE EIGHTH GATE');
  nameMark.position.set(8.2, 0, 2.5); nameMark.rotation.y = -0.5; root.add(nameMark);
  var seamMark = quietMarker(T, 'WHERE THE SEVEN END');
  seamMark.position.set(0, 0, 12.5); root.add(seamMark);
  // THE BATTENING (2026-10-14): the markers stood unprotected — no
  // footprint under either. A sign a walker can pass through is a
  // sign half-read; both stones now stand in the collision law.
  foot.push([8.2, 2.5, 3.6, 1.9, 0, 0.9, -0.5]);
  foot.push([0, 12.5, 3.6, 1.9, 0, 0.9, 0]);
  var pulse = 0;
  root.userData = {
    footprint: foot, center: { x: 0, z: 0 }, radius: 16, height: 16,
    update: function (t, S) {
      disc.material.opacity = 0.13 + 0.07 * Math.sin(t * 2.2);
      ring.rotation.z = Math.PI / 8 + Math.sin(t * 0.4) * 0.02;
      if (pulse > 0) {
        pulse -= 0.03;
        disc.material.opacity = 0.13 + pulse * 0.9;
        halo.intensity = 0.9 + pulse * 6;
      } else { halo.intensity = 0.9; }
      // passing through the disc: the gate answers with light
      if (S && S.mode === 'walk' && S.p && !this._cool) {
        var dx = S.p.x - P.x, dz = S.p.z - P.z;
        // gate-local frame: +Z is the facing direction (sin/cos of rotY)
        var ry = o.rotY || 0, fx = Math.sin(ry), fz = Math.cos(ry);
        var lz = dx * fx + dz * fz;        // along facing
        var lx = dx * fz - dz * fx;        // lateral
        if (Math.abs(lx) < 2.6 && Math.abs(lz) < 1.6) {
          this._cool = 8; pulse = 1;
        }
      }
      if (this._cool > 0) this._cool -= 0.016;
    }
  };
  return root;
}
export default createEighthGate;


===== FILE: public/city/gate-eighth.js.bak-batten-20261014 =====
/* 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);
  var face = new T.Mesh(new T.PlaneGeometry(3.1, 0.78),
    new T.MeshBasicMaterial({ map: textPlate(T, text), transparent: false }));
  face.position.set(0, 0.72, 0.42); face.rotation.x = -0.5; grp.add(face);
  var base = new T.Mesh(new T.BoxGeometry(3.6