## Summary
Multi-Z cells (bridges, stairs, paver-over-ground, multi-floor structures) now carry **per-stratum walkability data** in the cache instead of falling through to the slow path. The data is computed at chunk-build time, persisted in the `.swb` file, and selected at query time by matching the request's `sourceZ` against each stratum's `zCenter` (within `StepHeight` tolerance).
This is the Tier 4 strata feature, deferred from PR #2447 / PR #2448 / PR #2449. Builds on PR #2449's lazy backing store and public bake helpers.
## Wire format change (v1 → v2)
`StepCacheFile.FormatVersion = 2`. `MinSupportedVersion = 2`. v1 `.swb` files are silently rejected at open time (treated as missing) and overwritten on the next `SaveToFile` / `BakeMap`. **No migration** — older files just get re-baked.
The `MinSupportedVersion` sentinel is the model going forward: bump the constant when an incompatible change lands; admins re-bake on the next deploy. No matrix of v1↔v2↔v3 migration logic to maintain.
## What changed
- **`StepProbe.ComputeStrataAt(map, x, y)`** — enumerates walkable standing-Zs at the cell (one per land surface plus one per walkable static), collapses Zs within `2*StepHeight`, runs `ComputeMaskAt` at each surviving Z. Returns `null` for single-Z cells (caller uses the chunk's main mask).
- **`StepChunk`** — replaces the old `MultiZCells` bitmap with a **strata storage pair**:
- `ushort[256] StrataOffsetByCell` (sentinel `NoStrata = 0xFFFF` = "no strata for that cell")
- `byte[] StrataData` packed: `u8 stratumCount`, then `count × 19-byte stratum`
- `sbyte zCenter, byte walkMask, byte wetMask, sbyte walkZ_N..NW (8), sbyte swimZ_N..NW (8)`
- `IsCellMultiZ` derives from `StrataOffsetByCell[cell] != NoStrata` — same semantics, single source of truth.
- **`StepCache.BuildChunk`** — populates strata for cells flagged multi-Z via `SetStrata`. Chunks with zero multi-Z cells pay zero strata overhead (offset array + data array stay null).
- **`StepCache.TryGetMask`** — for multi-Z cells, scans strata with `TryStratumHit`; returns the matching one with `HitKind=Hit`. Falls through to slow path only when no stratum matches the query `sourceZ`.
- **`StepCacheFile`** — v2 serialization with strata trailer per chunk + `recordLength` in index entry. Lazy reader sizes scratch per-chunk-record using the recorded length, growing on demand for multi-Z-heavy chunks. Patches `IndexOffset` on `w.Buffer` (BufferWriter's current backing array) since it grows during variable-size chunk writes.
## File layout v2
```
Header (48 bytes):
u32 Magic = 0x42575300 ('SWB\0')
u32 Version = 2
u32 MapId
u64 Fingerprint XxHash3 over LandTable + ItemTable flags + map files (mapX.mul/.uop, staidxX.mul, staticsX.mul)
u64 BakeTimestamp informational
u32 ChunkCount
u64 IndexOffset position where chunk index begins
Per chunk (variable size):
u16 ChunkX, ChunkY
u32 BuiltMultisVersion
u8 HasStrata 0 = no strata trailer; 1 = strata trailer follows
byte WalkMask[256], WetMask[256]
sbyte SourceZ[256], WalkZN..WalkZNW[256], SwimZN..SwimZNW[256]
// Strata trailer (only when HasStrata == 1):
u16 StrataOffsetByCell[256] // NoStrata sentinel = 0xFFFF
u32 StrataDataLength
byte StrataData[StrataDataLength]
Per multi-Z cell: u8 count, then count × Stratum (19 bytes)
Index trailer (20 × ChunkCount bytes):
per chunk: { u64 chunkKey, u64 fileOffset, u32 recordLength }
```