# Theriapolis — `CodexUI` Module
## Design & Implementation Plan for a Custom MonoGame UI Layer

**Status:** Proposed. Targets the codebase state as of Phase 5 M5 complete
(329 tests green, Myra 1.0.0.204 still in tree, character creator written
against Myra in [CharacterCreationScreen.cs](Theriapolis.Game/Screens/CharacterCreationScreen.cs)).

**Audience:** A future Claude Code session implementing this module. The
session won't have any of the conversation history that produced this plan
— this document is the complete brief.

**Goal:** Replace the Myra-built character creator with a custom MonoGame
SpriteBatch-based UI module that **exactly mimics the visual design** from
Claude Design's React prototype. Build the module so it can be reused for
future stylized screens (title, inventory, save/load showcase). Keep Myra
in tree for utility screens (pause menu, slot picker) — this is a hybrid,
not a replacement.

---

## 1. Visual source of truth

The complete visual reference is at:

```
_design_handoff/character_creation/from_design/
├── README.md             — design's own integration notes
├── index.html            — full HTML + CSS (the `<style>` block is the design system)
├── src/                  — React JSX components (data flow, state model)
└── data/                 — JSON content (identical to Content/Data/)
```

**The agent must run the React prototype before starting:**

```bash
cd _design_handoff/character_creation/from_design
python3 -m http.server 8000
# open http://localhost:8000/
```

Click through every step. Observe the parchment background, gilded card
borders, serif display fonts (Cinzel for headings, Cormorant Garamond for
body), monospace tags (JetBrains Mono), the stepper's locked/active/complete
states, the card hover/selected states, the bonus pills with sourced
popovers, the drag-drop ability slots, the trait hover popovers. **Every
visual detail in `CodexUI` should match what the React prototype shows.**

The CSS variables block at the top of `index.html`'s `<style>` is the
authoritative color/typography/spacing reference. See §A1 below for the
extracted token table.

The current Myra port is in
[Theriapolis.Game/Screens/CharacterCreationScreen.cs](Theriapolis.Game/Screens/CharacterCreationScreen.cs)
— the *behavior* and *state model* port faithfully (validation, step
gating, click-pick-place stat assignment, auto-assign, roll history). The
*visual* port does not. Read it for behavior, not appearance.

---

## 2. Goals & non-goals

### Goals

1. **Pixel-perfect visual match to the React design** — parchment
   background, gilded card frames with selected-state glow, serif display
   fonts, codex header layout, stepper with ✕/→/✓ marks, card grids with
   hover and selected states.
2. **Reuse the existing MonoGame SpriteBatch rendering stack** — same
   `SpriteBatch`, same `SpriteFont` system, same content-pipeline patterns
   the world/tactical renderers use. No new framework dependency.
3. **Co-exist with Myra**, which stays in tree for utility screens (pause
   menu, save/load slot picker). The Game1 screen stack treats `CodexUI`
   screens and Myra screens identically — both implement `IScreen`.
4. **Reusable primitive set.** The character creator is the first
   deliverable but not the only one. Title screen, inventory, save/load
   showcase will all call into the same `CodexUI` primitives.
5. **Runtime-checkable assets.** Missing fonts or textures should fail
   loudly at content-load time with a clear message, not at first
   render. Same loud-fail discipline as `ContentLoader`.

### Non-goals

- **Replacing Myra entirely.** Pause menu, slot picker, debug overlays
  stay Myra. They don't need to be beautiful.
- **A general-purpose UI framework.** This is a small set of widgets
  hand-tailored to the Theriapolis codex aesthetic. It is not meant to
  scale to arbitrary game UIs.
- **Cross-platform mobile/touch.** Desktop only — same as the existing game.
- **Animations.** A subtle hover transition is fine; full motion design
  (e.g. card-flip transitions, ink-bleed entrances) is out of scope.
- **Localization / RTL.** English LTR only. The font and layout choices
  assume English.
- **Accessibility passes** (screen reader, high-contrast mode, reduced
  motion). Worth noting as a future concern; not in v1.

---

## 3. Stack & constraints

- **MonoGame DesktopGL 3.8.2** (already in `Theriapolis.Game.csproj` and
  `Theriapolis.Desktop.csproj`).
- **C# 12 / .NET 8** (`<LangVersion>12</LangVersion>`).
- **`Theriapolis.Core` must remain MonoGame-free** — enforced by
  `Architecture/CoreNoDependencyTests.cs`. All `CodexUI` code lives in
  `Theriapolis.Game/`.
- **Existing screen contract** — `Theriapolis.Game/Screens/IScreen.cs`:
  `Initialize(Game1)`, `Update(GameTime)`, `Draw(GameTime, SpriteBatch)`,
  `Deactivate()`, `Reactivate()`. New `CodexScreen` base class implements
  this.
- **Content pipeline** — Desktop project's `.csproj` already wildcards
  `..\Content\Gfx\**` to output. Add a new `Content/Gfx/codex/` subfolder
  for the new asset set. Desktop `RuntimeIdentifiers` currently include
  `win-x64; linux-x64; osx-x64; osx-arm64` — assets must be cross-platform
  PNGs, no platform-specific paths.
- **Fonts** — see §6 for two options (MonoGame Content Pipeline vs
  SpriteFontPlus / FontStashSharp). Pick one before starting.

---

## 4. Module file layout

```
Theriapolis.Game/
  CodexUI/
    Core/
      CodexScreen.cs            // base class — IScreen + widget tree + input dispatch
      CodexWidget.cs            // base class for every widget; bounds, hover, click, parent ref
      CodexLayout.cs            // layout helpers: row, column, grid, padding, alignment
      CodexInput.cs             // wraps MouseState + KeyboardState; edge-detect, hover delta
      CodexTheme.cs             // color tokens, density tokens, font tokens (see §A1)
      NineSlice.cs              // 9-slice texture renderer for scalable bordered panels
      Atlas.cs                  // strongly-typed loader for the codex asset set
    Widgets/
      CodexLabel.cs             // wrapped serif text, multi-line, alignment
      CodexButton.cs            // primary / ghost / small variants per the design
      CodexPanel.cs             // 9-sliced parchment panel with optional title bar
      CodexCard.cs              // selectable card with hover + selected glow + click handler
      CodexChip.cs              // trait / skill / language pill (multiple subtypes)
      CodexBonusPill.cs         // small +N / −N pill with sourced popover
      CodexStepper.cs           // 7-step horizontal stepper with locked/active/complete states
      CodexAbilityRow.cs        // ability slot + drop target + final-score readout + bar
      CodexPoolDie.cs           // draggable d20-like value tile for the stat pool
      CodexCheckboxRow.cs       // skill row with check/locked/unavailable states
      CodexTextBox.cs           // single-line text input (for the name field)
      CodexHoverPopover.cs      // floating popover with title + description + tag (trait hover)
      CodexOrnamentRule.cs      // horizontal hairline rule with central ornament glyph
    Screens/
      CodexCharacterCreationScreen.cs  // the wizard, ported from Myra
      CodexAside.cs                    // right-column live summary panel
    Steps/
      StepClade.cs              // one file per step, mirrors steps.jsx
      StepSpecies.cs
      StepClass.cs
      StepBackground.cs
      StepStats.cs              // most complex; drag-drop ability assignment
      StepSkills.cs
      StepReview.cs
    Drag/
      DragDropController.cs     // captures source on TouchDown, follows cursor with ghost,
                                // resolves drop on TouchUp; Escape cancels
```

**14 widget files + 7 step files + 6 core files + 2 screen files = ~29
new C# files.** Plan for ~3000 lines of code total (rough order of
magnitude — codex aesthetic widgets are mostly drawing logic + state).

---

## 5. Visual primitive catalog

Each row maps a React design element → the C# `CodexUI` widget that
implements it. The agent should keep this table next to the design open in
a browser as they implement.

| React selector / component | CodexUI widget       | Notes |
|----------------------------|----------------------|-------|
| `body { background: parchment }` | `CodexScreen` background | Tile a parchment PNG over the full viewport |
| `.codex-header` (title + folio sub) | `CodexLabel` (display font) | Two-line stacked label, serif large + small |
| `.stepper` row              | `CodexStepper`       | 7 step bullets with `locked / active / complete` visual states; ✕ / Roman / ✓ marks |
| `.step.active`              | `CodexStepper` active state | Gilded ring around the bullet, ink-darkened name |
| `.card`                     | `CodexCard`          | 9-sliced parchment frame; hover = subtle gild halo; selected = strong gild halo |
| `.card.selected`            | `CodexCard.IsSelected` | Stronger gild halo + corner ornament |
| `.scent-aura` overlay       | `CodexCard.SpecialAura` | Optional radial gild glow behind the card on hover (clade step only) |
| `.sigil`                    | Sprite atlas          | One PNG per clade (canidae, felidae, etc.) — clade icons |
| `.mod` (`.pos` / `.neg`)    | `CodexBonusPill`     | Small bordered pill, gilded for + and rust-red for − |
| `.trait-chips` row          | `CodexLayout.WrapRow`| Horizontal flex-wrap of chips — needs a flow layout helper |
| `.trait-name` chip          | `CodexChip` (Trait variant) | Bordered pill; hover = full popover with name + description + tag |
| `.skill-chip` (`.from-bg` / `.from-cls`) | `CodexChip` (Skill variant) | Two color variants based on source; popover shows name + governing ability + description |
| `.language-chip`            | `CodexChip` (Language variant) | Italic serif text in a bordered pill |
| `.popover` (rich)           | `CodexHoverPopover`  | Floating panel above/below trigger; viewport-clamped; appears after small hover delay |
| `.btn.primary`              | `CodexButton.Primary`| Filled gild background, ink text, embossed border |
| `.btn.ghost`                | `CodexButton.Ghost`  | Transparent fill, ink border, ink text |
| `.btn.small`                | `CodexButton.Small`  | Smaller padding/font, used for inline reroll/auto-assign |
| `.pool` row + `.die`        | `CodexPoolDie` + `DragDropController` | Draggable value tile; visual = a stylized small ornamented stone |
| `.ability-row` (full)       | `CodexAbilityRow`    | Composite: name + bonus pill + slot + final score + horizontal progress bar |
| `.slot` (drop target)       | `CodexAbilityRow.Slot` | Empty = dashed border; filled = gilded; drop target tracked by DragDropController |
| `.bar` + `.bar-fill`        | `CodexAbilityRow.ProgressBar` | Inset 9-slice; fill colored by relative score |
| `.skill-grid-by-ability`    | `CodexLayout.Grid`   | 3-column grid; each column = `.skill-group` |
| `.skill-group`              | `CodexPanel` (no border, just heading) | Heading is the ability long-name + short-tag |
| `.skill-row`                | `CodexCheckboxRow`   | `[locked]` / `[unavailable]` / `[checked]` / default; click toggles when class option |
| `input[type=text]` (name)   | `CodexTextBox`       | Single-line, serif italic, gilded underline |
| `.review-grid` blocks       | `CodexPanel`         | Each block = small panel with heading + content + `Edit ›` link |
| `.kit-grid` items           | `CodexLayout.WrapRow` of `CodexChip` | One chip per starting-kit item; equipped items get a corner mark |
| `.divider` / horizontal rule | `CodexOrnamentRule` | Two hairlines flanking a small central diamond glyph |

Anything in the React design that doesn't appear above (e.g. the Tweaks
panel, the fonts dropdown) is **out of scope** — the wizard ships with
one fixed theme baked in.

---

## 6. Asset list

### 6.1 Textures (PNG, sized for 1× rendering at the target window resolution)

Place under `Content/Gfx/codex/`. Add the directory pattern to
`Theriapolis.Desktop.csproj`'s `<Content Include>` block (already wildcards
`Gfx/**`).

| Asset | Size | Purpose | Notes |
|---|---|---|---|
| `parchment_bg.png`         | 512×512 (tileable) | Full-viewport background | Subtle wear pattern; tiles seamlessly |
| `parchment_card.png`       | 192×192 9-slice    | Card body fill | 24 px corners, 16 px inner edges |
| `gild_frame.png`           | 64×64 9-slice      | Card border (default) | 16 px corners, gilded line |
| `gild_frame_selected.png`  | 64×64 9-slice      | Card border (selected) | Thicker gild + inner glow |
| `gild_button_primary.png`  | 96×32 9-slice      | Primary button background | Gild fill with embossed bevel |
| `ink_button_ghost.png`     | 96×32 9-slice      | Ghost button border | Ink line, no fill |
| `wax_seal.png`             | 64×64 sprite       | Confirm button accent | Optional; red wax with crest |
| `ornament_diamond.png`     | 16×16 sprite       | Center of horizontal rule | Small gold diamond |
| `stepper_bullet_locked.png`| 24×24 sprite       | Stepper bullet (locked)  | ✕ over greyed parchment |
| `stepper_bullet_active.png`| 24×24 sprite       | Stepper bullet (active)  | Gild filled with Roman numeral |
| `stepper_bullet_done.png`  | 24×24 sprite       | Stepper bullet (complete)| Gild ring with ✓ |
| `chip_trait.png`           | 96×24 9-slice      | Trait chip background    | Subtle parchment fill, ink border |
| `chip_skill_bg.png`        | 96×24 9-slice      | Skill chip — from background | Gild edge variant |
| `chip_skill_class.png`     | 96×24 9-slice      | Skill chip — from class | Standard ink edge variant |
| `chip_language.png`        | 96×24 9-slice      | Language chip            | Italicized rendering, ink border |
| `pool_die.png`             | 32×40 sprite       | Stat pool tile           | Stylized small standing stone with engraved face |
| `slot_empty.png`           | 64×40 9-slice      | Empty ability slot       | Dashed ink border |
| `slot_filled.png`          | 64×40 9-slice      | Filled ability slot      | Gilded border, parchment fill |
| `bar_track.png`            | 16×8 9-slice       | Progress bar track       | Inset shadow |
| `bar_fill.png`             | 16×8 9-slice       | Progress bar fill        | Gilded gradient |
| `popover_bg.png`           | 96×96 9-slice      | Hover popover background | Parchment with darker ink border, drop shadow |
| `clade_sigil_canidae.png`  | 48×48 sprite       | Canidae icon             | Wolf-head heraldry mark |
| `clade_sigil_felidae.png`  | 48×48 sprite       | Felidae icon             | Lion-head heraldry mark |
| `clade_sigil_mustelidae.png` | 48×48 sprite     | Mustelidae icon          |  |
| `clade_sigil_ursidae.png`  | 48×48 sprite       | Ursidae icon             |  |
| `clade_sigil_cervidae.png` | 48×48 sprite       | Cervidae icon            |  |
| `clade_sigil_bovidae.png`  | 48×48 sprite       | Bovidae icon             |  |
| `clade_sigil_leporidae.png`| 48×48 sprite       | Leporidae icon           |  |

**Total: 27 asset files.** All can be hand-painted in Aseprite or
generated via Pixellab (the Tile Generation tooling already in this repo
— see `theriapolis-tile-generation-handoff.md` for the existing prompt
patterns). The 7 clade sigils are the most art-direction-sensitive
authoring task; the rest are repeating border patterns and small icons.

### 6.2 Fonts

The design uses **Cinzel** (small caps display), **Cormorant Garamond**
(serif body), **JetBrains Mono** (small caps tags), with **EB Garamond**
and **Spectral** as alternates. All five are available on Google Fonts as
TTF.

Pick **one** of two integration paths:

**Path A: MonoGame Content Pipeline** (preferred for shipped games)
- Install MonoGame Content Builder Tool: `dotnet tool install -g
  dotnet-mgcb-editor`
- Add `Content.mgcb` to `Theriapolis.Desktop` and reference it in the
  `.csproj` (commented-out reference is already there from Phase 0).
- For each font + size pair (Cinzel-Bold-32, Cinzel-Bold-18,
  CormorantGaramond-Italic-14, etc.), add a `SpriteFont` entry to
  `Content.mgcb` with the source TTF and the desired character set.
- The `.mgcb` build produces `.xnb` files that MonoGame's `ContentManager`
  loads at runtime.
- **Pros:** baked atlases, fastest runtime load, no extra runtime deps.
- **Cons:** requires `mgcb` tool installed, fixed font sizes (one .xnb per
  size).

**Path B: SpriteFontPlus / FontStashSharp** (faster iteration)
- Add NuGet: `FontStashSharp.MonoGame`.
- Load TTF files at runtime, render glyphs into a runtime-managed atlas.
- Any size at any time without rebuilding content.
- **Pros:** no content pipeline setup, runtime size flexibility.
- **Cons:** extra runtime dependency, slightly slower first-render per new
  glyph/size, atlas churn risk if sizes vary widely.

**Recommendation:** Path B for v1 (faster iteration during the styling
pass), revisit Path A if profiling shows atlas churn.

In either case, register the loaded fonts on a `CodexTheme.Fonts` static
table keyed by semantic name:

```csharp
public static class CodexFonts {
    public static SpriteFont DisplayLarge;   // Cinzel-Bold-32  (codex header)
    public static SpriteFont DisplayMedium;  // Cinzel-Bold-22  (h2)
    public static SpriteFont DisplaySmall;   // Cinzel-Bold-16  (h3)
    public static SpriteFont SerifBody;      // CormorantGaramond-Regular-16
    public static SpriteFont SerifItalic;    // CormorantGaramond-Italic-14
    public static SpriteFont MonoTag;        // JetBrainsMono-Medium-10
    public static SpriteFont MonoTagSmall;   // JetBrainsMono-Medium-9
}
```

Loaded once at game startup; all widgets read from this table.

---

## 7. Theme tokens

Extract from the React design's `:root` CSS variables. The agent should
reproduce these exactly. See appendix §A1 for the full table; here's the
canonical names the C# code should use:

```csharp
public static class CodexColors {
    // Backgrounds
    public static Color Bg          = HexColor("#f3ead4");  // parchment
    public static Color BgDeep      = HexColor("#e8dcb8");  // worn parchment shadow
    public static Color Panel       = HexColor("#ede0bf");  // card fill
    public static Color PanelInset  = HexColor("#e3d4a8");  // sunken inner panel
    // Inks
    public static Color Ink         = HexColor("#2a1f12");  // primary serif text
    public static Color InkSoft     = HexColor("#5a4a32");  // secondary text
    public static Color InkMute     = HexColor("#8a7858");  // tertiary text
    // Accents
    public static Color Gild        = HexColor("#a47118");  // gilded borders + select halo
    public static Color GildBright  = HexColor("#d4a23e");  // hover halo / pressed primary
    public static Color Seal        = HexColor("#8a2818");  // wax-red, danger / negative bonus
    public static Color Rule        = HexColor("#c8b88a");  // hairline rule color
}

public static class CodexDensity {
    public const int RowGap     = 6;
    public const int ColGap     = 8;
    public const int CardPad    = 14;
    public const int PanelPad   = 16;
    public const int ChipPad    = 6;
    public const int ButtonPad  = 10;
}
```

Density tokens correspond to the React design's `--gap` family; CardPad
and PanelPad let cards and aside panels keep consistent breathing room.

---

## 8. Layout primitives

The widget tree is hand-built in C# (no XAML). Every widget exposes:

```csharp
public abstract class CodexWidget {
    public Rectangle Bounds { get; protected set; }   // screen-space, set by parent during layout
    public bool      Visible { get; set; } = true;
    public bool      Enabled { get; set; } = true;
    public CodexWidget? Parent { get; set; }

    public virtual Point Measure(Point available);    // returns desired size
    public virtual void Arrange(Rectangle bounds);    // sets Bounds; recurses to children
    public virtual void Update(GameTime gt, CodexInput input);
    public virtual void Draw(SpriteBatch sb, GameTime gt);

    // Hit-testing helpers
    public bool ContainsPoint(Point p) => Bounds.Contains(p);
}
```

Layout containers are widgets that arrange their children:

- **`Column`** — vertical stack with `Spacing` (default `RowGap`), optional
  `Padding`, optional `HAlign` (left / center / right / stretch).
- **`Row`** — horizontal stack with same parameters; default `ColGap`.
- **`Grid`** — fixed-column grid; takes `Columns` (count) and a list of
  child widgets, lays them left-to-right then wraps.
- **`WrapRow`** — flow layout: lay children left-to-right, wrap to next
  line when width exceeded.
- **`Padding`** — single-child decorator that adds breathing room.
- **`Center`** — single-child decorator that centers the child horizontally
  and vertically within its assigned bounds.

Implementation note: do **measure-then-arrange** in two passes to support
content-driven sizing (a card sizes to fit its name + chips). Avoid React-
style flex math; just stack and clamp.

---

## 9. Input + state primitives

`CodexInput` wraps `MouseState` + `KeyboardState` and produces edge-detected
events:

```csharp
public sealed class CodexInput {
    public Point MousePosition { get; private set; }
    public Point MouseDelta    { get; private set; }
    public bool  LeftButtonDown { get; private set; }
    public bool  LeftJustPressed { get; private set; }
    public bool  LeftJustReleased { get; private set; }
    public bool  RightJustPressed { get; private set; }

    public bool  KeyJustPressed(Keys k);
    public bool  KeyDown(Keys k);
    public string TextEnteredThisFrame { get; private set; }   // for CodexTextBox
}
```

`CodexScreen.Update` calls `_input.Tick()` once per frame, then dispatches
to the widget tree. Each widget tracks its own hover/pressed state from
`input.MousePosition` against `Bounds`.

For hover popovers (`CodexHoverPopover`):
- Track `_hoverEnterTime` per widget; show popover after `HoverDelay = 0.4s`.
- On show, position popover relative to trigger widget; clamp to viewport
  edges; flip above/below if not enough room below.
- Hide on `MouseLeft` from both trigger and popover bounds.

For text input (`CodexTextBox`):
- Subscribe to `Game1.Window.TextInput` event in the screen's
  `Initialize`; route incoming `char` to the focused TextBox via
  `CodexInput.TextEnteredThisFrame`.
- Handle Backspace, Enter, arrow keys via `KeyJustPressed`.

---

## 10. Drag-and-drop

`DragDropController` is a screen-level helper that owns the drag lifecycle:

```csharp
public sealed class DragDropController {
    public bool IsDragging { get; private set; }
    public object? Payload { get; private set; }      // what's being dragged
    public CodexWidget? GhostWidget { get; private set; }  // visual that follows the cursor

    public void BeginDrag(object payload, CodexWidget ghost);
    public void Update(GameTime gt, CodexInput input);   // position ghost; detect drop or cancel
    public void Draw(SpriteBatch sb);                    // draw ghost on top of everything else

    public event System.Action<object, Point>? OnDrop;   // (payload, dropPositionScreen)
    public event System.Action<object>?         OnCancel;
}
```

Source widgets call `controller.BeginDrag(payload, ghost)` on `LeftJustPressed`
when their bounds contain the cursor. Drop targets register interest with
the controller; on `LeftJustReleased`, the controller hit-tests
registered targets at the drop position and fires `OnDrop`.

Escape cancels: in `Update`, if `IsDragging && input.KeyJustPressed(Keys.Escape)`,
call `OnCancel` and clear the drag.

For the character creator's stat pool:
- Source: each `CodexPoolDie` has a `payload = { from: "pool", value: int, idx: int }`.
- Drop target: each `CodexAbilityRow.Slot`.
- Slot-to-pool drop: the pool itself registers as a drop target.
- Slot-to-slot drop: the slot is both source and target.

Match the payload shape from the React design's `handleDrop` /
`dropToPool` functions in `steps.jsx` so the logic is portable.

---

## 11. Character creator port

Once the primitives exist, the character creator port is mostly
"translate `steps.jsx` to C# screen + step files." The state model is
already implemented in the existing
[CharacterCreationScreen.cs](Theriapolis.Game/Screens/CharacterCreationScreen.cs)
— port that state model verbatim, swap the Myra widgets for `CodexUI`
widgets.

Steps map 1:1 to the design's `steps.jsx`:

| React component | C# step file | Key widgets |
|---|---|---|
| `StepClade`      | `Steps/StepClade.cs`      | Predator/prey section headers, `CodexCard` grid with `CodexBonusPill` mods + `CodexChip` traits + clade sigil sprite |
| `StepSpecies`    | `Steps/StepSpecies.cs`    | 3-per-row grid filtered by clade; size + speed + mods + traits |
| `StepClass`      | `Steps/StepClass.cs`      | 2-per-row grid with `★ Suits Clade` ribbon overlay; level-1 features as chips |
| `StepBackground` | `Steps/StepBackground.cs` | 2-per-row grid; flavor body text + feature chip + skill chips |
| `StepStats`      | `Steps/StepStats.cs`      | Method tabs, pool row of `CodexPoolDie`, six `CodexAbilityRow`s with drag-drop, roll history line |
| `StepSkills`     | `Steps/StepSkills.cs`     | 3-column grid by ability; `CodexCheckboxRow` per skill |
| `StepReview`     | `Steps/StepReview.cs`     | Name input (`CodexTextBox`), `CodexPanel` summary blocks with `Edit ›` buttons, starting-kit chip wrap |

The `CodexAside` panel mirrors the React `<Aside />` from `app.jsx` — five
summary blocks (Name, Lineage, Calling+History, Abilities, Skills) plus
the optional Selected detail line.

The wizard validation, step gating, and "Confirm & Begin" handoff to
`WorldGenProgressScreen` are unchanged from the existing implementation —
copy that logic verbatim.

---

## 12. Integration with the existing screen system

### 12.1 Game1 changes

Game1 already pushes `CharacterCreationScreen` on the new-game flow (see
[TitleScreen.cs](Theriapolis.Game/Screens/TitleScreen.cs)). The change:

```csharp
// In TitleScreen.OnNewWorldClicked, the only diff is the screen type:
_game.Screens.Push(new CodexUI.Screens.CodexCharacterCreationScreen(seed));
```

The old Myra `CharacterCreationScreen.cs` can be **deleted** when the
new screen ships and its tests pass — it's a 1:1 functional replacement.

### 12.2 Asset loading

Add a single `CodexAtlas` instance owned by `Game1`:

```csharp
// In Game1.LoadContent (or whatever the existing pattern is):
CodexAtlas.LoadAll(GraphicsDevice, ContentDataDirectory);
```

`CodexAtlas` loads every PNG in `Content/Gfx/codex/` and exposes them as
strongly-typed properties. The fonts load from the chosen path (mgcb or
FontStashSharp) at the same time.

Loud-fail on missing assets:

```csharp
// In CodexAtlas.LoadAll:
foreach (var name in REQUIRED_ASSETS) {
    string path = Path.Combine(gfxDir, "codex", name);
    if (!File.Exists(path))
        throw new FileNotFoundException($"Codex asset missing: {path}");
}
```

### 12.3 Coexistence with Myra

The Myra screens (`PauseMenuScreen`, `SaveLoadScreen`, etc.) keep using
`Myra.Graphics2D.UI.Desktop`. The new `CodexScreen` base class doesn't
touch Myra at all. The screen stack treats them identically — both
implement `IScreen`. No conflicts.

A reasonable convention: `CodexUI` namespaces live under
`Theriapolis.Game.CodexUI.*`, Myra-using screens live in
`Theriapolis.Game.Screens.*` (existing). The two systems never share
widget types.

---

## 13. Milestones

Suggested implementation order. Each milestone is a ship point — branch
mergeable, tests passing, something visually demonstrable.

**M-A — Core + atlas + fonts (1–2 days).**
- Implement `CodexWidget`, `CodexLayout`, `CodexInput`, `CodexTheme`.
- Implement `NineSlice` renderer.
- Implement `CodexAtlas` with all 27 asset slots — using **placeholder
  PNGs** (solid colors with text labels) so the structure works before
  art is authored.
- Load fonts via chosen path.
- Build a test screen that draws a parchment background + 9-sliced card
  + a label so end-to-end pipeline is verified.

**M-B — Atomic widgets (2–3 days).**
- `CodexLabel`, `CodexButton` (3 variants), `CodexPanel`, `CodexChip` (3
  variants), `CodexBonusPill`, `CodexOrnamentRule`, `CodexTextBox`.
- Each widget is verified in the test screen.

**M-C — Composite widgets (2 days).**
- `CodexCard` (with hover + selected states), `CodexStepper`,
  `CodexCheckboxRow`, `CodexAbilityRow`, `CodexPoolDie`,
  `CodexHoverPopover`.
- `DragDropController`.

**M-D — Character creator port (3–4 days).**
- `CodexCharacterCreationScreen` shell + `CodexAside`.
- All 7 step files.
- Wire to existing `CharacterBuilder` (no changes there).
- Replace `TitleScreen.OnNewWorldClicked` to push the new screen.

**M-E — Asset authoring + visual polish (2–4 days, mostly art).**
- Replace placeholder PNGs with finished art (parchment textures,
  gilded borders, clade sigils, ornament glyphs, wax seal).
- Final color/spacing pass against the React design with the prototype
  open side-by-side.
- Smoke-test by running the game and clicking through the wizard.

**M-F — Cleanup (½ day).**
- Delete the old Myra `CharacterCreationScreen.cs`.
- Update `_design_handoff/character_creation/IMPLEMENTATION_STATUS.md`
  to reflect "now matches the design."
- Update `CLAUDE.md` if any project conventions changed.

**Total: ~10–14 days.** Front-loaded with infrastructure (M-A through
M-C); the wizard port (M-D) is mostly translation; the visual polish
(M-E) is where the codex aesthetic lands.

---

## 14. Testing strategy

### 14.1 What to test

- **Unit-test `NineSlice`.** Given a source rect and a target rect,
  verify it produces the right 9 sub-quads. No MonoGame dependency
  needed if the math is in a static helper.
- **Unit-test `CodexLayout` containers.** Measure + arrange of nested
  rows and columns produces expected child bounds.
- **Unit-test `DragDropController` lifecycle.** Begin → update positions
  → drop on registered target → fires OnDrop with correct payload.
- **Smoke-test the screen.** Headless render — push the screen,
  call Update + Draw a few frames, assert no exceptions. (Existing test
  pattern: see `Theriapolis.Tests/` — add a new `CodexUI/` folder.)

### 14.2 What NOT to test

- Pixel-exact render output. Visual regression testing on rendered
  output is fragile; defer to manual visual check against the React
  prototype.
- Font kerning / metrics. Trust the font system.

### 14.3 Architecture test

Add to `Theriapolis.Tests/Architecture/CoreNoDependencyTests.cs`: assert
that `Theriapolis.Game.CodexUI.*` types do **not** reference Myra (so
we don't accidentally cross-contaminate). The reflection check is the
same pattern as the existing Core-no-MonoGame check.

---

## 15. Open questions

These should be answered before M-A starts:

1. **Font path: A or B?** Recommend B (FontStashSharp) for v1 iteration
   speed, switch later if profiling demands.
2. **Asset authoring source.** Hand-painted in Aseprite, or generated via
   the Pixellab tooling already in the repo? The clade sigils benefit
   from hand-direction; the 9-slice borders are simple enough to author
   either way.
3. **Window resolution assumption.** The React design assumes ~1280×800
   minimum; what's the game's target? Confirm before authoring assets at
   1× scale (vs 2× or 3×).
4. **Theme switching.** The design's Tweaks panel supports light/dark/
   custom themes. The plan assumes the game ships with one fixed theme
   (the parchment codex). Confirm before adding theme-switching support
   to `CodexTheme`.
5. **Animation.** The plan calls hover transitions out of scope. Confirm
   no card-flip / step-transition animations are wanted in v1.
6. **Aside detail panel scope.** The current Myra port uses a click-to-show
   detail line in the aside panel as a substitute for hover popovers. With
   real hover popovers (`CodexHoverPopover`), is the aside detail line
   still wanted? Probably yes for keyboard navigation (no hover when no
   pointer).

---

## 16. What this plan does **not** cover

- Title screen redesign. Save for after the character creator ships and
  the primitives are proven.
- Inventory screen redesign. Same — defer until M3+ inventory mechanics
  shake out and the codex primitives are mature.
- HUD / combat overlay redesign. M5 ships those in Myra; rebuilding them
  with `CodexUI` is a much later concern (different aesthetic — combat
  HUD wants high-contrast functional, not parchment-decorative).
- The pause menu. Stays Myra. It's utility UI.

---

## Appendix A1 — Color & typography tokens (extracted from `index.html`)

Run this to extract the canonical CSS variables yourself:

```bash
grep -E '^\s*--' _design_handoff/character_creation/from_design/index.html | head -40
```

The definitive list at the time of writing:

```css
--bg:          #f3ead4;   /* parchment */
--bg-deep:     #e8dcb8;
--panel:       #ede0bf;
--panel-inset: #e3d4a8;
--ink:         #2a1f12;
--ink-soft:    #5a4a32;
--ink-mute:    #8a7858;
--gild:        #a47118;
--gild-bright: #d4a23e;
--seal:        #8a2818;
--rule:        #c8b88a;

--serif-display: 'Cinzel', 'Cormorant Garamond', serif;
--serif-body:    'Cormorant Garamond', 'EB Garamond', serif;
--mono:          'JetBrains Mono', monospace;
```

(If the React design has been updated since this plan was written,
re-extract from `index.html` and update `CodexColors` / `CodexFonts`
accordingly.)

---

*Theriapolis CodexUI Implementation Plan — 2026-04-25*
*Author: Claude (Opus 4.7) for LO, in continuity with the Phase 5 plan series.*
*Implementer: a future Claude Code session — read this whole file before starting M-A.*
