## Summary
First PR in a multi-PR migration of player-facing legacy `Gump` subclasses to the modern `DynamicGump` / `StaticGump<T>` system. Plan covers ~95 files across ~14 PRs by system; this PR is the foundation (smallest, isolated, no cross-refs).
- `VirtueGump` → `DynamicGump`: per-instance virtue hues from `GetHueFor()` and the conditional self/other button block prevent layout caching.
- `VirtueStatusGump` → `StaticGump<VirtueStatusGump>`: layout is identical for every player; only used as a navigation hub.
- `VirtueInfoGump` → `DynamicGump`: dynamic cliloc IDs (`1051000 + (int)virtue`, the description cliloc, and the conditional `1052055`/`1052052` footer) cannot be cached by `StaticGump<T>` — cliloc *numbers* are baked into layout bytes, only HTML/label *text* can be deferred to placeholders.
All three:
- `Singleton => true` (replaces previous instance instead of stacking)
- Constructors made `private`; entry points are static (`RequestVirtueGump`, `DisplayTo`) per the empty-gump rule (CLAUDE.md §13)
- `OnResponse` updated to `in RelayInfo info` modern signature
- `VirtueInfoGump` self-refresh button now uses `_beholder.SendGump(this)` instead of allocating a new instance
- Removed the unused `VirtueGumpItem : GumpImage` nested class — replaced with direct `builder.AddImage(...)` calls; preserves the legacy `class=VirtueGumpItem` attribute for packet parity
The special-cased TypeID for VirtueGump (`BaseGump.cs:86`) is preserved because the type's full name (`Server.Engines.Virtues.VirtueGump`) is unchanged.