<!-- NEW1 — TEST 1 · THE LIVING DISTRICT · part 442 -->
 'Sofia', 'Ravi', 'June', 'Omar', 'Tessa', 'Bodhi', 'Ines', 'Koa', 'Ruth', 'Silas', 'Vera'];

function makeLife(THREE, meta, pieces) {
  const { makeFigure, makeBike, poses, gait } = pieces;
  const rng = mulberry32(20261015);
  const group = new THREE.Group();

  /* ---------- the street lattice + the river law ----------
   * Nodes are intersections. A vertical street segment that spans
   * the river band exists ONLY on a bridge street. */
  const xs = meta.streets.xs, zs = meta.streets.zs;
  const band = meta.riverBand;
  const isBridgeX = (x) => meta.bridges.some((b) => Math.abs(b - x) < 0.01);
  function route(from, to) {
    const nearest = (p) => {
      let bi = 0, bj = 0, bd = 1e18;
      for (let i = 0; i < xs.length; i++) for (let j = 0; j < zs.length; j++) {
        const d = (xs[i] - p.x) ** 2 + (zs[j] - p.z) ** 2;
        if (d < bd) { bd = d; bi = i; bj = j; }
      }
      return [bi, bj];
    };
    const [si, sj] = nearest(from), [ti, tj] = nearest(to);
    const key = (i, j) => i + ',' + j;
    const prev = new Map([[key(si, sj), null]]);
    const queue = [[si, sj]];
    const crossesRiver = (j1, j2) => {
      const a = zs[j1], b = zs[j2];
      return Math.min(a, b) < band.zMax && Math.max(a, b) > band.zMin && a !== b
        && ((a <= band.zMin && b >= band.zMax) || (b <= band.zMin && a >= band.zMax));
    };
    while (queue.length) {
      const [i, j] = queue.shift();
      if (i === ti && j === tj) break;
      const next = [];
      if (i > 0) next.push([i - 1, j]);
      if (i < xs.length - 1) next.push([i + 1, j]);
      if (j > 0) next.push([i, j - 1]);
      if (j < zs.length - 1) next.push([i, j + 1]);
      for (const [ni, nj] of next) {
        if (prev.has(key(ni, nj))) continue;
        if (ni === i && crossesRiver(j, nj) && !isBridgeX(xs[i])) continue; // water: bridges only
        prev.set(key(ni, nj), [i, j]);
        queue.push([ni, nj]);
      }
    }
    const pts = [{ x: from.x, z: from.z }];
    if (prev.has(key(ti, tj))) {
      const chain = [];
      let cur = [ti, tj];
      while (cur) { chain.unshift({ x: xs[cur[0]], z: zs[cur[1]] }); cur = prev.get(key(cur[0], cur[1])); }
      // drop the first node if the straight start already passes it
      for (const p of chain) pts.push(p);
    }
    pts.push({ x: to.x, z: to.z });
    // simplify: remove collinear middle points
    const out = [pts[0]];
    for (let k = 1; k < pts.length - 1; k++) {
      const a = out[out.length - 1], b = pts[k], c = pts[k + 1];
      if ((a.x === b.x && b.x === c.x) || (a.z === b.z && b.z === c.z)) continue;
      out.push(b);
    }
    out.push(pts[pts.length - 1]);
    let length = 0;
    for (let k = 1; k < out.length; k++) length += Math.hypot(out[k].x - out[k - 1].x, out[k].z - out[k - 1].z);
    return { pts: out, length };
  }

  /* ---------- places ---------- */
  const byKind = (kind) => meta.complexes.filter((c) => c.kind === kind);
  const park = byKind('park')[0];
  const grocery = meta.complexes.find((c) => c.name === 'Grand Market Row') || byKind('market')[0];
  const pitch = { x: park.center.x + 16, z: park.center.z, hw: 9, hd: 6.5 };
  const workplaces = meta.complexes.filter((c) => c.kind !== 'suburb' && c.kind !== 'park');
  const jobSlot = (c) => { // a citizen's own spot at their complex — a small seeded ring
    const a = rng() * Math.PI * 2, r = 6 + rng() * 14;
    return { x: c.center.x + Math.cos(a) * r, z: c.center.z + Math.sin(a) * r * 0.7 };
  };

  /* ---------- the citizens ---------- */
  const houses = meta.houses.slice();
  const n = Math.min(POPULATION, houses.length);
  const citizens = [];
  for (let i = 0; i < n; i++) {
    const house = houses[Math.floor(rng() * houses.length)];
    houses.splice(houses.indexOf(house), 1);
    const job = workplaces[Math.floor(rng() * workplaces.length)];
    const bikeCommute = rng() < 0.4;
    const rig = makeFigure(THREE, rng);
    let holder = null, bike = null;
    if (bikeCommute) {
      bike = makeBike(THREE, rng);
      holder = new THREE.Group();
      holder.add(bike.group);
      rig.root.position.set(0, 0.30, -0.30);
      holder.add(rig.root);
      group.add(holder);
    } else {
      group.add(rig.root);
    }
    const c = {
      i, name: NAMES[i % NAMES.length], rig, holder, bike,
      home: house, job, jobPoint: jobSlot(job),
      mode: bikeCommute ? 'bike' : 'walk',
      leisureKind: ['football', 'run', 'bike', 'walk', 'none'][Math.floor(rng() * 5)],
      state: 'sleep', phase: rng() * 6.28, x: house.door.x, z: house.door.z,
      path: null, seg: 0, segT: 0, plan: null, timer: 0, spotT: 0,
      workMicro: null, departedDay: 0,
    };
    if (c.leisureKind === 'bike' && !bikeCommute) c.leisureKind = 'walk'; // no bike, no joyride — a stroll instead
    citizens.push(c);
    setVisible(c, false);
  }

  /* ---------- the kickabout's ball: one ball, passed among whoever
   * is on the pitch at leisure ---------- */
  const ba