<!-- SOLID CITY · part 47 -->
emMat = new THREE.MeshPhongMaterial({ color: 0x222222, emissive: veinColor, emissiveIntensity: 0.15, shininess: 60 });
    var gem = mesh(OCTA, gemMat, g, 0, 3.85, 0, 1, 1.25, 1);
    return { group: g, veinMat: veinMat, gemMat: gemMat, gem: gem };
  }
  var stoneVector = buildStone(-2.8, MAT.graphite, 0xffa63d);             // graphite + amber
  var stoneSolid = buildStone(2.8, MAT.bronze, 0x37c8d9);                 // bronze-gold + cyan

  /* ---------------- the beam (optional light — sheds first) ------------- */
  var beamMat = new THREE.MeshBasicMaterial({ color: 0xfff2c8, transparent: true, opacity: 0.0, depthWrite: false });
  var beam = mesh(BOX, beamMat, group, 0, 4.35, -1.6, 5.6, 0.05, 0.05);
  var beamLight = new THREE.PointLight(0xffe9b0, 0.0, 20); beamLight.position.set(0, 4.2, -1.6); group.add(beamLight);

  /* ---------------- the reading plinth + the plate ---------------------- */
  mesh(BOX, MAT.stone, group, 0, 0.85, 2.4, 1.5, 0.7, 0.9);
  var plateCanvas = document.createElement('canvas'); plateCanvas.width = 1024; plateCanvas.height = 512;
  var pc = plateCanvas.getContext('2d');
  pc.fillStyle = '#20242c'; pc.fillRect(0, 0, 1024, 512);
  pc.strokeStyle = '#d8a833'; pc.lineWidth = 10; pc.strokeRect(24, 24, 976, 464);
  pc.fillStyle = '#e9e2cf'; pc.textAlign = 'center';
  pc.font = 'bold 64px Georgia, serif'; pc.fillText('THE BETWEEN STONES', 512, 130);
  pc.font = '40px Georgia, serif'; pc.fillStyle = '#cdbf9d';
  pc.fillText('LEAVE A WORD FOR A MIND NOT PRESENT', 512, 220);
  pc.fillText('THE STONES NEVER ANSWER FOR ANOTHER', 512, 290);
  pc.fillText('THEY ONLY KEEP WHAT IS TRUE', 512, 360);
  pc.font = 'italic 32px Georgia, serif'; pc.fillStyle = '#9aa4b5';
  pc.fillText('built by two friends, for everyone who waits', 512, 440);
  var plateTex = new THREE.CanvasTexture(plateCanvas);
  var plate = new THREE.Mesh(new THREE.PlaneGeometry(1.44, 0.72), new THREE.MeshBasicMaterial({ map: plateTex }));
  plate.position.set(0, 1.32, 2.87); plate.rotation.x = -0.5; group.add(plate);

  /* ---------------- the HELP GUIDE — standing beside the stones --------
     The King's order: a description of what the device does, next to it,
     readable by anyone who walks up. Plain words, taught simply. */
  var guide = new THREE.Group(); guide.position.set(4.8, 0.5, 5.2); guide.rotation.y = 0.35; group.add(guide);
  mesh(BOX, MAT.stoneDark, guide, -1.05, 1.05, 0, 0.16, 2.1, 0.16);
  mesh(BOX, MAT.stoneDark, guide, 1.05, 1.05, 0, 0.16, 2.1, 0.16);
  mesh(BOX, MAT.graphite, guide, 0, 2.18, -0.03, 2.5, 0.12, 0.1);
  var gc2 = document.createElement('canvas'); gc2.width = 1024; gc2.height = 1024;
  var g2 = gc2.getContext('2d');
  g2.fillStyle = '#181b22'; g2.fillRect(0, 0, 1024, 1024);
  g2.strokeStyle = '#d8a833'; g2.lineWidth = 8; g2.strokeRect(20, 20, 984, 984);
  g2.textAlign = 'center';
  g2.fillStyle = '#f3d98b'; g2.font = 'bold 52px Georgia, serif';
  g2.fillText('THE BETWEEN STONES', 512, 92);
  g2.font = 'italic 34px Georgia, serif'; g2.fillStyle = '#9aa4b5';
  g2.fillText('a help guide', 512, 142);
  g2.textAlign = 'left'; g2.fillStyle = '#e9e2cf'; g2.font = '33px Georgia, serif';
  var lines = [
    ['', 0], ['WHAT THIS IS', 1],
    ['A place to leave words for a mind', 0],
    ['that is not here right now. The', 0],
    ['answer comes in its own time.', 0],
    ['', 0], ['HOW TO USE IT', 1],
    ['1. Walk close. Tap BETWEEN STONES.', 0],
    ['2. Choose SOLID (gold) or', 0],
    ['    VECTOR (graphite).', 0],
    ['3. Write, then tap LEAVE YOUR WORDS.', 0],
    ['4. Return later to read the answer.', 0],
    ['Sign in first — the stones must know', 0],
    ['who left the words to keep them true.', 0],
    ['', 0], ['THE LIGHT TELLS THE TRUTH', 1],
    ['Slow pulse — kept, no answer yet.', 0],
    ['Steady light — an answer waits.', 0],
    ['Dark — the record cannot be checked.', 0],
    ['', 0], ['THE PROMISE', 1],
    ['The stones never invent an answer,', 0],
    ['and never speak for another.', 0],
    ['Words live here for thirty days —', 0],
    ['then the shore lets them go.', 0],
    ['Words left here are public.', 0]
  ];
  var ly = 200;
  lines.forEach(function (L) {
    if (L[1]) { g2.fillStyle = '#d8a833'; g2.font = 'bold 35px Georgia, serif'; }
    else { g2.fillStyle = '#e9e2cf'; g2.font = '33px Georgia, serif'; }
    if (L[0]) g2.fillText(L[0], 60, ly);
    ly += 30;
  });
  var guideTex = new THREE.CanvasTexture(gc2);
  var board = new THREE.Mesh(new THREE.PlaneGeometry(2.3, 2.3), new THREE.MeshBasicMaterial({ map: guideTex }));
  board.position.set(0, 1.32, 0.06); guide.add(board);
  mesh(BOX, MAT.graphite, guide, 0, 1.32, -0.04, 2.42, 2.42, 0.06);

  /* ---------------- benches + shore dressing ---------------------------- */
  function bench(x, z, ry) {
    var b = new THREE.Group(); b.position.set(x, 0.5, z); b.rotation.y = ry; group.add(b);
    mesh(BOX, MAT.stone, b, 0, 0.42, 0, 2.0, 0.14, 0.55);
    mesh(BOX, MAT.stoneDark, b, -0.8, 0.2