<!-- SOLID CITY · part 3258 -->
undAt;
  var fx = GATE.foot[0], fz = GATE.foot[1];
  var dx = GATE.corner[0] - fx, dz = GATE.corner[1] - fz;
  var LEN = Math.hypot(dx, dz), ux = dx / LEN, uz = dz / LEN;
  var px = -uz, pz = ux;                       /* the across direction */
  var yaw = Math.atan2(ux, uz);
  function topAt(s) {
    var P = GATE.profile;
    if (s <= P[0][0]) return P[0][1];
    for (var i = 0; i < P.length - 1; i++) {
      if (s <= P[i + 1][0]) { var t = (s - P[i][0]) / (P[i + 1][0] - P[i][0]); return P[i][1] + (P[i + 1][1] - P[i][1]) * t; }
    }
    return P[P.length - 1][1];
  }
  function pointAt(s, across) { return [fx + ux * s + px * across, fz + uz * s + pz * across]; }

  var M = {
    stone: new T.MeshStandardMaterial({ color: 0x8f96a3, roughness: 0.62, metalness: 0.25 }),
    dark: new T.MeshStandardMaterial({ color: 0x232c3d, roughness: 0.45, metalness: 0.6 }),
    gold: new T.MeshStandardMaterial({ color: 0xd8a833, roughness: 0.3, metalness: 0.85, emissive: 0x332200 }),
    lamp: new T.MeshStandardMaterial({ color: 0xffe9b0, emissive: 0xffca6a, emissiveIntensity: 1.6, roughness: 0.4 })
  };
  var group = new T.Group(); group.name = 'THE SOUTH GATE';
  var deckTris = [];
  var vA = new T.Vector3(), vB = new T.Vector3(), vC = new T.Vector3();

  function pitchedSlab(s0, s1, across, width, thick, dropBelow, mat, name) {
    var mid = (s0 + s1) / 2, run = (s1 - s0) + 0.07;
    var rx = -Math.atan2(topAt(s1) - topAt(s0), s1 - s0);   /* PITCH TRUTH: atan2, as everywhere now */
    var c = pointAt(mid, across), cy = (topAt(s0) + topAt(s1)) / 2 - dropBelow - thick / 2;
    var g = new T.BoxGeometry(width, thick, run);
    var e = new T.Euler(rx, yaw, 0, 'YXZ');
    var m = new T.Matrix4().makeRotationFromEuler(e); m.setPosition(c[0], cy, c[1]);
    g.applyMatrix4(m);
    var mesh = new T.Mesh(g, mat); mesh.name = name || 'GATE_SLAB';
    group.add(mesh);
    return g;
  }
  function collectTop(geo) {
    var pos = geo.attributes.position, idx = geo.index;
    var n = idx ? idx.count : pos.count;
    function v(i) { return new T.Vector3().fromBufferAttribute(pos, idx ? idx.getX(i) : i); }
    for (var i = 0; i + 2 < n; i += 3) {
      var a = v(i), b = v(i + 1), c = v(i + 2);
      var cr = b.clone().sub(a).cross(c.clone().sub(a));
      if (cr.y > 0.5 * cr.length()) deckTris.push([a, b, c]);
    }
  }

  /* the deck: profile spans, sub-split for the pitch to stay honest */
  var spans = [[0, 2.2, GATE.padWidth], [2.2, 9.6, GATE.width], [9.6, 11.4, GATE.width], [11.4, 18.87, GATE.width]];
  spans.forEach(function (sp) {
    var n = Math.max(1, Math.ceil((sp[1] - sp[0]) / 1.9));
    for (var j = 0; j < n; j++) {
      var s0 = sp[0] + (sp[1] - sp[0]) * (j / n), s1 = sp[0] + (sp[1] - sp[0]) * ((j + 1) / n);
      collectTop(pitchedSlab(s0, s1, 0, sp[2], 0.22, 0, M.stone, 'GATE_DECK'));
      /* gold nosing on each span's downhill edge */
      pitchedSlab(s0, s0 + 0.09, 0, sp[2], 0.05, -0.025, M.gold, 'GATE_NOSING');
    }
    /* stringers under both edges of the span */
    [-1.55, 1.55].forEach(function (ac) { pitchedSlab(sp[0], sp[1], ac, 0.24, 0.3, 0.22, M.dark, 'GATE_STRINGER'); });
  });

  /* posts down to the true ground */
  [2.2, 6.0, 9.6, 11.4, 15.0, 18.3].forEach(function (s) {
    [-1.45, 1.45].forEach(function (ac) {
      var c = pointAt(s, ac), g = groundAt(c[0], c[1]), top = topAt(s) - 0.2;
      var h = Math.max(0.3, top - (g - 0.4));
      var geo = new T.CylinderGeometry(0.14, 0.18, h, 8);
      var mesh = new T.Mesh(geo, M.dark); mesh.position.set(c[0], g - 0.4 + h / 2, c[1]); mesh.name = 'GATE_POST';
      group.add(mesh);
      var capG = new T.BoxGeometry(0.44, 0.1, 0.44), cap = new T.Mesh(capG, M.gold);
      cap.position.set(c[0], top + 0.03, c[1]); cap.name = 'GATE_POST_CAP'; group.add(cap);
    });
  });

  /* the balustrade: uprights, a gold top rail, a silver mid rail — pitched with the deck */
  var railS = [0.6, 2.2, 4.1, 6.0, 7.9, 9.6, 11.4, 13.3, 15.2, 17.1, 18.6];
  [-1.68, 1.68].forEach(function (ac) {
    for (var i = 0; i < railS.length; i++) {
      var c = pointAt(railS[i], ac), g2 = new T.BoxGeometry(0.07, 1.0, 0.07);
      var up = new T.Mesh(g2, M.dark); up.position.set(c[0], topAt(railS[i]) + 0.5, c[1]); up.name = 'GATE_RAIL_POST'; group.add(up);
    }
    for (var j = 0; j < railS.length - 1; j++) {
      [1.0, 0.55].forEach(function (hgt, k) {
        var s0 = railS[j], s1 = railS[j + 1], mid = (s0 + s1) / 2;
        var rx = -Math.atan2(topAt(s1) - topAt(s0), s1 - s0);
        var cc = pointAt(mid, ac);
        var g3 = new T.BoxGeometry(0.06, 0.06, (s1 - s0) + 0.05);
        var e = new T.Euler(rx, yaw, 0, 'YXZ'), m = new T.Matrix4().makeRotationFromEuler(e);
        m.setPosition(cc[0], (topAt(s0) + topAt(s1)) / 2 + hgt, cc[1]); g3.applyMatrix4(m);
        var rail = new T.Mesh(g3, k === 0 ? M.gold : M.stone); rail.name = 'GATE_RAIL'; group.add(rail);
      });
    }
  });

  /* lamps at the landing */
  [9.6, 11.4].forEach(function (s) {