<!-- SOLID CITY · part 2440 -->
  B — Ward 6's drawn line, east end (-1158,-510), by the Long Road.
 * The Crown turns at (-1016, 88, -624), midway in the sky above them. */
export function createJubileePassage({ THREE, groundAt } = {}) {
  if (!THREE || typeof groundAt !== 'function') throw new Error('THREE and groundAt required');
  const GOLDEN = 2.399963229728653; // the golden angle, as the wormhole turns
  const CROWN = { x: -1016, y: 88, z: -624 };
  const MOUTHS = [
    { key: 'A', name: 'THE SHOULDER MOUTH', x: -843, z: -705 },
    { key: 'B', name: "THE ROAD'S-END MOUTH", x: -1158, z: -510 }
  ];
  const group = new THREE.Group(); group.name = 'THE JUBILEE PASSAGE';

  function swirlTexture() {
    try {
      const cv = document.createElement('canvas'); cv.width = cv.height = 256;
      const c = cv.getContext('2d');
      const g = c.createRadialGradient(128, 128, 8, 128, 128, 128);
      g.addColorStop(0, 'rgba(255,240,200,0.95)'); g.addColorStop(0.35, 'rgba(216,168,51,0.75)');
      g.addColorStop(0.7, 'rgba(120,90,180,0.45)'); g.addColorStop(1, 'rgba(40,60,120,0)');
      c.fillStyle = g; c.fillRect(0, 0, 256, 256);
      c.strokeStyle = 'rgba(255,225,150,0.8)'; c.lineWidth = 5;
      for (let i = 0; i < 42; i++) {
        const a = i * GOLDEN, r = 6 + i * 2.8;
        c.beginPath(); c.arc(128 + Math.cos(a) * r * 0.82, 128 + Math.sin(a) * r * 0.82, 3.2, a, a + 2.2); c.stroke();
      }
      const t = new THREE.CanvasTexture(cv); return t;
    } catch (e) { return null; }
  }
  function fadeTexture() {
    try {
      const cv = document.createElement('canvas'); cv.width = 16; cv.height = 128;
      const c = cv.getContext('2d');
      const g = c.createLinearGradient(0, 128, 0, 0);
      g.addColorStop(0, 'rgba(255,215,120,0.55)'); g.addColorStop(0.75, 'rgba(255,215,120,0.16)'); g.addColorStop(1, 'rgba(255,215,120,0)');
      c.fillStyle = g; c.fillRect(0, 0, 16, 128);
      return new THREE.CanvasTexture(cv);
    } catch (e) { return null; }
  }
  const swirl = swirlTexture(), fade = fadeTexture();
  const stone = new THREE.MeshLambertMaterial({ color: 0x3a4152 });
  const gold = new THREE.MeshLambertMaterial({ color: 0xd8a833, emissive: 0x6b4e10 });
  const swirlMat = new THREE.MeshBasicMaterial({ map: swirl || undefined, color: swirl ? 0xffffff : 0xd8a833, transparent: true, opacity: 0.95, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.DoubleSide });
  const pillarMat = new THREE.MeshBasicMaterial({ map: fade || undefined, color: 0xffd778, transparent: true, opacity: fade ? 0.85 : 0.30, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.DoubleSide });

  const swirls = [];   // the mouths' swirling discs
  const columns = [];  // passage spark columns, one per mouth

  for (const m of MOUTHS) {
    const gy = groundAt(m.x, m.z);
    m.gy = gy; m.top = gy + 0.3;
    const mg = new THREE.Group(); mg.position.set(m.x, 0, m.z); mg.name = m.name;
    const disc = new THREE.Mesh(new THREE.CylinderGeometry(3.4, 3.8, 0.6, 36), stone);
    disc.position.y = gy; disc.receiveShadow = true; mg.add(disc);
    const ring = new THREE.Mesh(new THREE.TorusGeometry(2.6, 0.09, 8, 48), gold);
    ring.rotation.x = Math.PI / 2; ring.position.y = m.top + 0.05; mg.add(ring);
    const sw = new THREE.Mesh(new THREE.CircleGeometry(2.45, 40), swirlMat);
    sw.rotation.x = -Math.PI / 2; sw.position.y = m.top + 0.04; mg.add(sw);
    swirls.push(sw);
    // the pillar of light: refused earth, reaching the crown's height
    const pillar = new THREE.Mesh(new THREE.CylinderGeometry(1.15, 1.5, CROWN.y - m.top, 20, 1, true), pillarMat);
    pillar.position.y = (CROWN.y + m.top) / 2; mg.add(pillar);
    // the passage sparks: a golden-angle column, lit only while the passage fires
    const N = 150, pos = new Float32Array(N * 3);
    for (let i = 0; i < N; i++) {
      const f = i / N, a = i * GOLDEN, r = 1.05;
      pos[i * 3] = Math.cos(a) * r; pos[i * 3 + 1] = m.top + f * (CROWN.y - m.top); pos[i * 3 + 2] = Math.sin(a) * r;
    }
    const pg = new THREE.BufferGeometry(); pg.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3));
    const pts = new THREE.Points(pg, new THREE.PointsMaterial({ color: 0xffd778, size: 0.85, transparent: true, opacity: 0, blending: THREE.AdditiveBlending, depthWrite: false }));
    pts.name = 'passage-sparks'; mg.add(pts);
    columns.push({ pts, t: -1 });
    group.add(mg);
  }

  // THE JUBILEE CROWN — one turning spiral in the heavens between the mouths
  const crown = new THREE.Group(); crown.position.set(CROWN.x, CROWN.y, CROWN.z); crown.name = 'THE JUBILEE CROWN';
  const ringA = new THREE.Mesh(new THREE.TorusGeometry(9, 0.18, 8, 72), gold);
  const ringB = new THREE.Mesh(new THREE.TorusGeometry(6.4, 0.12, 8, 64), new THREE.MeshLambertMaterial({ color: 0x7fd7d5, emissive: 0x1d4d4b }));
  ringA.rotation.x = Math.PI / 2.25; ringB.rotation.x = Math.PI / 1.8; ringB.rotation.y = 0.5;
  crown.add(ringA); crown.add(ringB);
  {
    const N = 700,