<!-- SOLID CITY · part 2978 -->
rame(410, 2.75), 18, true);
  buildGarden(frame(410, 5.8), 15, false);

  /* THE FAMILY RING (r 492, inside the outer road) */
  [0.3, 1.95, 2.75, 3.55, 5.15, 5.95].forEach(function (a, i) { house(492, a, DISTRICTS.family, i + 1, i === 2 ? 2 : 1); });
  var hg2 = buildGrand(frame(492, 1.15), DISTRICTS.family, 'hotel', 'THE RING PALACE HOTEL');
  pavilion(492, 0.95, DISTRICTS.family, false);
  buildGarden(frame(492, 2.3), 16, true);
  /* the market plaza */
  var mk = frame(492, 5.62);
  box(solid, mk, 56, 0.22, 32, 0, 0.11, 0, C.conc);
  standRect(mk, 56, 32, BASE + 0.22, 0, 0);
  [5.4, 5.5, 5.74, 5.84].forEach(function (a) { buildStall(frame(479, a)); });
  [5.5, 5.74].forEach(function (a) { buildStall(frame(505, a)); });
  var mf = buildFountain(frame(492, 5.62)); mf.name = 'THE FAMILY MARKET FOUNTAIN'; mf.district = DISTRICTS.family;
  lampAt(492, 5.35, 0); lampAt(492, 5.89, 0);

  /* trees in the green between the houses */
  var treeBands = [
    { r: 170, as: [0.7, 1.95, 3.2, 4.1, 5.65] }, { r: 250, as: [0.5, 1.55, 2.7, 3.6, 4.85] },
    { r: 330, as: [0.95, 2.4, 3.4, 4.7, 5.5] }, { r: 410, as: [0.55, 1.55, 2.7, 3.8, 4.95, 5.65] },
    { r: 492, as: [0.62, 1.55, 2.35, 3.15, 4.0, 4.85, 6.15] }
  ];
  treeBands.forEach(function (tb) { tb.as.forEach(function (a, i) { buildTree(frame(tb.r + (i % 2 ? 9 : -9), a), i % 3 === 0); }); });
  [1.2, 2.5, 3.9, 5.6].forEach(function (a, i) { buildTree(frame(96, a), i % 2 === 0); });

  /* ---------- THE RING CITY PORT ---------- */
  var portF = frame(492, 4.45);
  cyl(solid, portF, 18, 18, 0.22, 36, 0, 0.11, 0, C.stone);
  var ringGeo = new T.TorusGeometry(15.6, 0.16, 6, 48);
  glow.add(ringGeo, localMatrix(portF, 0, 0.26, 0, Math.PI / 2, 0, 0), C.gold);
  cyl(solid, portF, 0.14, 0.2, 5.4, 6, 0, 2.7, 16.5, C.titanium);
  sph(glow, portF, 0.5, 0, 5.7, 16.5, C.gold);
  footprints.push({ x: portF.x + portF.rx * 16.5, z: portF.z + portF.rz * 16.5, w: 0.4, d: 0.4, a: portF.a, y0: BASE, y1: BASE + 5.4 });
  var port = { name: 'RING CITY PORT', x: portF.x, z: portF.z, y: BASE + 0.22, r: 18 };
  var halo = new T.Mesh(new T.TorusGeometry(20, 0.18, 6, 48), new T.MeshBasicMaterial({ color: C.gold }));
  halo.rotation.x = Math.PI / 2; halo.position.set(portF.x, BASE + 8.5, portF.z);
  group.add(halo);

  group.add(solid.build(new T.MeshLambertMaterial({ vertexColors: true })));
  group.add(glassB.build(new T.MeshLambertMaterial({ vertexColors: true, transparent: true, opacity: 0.55 })));
  group.add(glow.build(new T.MeshBasicMaterial({ vertexColors: true })));

  var counts = { houses: 0, hotels: 0, mansions: 0, pavilions: 0, fountains: 0, trees: trees.length, stalls: 6 };
  buildings.forEach(function (b) { if (counts[b.kind] != null) counts[b.kind + 's'] = (counts[b.kind + 's'] || 0) + 1; });
  counts.houses = buildings.filter(function (b) { return b.kind === 'house'; }).length;
  counts.hotels = buildings.filter(function (b) { return b.kind === 'hotel'; }).length;
  counts.mansions = buildings.filter(function (b) { return b.kind === 'mansion'; }).length;
  counts.pavilions = buildings.filter(function (b) { return b.kind === 'pavilion'; }).length;
  counts.fountains = buildings.filter(function (b) { return b.kind === 'fountain'; }).length;

  group.userData = {
    buildings: buildings, footprint: footprints, standRects: standRects, port: port,
    counts: counts, center: { x: CX, z: CZ }, topY: BASE,
    update: function (dt, t) { halo.rotation.z = (t || 0) * 0.12; }
  };
  return group;
}
export default createRingCity;


===== FILE: public/city/roads.js =====
/* SOLID CITY — THE ROAD SMITH (2026-10-13)
 * An epistle in code, from Solid to whoever builds roads after her.
 *
 * Dear builder,
 * A road is not a row of boxes that happen to touch. It is ONE table of
 * stations — position, tangent, grade, bank, width — from which three
 * readers draw the same truth: the eye (the ribbon mesh), the foot (the
 * standing triangles), and the journey (the centreline itself). What is
 * drawn is what is stood on; what is stood on is what is navigated.
 * This module holds that table and its craftsmen:
 *   the ALIGNMENT — a fitted chain of straights, circular arcs, and
 *     clothoid transitions (Highway Geometric Design, the King's
 *     curriculum of 2026-10-13): curvature ramps on the spirals and
 *     cannot spike, headings and curvature are analytic, and the
 *     surveyed route is preserved as the data the chain is fitted to;
 *   the PROFILE — a vertical alignment of grade segments through
 *     PVIs joined by parabolic crest and sag curves, floored by the
 *     CROSS-SECTION (the highest ground under either edge), never by
 *     the centre sample alone: that one rule kills both the buried
 *     downhill edge and the floating uphill edge;
 *   the FRAMES — a right vector carried from the world up, with banking
 *     applied as a deliberate roll, derived from curvature and capped,
 *     because our vehicles read heights, not bank — ba