diff --git a/PROJECT.md b/PROJECT.md
index a78665d..e8f3d1d 100644
--- a/PROJECT.md
+++ b/PROJECT.md
@@ -5531,6 +5531,60 @@ day, textbook ... verdecken manchmal den Ball."*
serve's landing). Gore keeps its words on the bodies they belong to. Checked in the pane: PERFECT
under him, ball clear.
+### Step 4, measured first: the ladder cannot climb, because points are decided by the player's whiffs (2026-10-14, v372)
+Oliver on v371: *"okay, aber der Text wird auf dem iPhone teilweise links abgeschnitten"*, and *"es kann auch
+sein, dass durch die geänderte Mechanik alles etwas schwerer geworden ist"*.
+- **The praise word is kept on screen** (`render`, the pops loop): v371 centres it under the player's feet,
+ and a player out wide pushed "SHOT OF THE DAY" past the left edge. Every pop is clamped to the canvas by
+ its measured width. Gore's words get the same clamp.
+- **`tools/rally-sim.mjs` grew a human bot** (`BOT=human`, the default): it lands its finger on the timed
+ ring with `JIT` ms of gaussian spread (clamped ±2σ) plus a fixed `BIAS`, taps with a 120 ms lift, lobs or
+ passes a net man, drops a deep one. `CLIMB=1` plays the Climb's step boost on a pinned rung (training
+ with `train` off and hp topped up; driving the real Climb ended the run). `XP` sets the level, `WHY=1`
+ files every lost point under how it came about, `PROBE=1` times ring vs contact, `JSON=1` for
+ aggregation. Run 8 seeds and pool them: one seed's 40-50 points per rung is ±7%.
+- **Measured** (Climb, level 26, 8 seeds × 500 s, point win %; σ40 ≈ a practised thumb, σ60 an average one):
+
+ | rung | 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
+ |---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
+ | σ40 | 78 | 65 | 77 | 57 | 50 | 44 | 40 | 62 | 48 | 40 | 65 | 41 | 49 | 64 | 40 | 61 | 45 |
+ | σ60 | 61 | 39 | 52 | 38 | 32 | 30 | 29 | 44 | 27 | 29 | 48 | 23 | 31 | 51 | 29 | 49 | 34 |
+
+ The curve falls over the first six rungs and is then FLAT, zigzagging by style: the three net rushers and
+ the second slicer are 15-20 points easier than their neighbours, and the Champion is the Runner.
+- **Why it cannot climb.** Nearly every lost point (`WHY=1`) is the player's own "MISS tap too early"; the
+ CPU's whiff rate is `matchFlub`·0.76^step, ~1% at the Runner and ~0.07% at the Champion. The auto-run
+ makes the player defensively near-perfect, so the CPU's pace, width, quality and style barely decide a
+ point; the player's whiff rate decides, and no opponent dial touches it. Three experiments, all measured:
+ 1. **A quality ramp** (every rung's q band on a line from the shambler's to the Brute's, smart aim from rung
+ 6): no change outside the noise. Reverted.
+ 2. **A follow-through** (a groundstroke swing meeting a ball up to 60 ms after its window at q 0.05,
+ instead of whiffing): the bot won 80-99% everywhere, with 20-40-shot rallies. Reverted.
+ 3. **On top of it, the CPU putting sitters away** (a q < 0.5 ball answered at q 0.95 to the open court,
+ with a chance from 0.2 at the shambler to 0.85 at the champion): no change. The auto-run reaches the
+ put-away too. Reverted.
+- **Where the "harder" comes from.** The early whiff edge. A tap a little early starts the swing while
+ the ball is in the air, the swing waits for the bounce (`letBounce`), and the groundstroke window has run
+ out by then: a whiff at ~70 ms early (a tap 120 ms LATE still meets it). Before step 1 the same ball was
+ volleyed in the 1.8x window, so the edge sat near 130 ms. Sweep at level 26, rung 8, perfect thumb plus a
+ fixed bias: −70 ms is 56% contact, −50 ms q 0.24, 0 and +50 ms q 0.99, +70 ms q 0.52. The ring itself is
+ centred: contact comes 4-10 ms after its predicted moment, which is frame quantisation.
+- **Shipped: the quality curve loses its cliff** (`TUNE.qSeamless`, rally modes, player only). q was 1 inside
+ `perfectMs` and `1 - off/half` outside it: a groundstroke fell from 1 to 0.43 one ms past PERFECT. It now
+ falls from 1 at PERFECT's edge to the window's edge. −30 ms is q 0.86 (was 0.61), +70 ms q 0.76 (was 0.52).
+ The window, PERFECT and the whiff edge are unchanged, and **the bot's win rate does not move**; this is a
+ feel fix, not the difficulty fix. Test: a tap 3 frames early of the kiss returns at q >= 0.45 (0.33 on
+ the old curve).
+- **The decision for Oliver**, because each option changes what the game IS:
+ - (a) **Keep the error race and dial it**: the CPU's whiff curve gets a floor and a gentler decay so the
+ ladder slopes smoothly, styles get compensated. Cheap, predictable, still "who whiffs first".
+ - (b) **The follow-through, plus a CPU that can WIN points**: an early tap becomes a weak ball instead of a
+ whiff, and a sitter is punished by a put-away the auto-run does NOT fully cover (the run reacts late
+ after a weak ball of your own, or a put-away's pace is beyond its legs). Points then turn on shot
+ quality, which is what the opponent dials scale. More tennis, more tuning.
+ - (c) **Widen the early whiff edge only** (a smaller tail), keeping the error race: undoes most of the
+ "harder" without changing the model.
+
# OPEN — leftovers, in rough priority order
Nothing here is broken; these are known gaps and unverified risks.
@@ -5538,8 +5592,9 @@ Nothing here is broken; these are known gaps and unverified risks.
0. **THE RALLY GOES FLAT (2026-10-13), the top item.** Every match converged on serve, both at the net,
reflex volleys, won by a wrong-foot. Step 1 (the court gets its length) SHIPPED v361, awaiting the
phone verdict; the tap timing + leg knee (v362), the soft ball (v363, drop/lob split v364) and the
- opponent styles (v364) followed. Awaiting the phone verdict on v364. See the section of that name
- above. Fallback tag `rally-baseline-v360`.
+ opponent styles (v364) followed; the swing-log fixes ran to v371. **Step 4 (v372) is BLOCKED on a
+ design decision**: the ladder cannot climb while points are decided by the player's whiffs. See "Step
+ 4, measured first" above for options (a)/(b)/(c). Fallback tag `rally-baseline-v360`.
1. **`RES` 4 is unverified on a real phone under load.** It nearly doubled the fill rate (810 -> 1080
device px across). Oliver has not reported stutter, but he also has not played a late horde since.
diff --git a/index.html b/index.html
index 4a4bf03..6d2cb83 100644
--- a/index.html
+++ b/index.html
@@ -1140,6 +1140,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
REC
-
+