# Lane G — Colour zones, paint puddles, MEGA/MINI

**Owns:** `src/paint/puddles.ts`, `src/paint/buffs.ts` (extend — you inherit
Lane B's file), `src/course/zones.ts`, `demos/lane-g.html`, `src/demo/lane-g.ts`.
**Never touches:** contracts, world, game, main, package.json, `src/blob/`,
`src/machine/` (USE machine parts via import — just don't edit them), other
lane dirs. No new deps.

## Why this lane exists (live-play finding, 2026-07-18)
A naive racer currently finishes the course with **0% paint** — ballistic
cannons miss fast movers and nothing on the ground paints you. The core
mechanic is skippable. Fix: paint must be guaranteed by ROUTE, not by aim, and
colours must come in ZONES so buffs actually activate (20% threshold is
unreachable when three colours dilute each other — also observed live).

## Deliverables
1. `src/paint/puddles.ts` — `PaintPuddle`: a flat coloured strip on the track
   (mesh + config `{position, size, color}`). While the blob's ground contact
   is inside the strip, stamp its UNDERSIDE via `skin.splatAtPoint` at the
   contact point on a cadence (~6 stamps/s, small radius) — rolling through
   paint coats your belly. Registered via an `installPuddles(world, blob, skin,
   configs)` entry point (integration calls it). Emit `paint:splatted
   {color, target:'blob'}` per stamp (audio hooks it).
2. `src/paint/buffs.ts` — extend the coverage→modifier mapping (GDD §6):
   - **purple MEGA**: `size` 1.0→1.45 + extra `massMul` (heavy). Strength scales
     20%→70% like the others.
   - **pink MINI**: `size` 1.0→0.62, LESS mass (lighter than clean at high %).
   - purple and pink fight: net size uses (purple − pink) strength.
   - Existing red/green/blue unchanged. Keep the pure-function style +
     UPDATE `src/paint/coverage-math.test.ts` for the new mappings.
3. `src/course/zones.ts` — `installZones(world, blob, skin)`: builds the colour
   zones ON the existing greybox (import nothing from game.ts; use absolute
   course coordinates — the course spans z 30 → -70, see src/course/greybox.ts):
   - **RED zone** (ramp approach, z≈20..8): red puddle strip + the existing
     red cannon position gets a second red cannon (crossfire). BURN speed helps
     the climb.
   - **GREEN zone** (shelves + slope, z≈8..-24): green puddles on both shelves;
     GRIP helps the slope and gravity-flip future.
   - **BLUE zone** (milk river, z≈-24..-37): blue puddle lines the riverbanks;
     waterproof = your paint survives the water.
   - **PURPLE/PINK fork** (machine district, z≈-37..-58): LEFT lane (x<0):
     purple puddle → MEGA → heavy enough to trip the pressure plate → boot yeet
     (the course TEACHES paint=weight). RIGHT lane (x>3): pink puddle → MINI →
     a **low tunnel** (build it: a slab roof over the track at y≈0.9, only a
     MINI blob fits under) that shortcuts toward the finish. Normal-size blobs
     bonk and must take another lane.
   - Cannons: relocate/add so each zone's cannons fire ITS colour (configs
     returned so integration can place them; or place them yourself in
     installZones — your call, document it).
4. Demo `demos/lane-g.html` + `src/demo/lane-g.ts`: a test ball rolling through
   each zone in sequence with live coverage/modifier logging; prove: puddles
   coat reliably, purple grows + trips a test plate, pink shrinks + fits the
   tunnel while a clean ball bonks.

## Acceptance
- `npm run build` + updated coverage-math tests pass.
- A straight-line hold-forward run through the zones ends with ≥40% total
  coverage and at least one active buff (puddles guarantee it — verify in demo).
- Purple demo ball trips a `massThreshold: baseMass*1.25` plate; clean ball doesn't.
- Pink demo ball (≥35% pink) passes under the tunnel; clean ball cannot.
- No edits outside owned paths.

## Contracts
Emit `paint:splatted` per puddle stamp. Existing events unchanged. `blob.modifiers.size`
is already honored by the controller (collider + visual scale) — do not touch the blob.
