Multi-instancing migration (Step 4): convert Wizard.Data.BattleRecoveryInfo — the per-battle
mid-game recovery snapshot (replay/reconnect/late-join state, owned by RecoveryDataHandler /
RecoveryNetworkManagerBase / RecoveryController) — to resolve through BattleAmbient.Current
when a scope is active, falling back to a `_battleRecoveryInfoFallback` static when not.
The original was a plain auto-property `{ get; set; }`; converting to a manual property +
ambient-aware getter/setter preserves the public read/write surface byte-identical
(design 2026-06-07-engine-multi-instancing, Task 4).

This is the only of the three Step-4 conversions whose setter we DO route through ambient
(unlike ViewerId, where the in-scope setter is a no-op): recovery info is mutated during the
battle (new snapshots overwrite old ones as recovery checkpoints stream in), so scoped writes
must land on the AsyncLocal slot, not bleed into the process-wide fallback.

The existing line-348 reset inside `Data.Clear()` (`BattleRecoveryInfo = null;`) keeps working
unchanged because it goes through the public setter, which now correctly routes to whichever
slot the caller's scope (or lack thereof) points at.

In-file references (1 declaration; ~5 setter/getter call sites elsewhere in the file go through
the public property and need no edits) handled as follows:
  - line 179 auto-property → manual ambient-first getter + ambient-routed setter

--- Engine/Wizard/Data.cs  (~line 179)
-	public static BattleRecoveryInfo BattleRecoveryInfo { get; set; }
+	private static BattleRecoveryInfo _battleRecoveryInfoFallback;
+	public static BattleRecoveryInfo BattleRecoveryInfo
+	{
+		get => SVSim.BattleEngine.Ambient.BattleAmbient.Current?.RecoveryInfo ?? _battleRecoveryInfoFallback;
+		set
+		{
+			var c = SVSim.BattleEngine.Ambient.BattleAmbient.Current;
+			if (c != null) c.RecoveryInfo = value;
+			else _battleRecoveryInfoFallback = value;
+		}
+	}
