<!-- NEW1 — TEST 1 · THE LIVING DISTRICT · part 444 -->
on beginLeisure(c) {
    c.state = 'leisure'; c.timer = 60 + rng() * 60;
    c.leisurePt = null;
    if (c.leisureKind === 'football') { c.fx = pitch.x + (rng() * 2 - 1) * pitch.hw; c.fz = pitch.z + (rng() * 2 - 1) * pitch.hd; placeAt(c, c.fx, c.fz); }
    if (c.leisureKind === 'run') c.runA = rng() * 6.28;
    if (c.leisureKind === 'bike') {
      // the joyride loops the streets of the rider's OWN suburb block,
      // starting from whichever point of the loop is nearest to them.
      const sub = meta.complexes.find((x) => x.name === c.home.suburb);
      c.loopC = sub ? sub.center : { x: c.x, z: c.z };
      const hw = 41, hd = 41, cx = c.loopC.x, cz = c.loopC.z;
      const nx = Math.max(cx - hw, Math.min(cx + hw, c.x));   // c.x clamped onto the loop's span
      const nz = Math.max(cz - hd, Math.min(cz + hd, c.z));
      const edges = [ // each edge's closest point + the perimeter distance at that point
        { s: nx - (cx - hw), x: nx, z: cz - hd },
        { s: 2 * hw + (nz - (cz - hd)), x: cx + hw, z: nz },
        { s: 2 * hw + 2 * hd + (cx + hw - nx), x: nx, z: cz + hd },
        { s: 4 * hw + 2 * hd + (cz + hd - nz), x: cx - hw, z: nz },
      ];
      let best = edges[0], bd = Infinity;
      for (const e of edges) { const d = (e.x - c.x) ** 2 + (e.z - c.z) ** 2; if (d < bd) { bd = d; best = e; } }
      c.bikeS = best.s;
    }
  }
  function stepLeisure(c, dtGame, dtReal, tReal) {
    c.timer -= dtGame;
    if (c.leisureKind === 'football') {
      // a kickabout: jog to a new pitch point now and then
      if (c.leisurePt == null || Math.hypot(c.x - c.leisurePt.x, c.z - c.leisurePt.z) < 0.6) {
        if (rng() < 0.02 || c.leisurePt == null) c.leisurePt = { x: pitch.x + (rng() * 2 - 1) * pitch.hw, z: pitch.z + (rng() * 2 - 1) * pitch.hd };
        poses.idle(c.rig, tReal, 1);
      }
      if (c.leisurePt) {
        const dx = c.leisurePt.x - c.x, dz = c.leisurePt.z - c.z, d = Math.hypot(dx, dz);
        if (d > 0.6) {
          const sp = gait.run.speed * 2.4;
          placeAt(c, c.x + (dx / d) * sp * dtReal, c.z + (dz / d) * sp * dtReal);
          faceTo(c, dx, dz);
          c.phase += sp * dtReal * gait.run.phasePerUnit;
          poses.run(c.rig, c.phase, 0.8);
        }
      }
    } else if (c.leisureKind === 'run') {
      c.runA += dtReal * 0.45;
      const x = park.center.x + Math.cos(c.runA) * 26, z = park.center.z + Math.sin(c.runA) * 26;
      faceTo(c, x - c.x, z - c.z);
      placeAt(c, x, z);
      c.phase += dtReal * 9.2; // the runners' own clock (nervesmith's note)
      poses.run(c.rig, c.phase, 1);
    } else if (c.leisureKind === 'bike') {
      // a joyride around the Signal Square streets
      const sq = c.loopC, hw = 41, hd = 41;
      if (!c.bike) { poses.idle(c.rig, tReal, 1); } // walkers who wished for a bike stroll instead
      else {
        c.phase += dtReal * gait.bike.speed * 3 * gait.bike.phasePerUnit;
        const P = 4 * (hw + hd);
        c.bikeS = ((c.bikeS || 0) + dtReal * gait.bike.speed * 3) % P;
        let s = c.bikeS, x, z, dx, dz;
        const W = hw * 2, D = hd * 2;
        if (s < W) { x = sq.x - hw + s; z = sq.z - hd; dx = 1; dz = 0; }
        else if (s < W + D) { x = sq.x + hw; z = sq.z - hd + (s - W); dx = 0; dz = 1; }
        else if (s < 2 * W + D) { x = sq.x + hw - (s - W - D); z = sq.z + hd; dx = -1; dz = 0; }
        else { x = sq.x - hw; z = sq.z + hd - (s - 2 * W - D); dx = 0; dz = -1; }
        faceTo(c, dx, dz); placeAt(c, x, z);
        poses.bike(c.rig, c.phase, 1);
        const spin = gait.bike.speed * 3 * dtReal / 0.34;
        c.bike.wF.rotation.x += spin; c.bike.wB.rotation.x += spin;
      }
    } else {
      // a stroll around the park sidewalks
      c.phase += gait.walk.speed * 2 * dtReal * gait.walk.phasePerUnit;
      poses.idle(c.rig, tReal, 0.6);
      faceTo(c, Math.sin(tReal * 0.1 + c.i), Math.cos(tReal * 0.1 + c.i));
    }
    if (c.timer <= 0) startRoute(c, c.home.door, 'home');
  }

  /* ---------- the day itself ---------- */
  let gameMin = 360; // Day 1 begins at 06:00 — the city about to wake
  let day = 1;
  let tReal = 0;
  for (const c of citizens) { c.plan = assess(c, day); placeAt(c, c.home.door.x, c.home.door.z); }

  function update(dtReal) {
    const dtGame = dtReal * RATE;
    gameMin += dtGame; tReal += dtReal;
    if (gameMin >= DAY) {
      gameMin -= DAY; day++;
      for (const c of citizens) { c.plan = assess(c, day); if (c.state === 'sleep') { /* new day, new assessment */ } }
    }
    const now = gameMin;
    for (const c of citizens) {
      const p = c.plan;
      switch (c.state) {
        case 'sleep':
          if (c.departedDay !== day && now >= p.depart && now < p.workEnd) {
            c.departedDay = day;
            placeAt(c, c.home.door.x, c.home.door.z);
            startRoute(c, c.jobPoint, 'work');
          }
          break;
        case 'travel': stepTravel(c, dtGame, dtReal); break;
        case 'work':