<!-- SOLID CITY · part 3109 -->
w T.MeshLambertMaterial({ vertexColors: true, transparent: true, opacity: 0.6 })));
  /* THE RED LINE — Stage 4's wayfinding pulled forward
   * by the King's elevation word: a tactical red inlay
   * runs the gallery's spine and rings both plazas, so
   * the journey reads itself — follow the red line from
   * city to city. All its pieces merge into ONE mesh and
   * ONE draw, under one pulsing material. */
  (function buildRedLine() {
    function mergeGeos(list) {
      var pos = [], nor = [], uv = [], idx = [], off = 0, v = new T.Vector3(), nm = new T.Matrix3();
      list.forEach(function (it) {
        var g = it.geo, p = g.attributes.position, n = g.attributes.normal, u = g.attributes.uv, ix = g.index;
        nm.getNormalMatrix(it.mtx);
        for (var i = 0; i < p.count; i++) { v.fromBufferAttribute(p, i).applyMatrix4(it.mtx); pos.push(v.x, v.y, v.z); v.fromBufferAttribute(n, i).applyMatrix3(nm).normalize(); nor.push(v.x, v.y, v.z); uv.push(u ? u.getX(i) : 0, u ? u.getY(i) : 0); }
        for (var j = 0; j < ix.count; j++) idx.push(ix.getX(j) + off);
        off += p.count;
      });
      var geo = new T.BufferGeometry();
      geo.setAttribute('position', new T.Float32BufferAttribute(pos, 3));
      geo.setAttribute('normal', new T.Float32BufferAttribute(nor, 3));
      geo.setAttribute('uv', new T.Float32BufferAttribute(uv, 2));
      geo.setIndex(idx); return geo;
    }
    var parts = [], M = new T.Matrix4(), E = new T.Euler(), Q = new T.Quaternion(), ONE = new T.Vector3(1, 1, 1), PV = new T.Vector3();
    function put(geo, x, y, z, ry) { E.set(0, ry || 0, 0); Q.setFromEuler(E); PV.set(x, y, z); M.compose(PV, Q, ONE); parts.push({ geo: geo, mtx: M.clone() }); }
    for (var z = 1402; z <= 1542; z += 4.5) put(new T.BoxGeometry(0.55, 0.05, 4.4), GAL.x, galleryFloorY(z) + 0.055, z, 0);
    [A_C, B_C].forEach(function (Cc) {
      var ring = new T.TorusGeometry(60, 0.17, 6, 72); ring.rotateX(Math.PI / 2);
      put(ring, Cc.x, Cc.f + 0.31, Cc.z, 0);
      for (var k = 0; k < 4; k++) { var a = k * Math.PI / 2 + Math.PI / 4; put(new T.BoxGeometry(46, 0.05, 0.55), Cc.x + Math.cos(a) * 35, Cc.f + 0.31, Cc.z + Math.sin(a) * 35, -a); }
    });
    redlineMat = new T.MeshLambertMaterial({ color: 0x5a0d14, emissive: 0xd42434, emissiveIntensity: 0.8 });
    var mesh = new T.Mesh(mergeGeos(parts), redlineMat);
    mesh.frustumCulled = false; mesh.renderOrder = 1; mesh.name = 'the-red-line'; group.add(mesh);
    heroInfo.redline = parts.length;
  })();
  buildGlow();
  group.userData = {
    footprints: footprints, layers: layers,
    centers: { a: A_C, b: B_C }, gallery: GAL,
    texTier: opts.texTier || null,
    molded: molded,
    practice: practiceInfo,
    glow: glowInfo,
    hero: heroInfo,
    counts: { footprints: footprints.length, layers: layers.length },
    update: function (dt, t) {
      t = t || 0;
      crystals.forEach(function (c, i) { c.rotation.y = t * (0.14 + i * 0.03); });
      if (crystals[0] && crystals[0].morphTargetInfluences) crystals[0].morphTargetInfluences[0] = 0.16 + 0.13 * Math.sin(t * 0.55);
      if (redlineMat) redlineMat.emissiveIntensity = 0.72 + 0.28 * Math.sin(t * 1.25);
      if (glowMat) glowMat.color.setScalar(0.86 + 0.14 * Math.sin(t * 0.9));
      for (var gi = 0; gi < haloMats.length; gi++) haloMats[gi].opacity = 0.30 + 0.06 * Math.sin(t * 0.7 + gi * 2.1);
      if (lavaClones.length) {
        var p = 0.9 + 0.1 * Math.sin(t * 0.7);
        var ve = 0.32 + 0.15 * Math.sin(t * 0.5) + 0.03 * Math.sin(t * 3.7);
        for (var i = 0; i < lavaMats.length; i++) { lavaMats[i].color.setRGB(p, p * 0.97, p * 0.95); lavaMats[i].emissiveIntensity = ve; }
        for (var j = 0; j < lavaClones.length; j++) { lavaClones[j].offset.x = (t * 0.004) % 1; lavaClones[j].offset.y = (t * 0.006) % 1; }
      }
    }
  };
  return group;
}
export { createServitude, A_C as SERV_A, B_C as SERV_B };
export default createServitude;


===== FILE: public/city/servitude.js.bak-cloth-20261014 =====
/* THE SERVITUDE — a letter in the code.
 *
 * From the King's word (2026-10-14): "dig an entree in to
 * the mountain and build a huge city in it called the
 * Servitude" — "same as the ancestors area with layers but
 * city upon city" — "another one if it fits, with cave
 * tunnels connecting them, and a shop on each tunnel, a
 * place to buy replenishments of health and safety" — "a
 * nice journey to walk across the inside of the mountain,
 * like old lava tubes" — "stalactites and stalagmites,
 * joined with red and black marble."
 *
 * The digging itself lives in city.js (servitudeCarve): two
 * bowls capped into the far massif — THE FIRST HOLLOW
 * (2600,1280, floor 520) and THE DEEP HOLLOW (2600,1660,
 * floor 440) — the Entree shelf cut into the First Hollow's
 * south-western face, and the gallery slot descending
 * through the saddle between them. This factory builds
 * what stands inside the digging: two layered cities of
 * red and black marble, a ro