<!-- SOLID CITY · part 3048 -->
[sites.length - 1].v === vi - 1;
      var nextSite = false; // resolved below by leg sharing
      var roomIn = leg[vi - 1] * (prevSite ? 0.5 : 0.94);
      sites.push({ v: vi, D: D, roomIn: roomIn, roomOut: 0 });
    }
    for (var si = 0; si < sites.length; si++) {
      var s2 = sites[si];
      var hasNext = si + 1 < sites.length && sites[si + 1].v === s2.v + 1;
      s2.roomOut = leg[s2.v] * (hasNext ? 0.5 : 0.94);
      s2.room = Math.min(s2.roomIn, s2.roomOut);
    }
    /* per-site design: radius and spirals within the room */
    for (si = 0; si < sites.length; si++) {
      var s3 = sites[si], aD = Math.abs(s3.D), tanH = Math.tan(aD / 2);
      var R = Math.min(26, Math.max(6.5, s3.room * 0.92 / Math.max(tanH, 1e-3)));
      var Ls = Math.min(14, Math.max(2.5, 0.55 * R * aD));
      /* THE DEVIATION CAP — the surveyed points on the mountain
       * legs are PATH points, not PIs of an imagined design: a
       * generous arc on a hard turn swings metres inside the
       * vertex, through ground the district has already given to
       * homes. The arc's apex may stand at most ~3 m from the
       * surveyed vertex (the spline's own habit), so R shrinks
       * until the geometry honours it — at a true hairpin the
       * survey's own radius was always small, and the chain
       * degrades to it honestly. */
      var TOL_DEV = 2.0, R_FLOOR = 4.5;
      for (var dv = 0; dv < 6; dv++) {
        if (R <= R_FLOOR) break;   // the cap yields at true hairpins: a civil radius swinging wide beats a crushed kink
        var pShift = Ls * Ls / (24 * R);
        var dev = (R + pShift) * (1 / Math.cos(aD / 2) - 1);
        if (dev <= TOL_DEV) break;
        var scale = Math.max(0.45, TOL_DEV / dev * 0.92);
        R *= scale; Ls *= scale;
        if (R < R_FLOOR) { Ls *= R_FLOOR / R; R = R_FLOOR; }
      }
      function consumed(Rx, Lx) { return (Rx + Lx * Lx / (24 * Rx)) * tanH + Lx / 2 - Math.pow(Lx, 3) / (240 * Rx * Rx); }
      var guard = 0;
      while (consumed(R, Ls) > s3.room && guard++ < 6) { Ls *= 0.6; if (Ls < 1.2) { Ls = 0; break; } }
      if (consumed(R, Ls) > s3.room) { R = Math.max(4.5, s3.room / Math.max(tanH, 1e-3)); Ls = 0; }
      if (Ls > 0 && Ls / R > aD * 0.9) Ls = 0.9 * R * aD;   // the spirals may not eat the arc
      s3.R = R; s3.Ls = Ls; s3.T = consumed(R, Ls);
    }
    /* the element chain, walked forward from the first point */
    var els = [];   // {kind, len, k0, k1, h0} — curvature linear in s for clothoids
    var px = V[0][0], pz = V[0][1], hdg = phi[0];
    function pushLine(toX, toZ) {
      var d = Math.hypot(toX - px, toZ - pz);
      if (d > 0.01) els.push({ kind: 'line', len: d, k0: 0, k1: 0, h0: hdg });
      px = toX; pz = toZ;
    }
    for (si = 0; si < sites.length; si++) {
      var s4 = sites[si], vv = s4.v;
      var inX = V[vv][0] - Math.sin(phi[vv - 1]) * s4.T, inZ = V[vv][1] - Math.cos(phi[vv - 1]) * s4.T;
      pushLine(inX, inZ);
      var sgn = Math.sign(s4.D), kArc = sgn / s4.R;
      if (s4.Ls > 0.01) { els.push({ kind: 'cloth', len: s4.Ls, k0: 0, k1: kArc, h0: hdg }); hdg += kArc * s4.Ls / 2; }
      var arcSweep = s4.D - (s4.Ls > 0.01 ? kArc * s4.Ls : 0);
      if (Math.abs(arcSweep) > 1e-4) { els.push({ kind: 'arc', len: Math.abs(arcSweep) * s4.R, k0: kArc, k1: kArc, h0: hdg }); hdg += arcSweep; }
      if (s4.Ls > 0.01) { els.push({ kind: 'cloth', len: s4.Ls, k0: kArc, k1: 0, h0: hdg }); hdg += kArc * s4.Ls / 2; }
      px = V[vv][0] + Math.sin(phi[vv]) * s4.T; pz = V[vv][1] + Math.cos(phi[vv]) * s4.T;
      // the walker's position after the elements is integrated below; px/pz here only anchor the NEXT tangent's start in exact geometry
    }
    // final tangent to the last surveyed point (exact)
    els.push({ kind: 'line', len: 0, k0: 0, k1: 0, h0: hdg, toEnd: true });
    /* integrate: fine steps, midpoint rule on the analytic heading;
     * the final line element's length is whatever closes the chain */
    var totalEls = els.length;
    // first pass: lengths of all but the closing line
    var dense = [[V[0][0], V[0][1], phi[0], 0]];   // x, z, heading, kappa
    var cx = V[0][0], cz = V[0][1], th = phi[0], sAcc = 0;
    var DT = 0.2;
    for (var ei = 0; ei < totalEls; ei++) {
      var el = els[ei];
      var elLen = el.len;
      if (el.toEnd) {
        // direction is th (== final leg heading); length closes to V[nv-1]
        var ex = V[nv - 1][0] - cx, ez = V[nv - 1][1] - cz;
        elLen = ex * Math.sin(th) + ez * Math.cos(th);
        if (elLen < 0) elLen = 0;
      }
      var steps = Math.max(1, Math.ceil(elLen / DT)), dt = elLen / steps;
      for (var q = 0; q < steps; q++) {
        // heading at the substep's midpoint, from the element's own
        // law: θ(s) = h0 + k0·s + (k1−k0)·s²/(2L) — exact for lines,
        // arcs, and clothoids alike; the midpoint rule on an analytic
        // heading keeps position error at millimetres per element
        var sMid = (q + 0.5) * dt;
     