04 — UI/UX: The Every Last City Client
This document specifies the Every Last City client. It owns the presentation of a game whose turn has three phases: an Orders phase in which every player writes conditional policies against a board that does not move, a Cascade in which the engine resolves every unit in initiative order with no input at all, and a Reckoning of bookkeeping (10-turn-model.md TM-020). Almost everything distinctive about this client follows from that shape. There is no unit-by-unit prompting loop, no "next unit awaiting orders" key, and no moment at which a player watches a result and then decides what to do next. In their place this document specifies four load-bearing surfaces:
- The Orders board (§4) — issuing and revising orders across many units at once, against a stable board, with a readiness affordance and free revision until the phase closes.
- The Cascade (§5) — a first-class screen. Watching a plan execute, fog-filtered, clustered when large, with step-through and a written after-action report.
- The Dispatch (§9) — attention routing. Seven items out of three hundred units, ranked by the core's integer terms and the client's own adaptive weight.
- The Watch and the Board (§12) — victory made legible continuously rather than announced at the end.
Around those sit the map, the unit and city panels, the Industry and economy screens, the combat preview, and the input, onboarding, accessibility and mobile work that is independent of any of it.
The client is a web client: a Phaser 3 WebGL canvas renders the tile map and nothing else, and every other surface is real DOM (React 18 + TypeScript). The browser is the primary delivery target; Tauri 2 wraps the same bundle for desktop and mobile. §1 fixes that split and the canvas/DOM coordination contract (UX-010 through UX-060) that the rest of the document builds on.
Status: Draft v0.2 · Owner: unassigned · Depends on: docs/design/00-direction.md, docs/design/01-decision-turn-model.md, 01-game-rules.md (map, terrain, vision, fog, the Intelligence Picture, setup), 02-units-and-industry.md (unit schema, the Industry ladder, the production queue), 03-architecture.md (determinism, the order log, views, budgets), 05-multiplayer.md (transport, presence, lobbies, notifications), 06-ai.md (seat disclosure), 07-modding-content.md (localisation, generator schemas, content editing), 08-services-platform.md (accounts and profiles), 10-turn-model.md (phases, Orders, the Cascade, Initiative, Stance, playback), 11-combat.md (the prediction contract and the readout), 12-economy.md (the visible brakes and what the player sees), 13-command.md (the order vocabulary, Formations, the Dispatch), 14-victory.md (the Terms, the Watch, the Board, the Chronicle)
1. Client foundations
UX-010 The client MUST be one TypeScript codebase (@everylastcity/client, strict: true)
composed of exactly two presentation layers, with a strict division of responsibility:
| Layer | Technology | Owns |
|---|---|---|
| Map canvas | Phaser 3, WebGL | The tile map and nothing else: terrain, rivers and installations, units, cities, fog and intelligence regions, on-map overlays (UX-460), order previews, Cascade playback animation (§5), and the minimap |
| Interface | DOM — React 18 + TypeScript, plain CSS | Everything else: menus and screens (§2), the setup wizard and the Terms (§3), the Orders board and its readouts (§4), the after-action report (§5), every HUD region (§7), the unit panel and order-stack editor (§9), the Dispatch (§9), production, Industry and economy screens (§10), the combat preview (§11), the Watch, the Board and the Chronicle (§12), settings and key binds (§13), onboarding and the encyclopedia (§14) |
The split MUST be strict in both directions: the canvas MUST NOT draw buttons, text entry, tables, lists, or dialog chrome, and the DOM MUST NOT draw map content. No third UI technology may be introduced. Where this document says "button", "dialog", "panel", "list", or "table", it means a DOM element; where it says the map "renders", it means the Phaser layer.
Rationale: the game is UI-dense, not action-dense. An order stack editor, a fourteen-column production table, a fourteen-row victory matrix and an encyclopedia are the client's real workload, and a game engine's control toolkit is the wrong instrument for all four. The canvas earns its place only for the tile map, where thousands of sprites must be batched. Confining the engine to the map is also what makes the browser a first-class platform rather than a port (UX-018), and it is what turns accessibility (§15) and data-dense UI (UX-045) from engine-limited compromises into native browser capabilities.
UX-012 Layer composition and z-order. The canvas MUST occupy a single full-bleed layer beneath a single DOM overlay root; the two MUST share one coordinate origin and one resize observer. DOM elements MUST NOT be inserted into the canvas layer and Phaser MUST NOT be asked to manage DOM (no engine-managed DOM containers); the only sanctioned bridges between the layers are the input contract of UX-014 and the projection of UX-016.
Stacking MUST be declared once as a named scale and MUST NOT be worked around with ad-hoc values: map canvas → map-anchored overlays → docked HUD panels → floating panels → modal dialogs and their scrim → context menus → command palette → toasts → tooltips → drag ghosts. Every layer above the canvas MUST live in one stacking context, so a modal always covers a tooltip and a menu always covers a panel. At most one modal scrim may exist at a time.
The DOM overlay root MUST default to pointer-events: none, with interactive subtrees opting back
in, so the map remains directly reachable through the empty space between HUD regions. Every docked
or floating surface MUST publish the screen rectangle it occludes, and all map-centring behaviour
(UX-490, UX-870, UX-980, jump-to-location from any report or Dispatch item) MUST centre on the
visible map area rather than the raw viewport.
UX-014 Input routing and hit-testing. There MUST be exactly one input pipeline, with DOM hit-testing authoritative:
- Pointer input MUST use Pointer Events. An event that lands on an interactive DOM element belongs
to the DOM and MUST NOT reach the map; only events that reach the canvas or a
pointer-events: noneregion of the overlay are map input. The client MUST NOT implement a second, competing hit-test cascade over map entities in the DOM. - Every map drag — pan, drag-box selection (UX-840), the order drag-path (UX-670), geometry authoring (UX-735), stack drag-to-host (UX-770) — MUST use pointer capture, so a drag that crosses a HUD panel or leaves the window completes correctly instead of stranding the interaction.
- Over the map surface only, the client MUST suppress browser defaults that would otherwise
steal the gesture: context menu, text-selection and long-press callout, double-tap zoom,
overscroll/pull-to-refresh, and wheel page-scroll/browser-zoom (
touch-action: none). Over DOM panels these defaults MUST continue to work — native scrolling, native selection, native copy, and the native context menu in text fields are requirements, not oversights (UX-045). - Keyboard commands (§13) MUST dispatch only when focus is on the map surface or on a DOM element that does not itself consume the key. While a text input, textarea, or contenteditable holds focus, every printable character plus Backspace, Delete, Home, End, and the arrow keys MUST go to that field and MUST NOT fire a game command.
- Escape MUST resolve topmost-layer-first in this order: cancel an in-progress map interaction (drawing a Screen line, painting a Survey region, placing March waypoints, picking an attack target, siting a Depot, assembling a Formation) → dismiss the topmost menu, palette, or tooltip → close the topmost dialog → clear the selection (UX-680). Escape MUST always produce a visible effect at the first layer that can consume it.
- Gamepad input (UX-1240) MUST be polled on the render loop and dispatched through the same command dispatcher as keyboard, never by synthesising DOM focus events.
UX-016 Map-anchored UI. Interface that belongs to a map location — the hover location readout (UX-520), unit and city tooltips, the context menu (UX-600), the touch radial menu (UX-1400), the stack/cargo widget (UX-770), the route ETA readout (UX-670), endurance and range callouts (UX-880), Dispatch pins (UX-810), Bellwether markers (UX-1165), and Cascade playback callouts (§5) — is DOM and MUST be positioned by a single world→screen projection exported by the canvas layer. No other code may compute map screen coordinates.
- Anchors MUST be expressed as a world cell or an entity id, never as cached screen pixels.
- Anchored elements MUST re-project in the same frame as the map on every camera change: pan, zoom, LOD tier change (UX-390), wrap-seam crossing (UX-410), window resize, and device-pixel-ratio change. Visible lag between a unit and its label is a defect, not a tolerance.
- On wrapped maps (GR-190) the projection MUST return the instance nearest the camera, so an anchor does not jump across the seam mid-drag.
- Anchored elements MUST flip or shift to stay fully inside the visible map area (UX-012 occlusion rectangles) and MUST NOT be clipped by a HUD panel. When the anchor scrolls off-screen or falls into fog, the element MUST be dismissed, or clamped to the viewport edge with a direction indicator, per element type — never left pointing at nothing.
- Anchored elements MUST be pooled and reused rather than created per frame (UX-050).
Rationale: these surfaces sit on the map but are still menus, tooltips, and readouts. Keeping them in the DOM is what makes them keyboard-operable and screen-reader-readable (UX-1320, UX-1325) — canvas-drawn equivalents would be invisible to assistive technology and would have to reimplement focus, hit-testing, and text layout by hand.
UX-018 Platform delivery. The browser is the primary platform and MUST implement every requirement in this document; no requirement may be satisfied only in a packaged build.
- Browser targets are current evergreen Chromium, Firefox, and Safari, with WebGL2 assumed and a declared degradation path (UX-385).
- Windows, macOS, Linux, iOS, and Android MUST ship as Tauri 2 wrappers around the same client bundle. Platform-specific UI forks MUST NOT exist.
- Platform differences MUST be confined to one narrow capability adapter with a browser implementation and a Tauri implementation, covering at minimum: opening and saving files (saves, archives, turn bundles, content packs), OS notifications, deep links (UX-1440), window/fullscreen control, and clipboard. Feature code MUST depend on the adapter interface, never on the host.
- Where a capability is weaker in the browser, the browser build MUST degrade to a documented in-browser equivalent (download/upload in place of direct filesystem access, Web Push in place of OS notification APIs) rather than omit the feature.
The iOS path carries the tightest constraints of the six targets, because Tauri 2 mobile is young and iOS mandates WKWebView; it is tracked as open question 2 rather than assumed away.
UX-020 The client MUST NOT mutate game state. Every player action MUST be expressed as one or
more order records submitted to @everylastcity/core and appended to the authoritative log
(10-turn-model.md TM-200, TM-210); there MUST be no other path by which player intent reaches the
simulation. All display state MUST be rendered from the core's read-only, per-player fog-filtered
views. Any information shown to a player MUST come from that player's filtered view (01-game-rules.md
GR-1180; 03-architecture.md AR-420) — the UI layer can never leak fogged data. This binds both layers
equally: the canvas draws only what the filtered view contains, and no DOM panel, table, tooltip, or
accessibility affordance (UX-1320) may surface a value the view withholds.
UX-030 The phase MUST be explicit. The client MUST show, at all times and without a click, which
of Setup, Orders, Cascade, Reckoning or Ended the game is in (TM-090), and MUST make each
transition legible rather than incidental. During the Cascade and the Reckoning the client MUST NOT
present any control that implies input is possible, MUST NOT offer a confirmation, an opportunity
prompt, or a mid-resolution decision of any kind, and MUST NOT queue an order for later submission
(TM-050, TM-260).
Rationale: the single most confusing thing a client of this shape can do is accept an input that looks like it changed something and did not. TM-050 forbids a mid-Cascade prompt in the engine; this requirement forbids the interface from implying one exists. Everything a prompt would ask is expressible in advance through Sanctions (§9), and where it genuinely is not, the answer is a richer Sanction vocabulary, not a prompt.
UX-040 No rules arithmetic in the client. Every number the client displays MUST come from a core export. Specifically the client MUST NOT compute, re-derive, approximate, interpolate, or round-trip: Initiative or any of its terms (TM-1080), any combat figure (11-combat.md CB-2470), any economic quantity (12-economy.md EC-050), any victory value (14-victory.md VC-3170), any command-layer decision or score (13-command.md CM-100), or any prediction (CB-2140 — there is exactly one resolution function and the preview is implemented by calling it). Per-mille figures MUST be displayed in per-mille or as a percentage derived from the per-mille value, and MUST NOT be re-derived from a percentage (CB-2480).
Sorting, filtering, grouping and number formatting in the client are display-only: they MUST NOT influence the content or ordering of any order record submitted to the core, so locale-sensitive collation and number formatting can never reach rules computation.
Rationale: a second implementation of any of this arithmetic is a divergence waiting to be discovered by a player mid-game, and the specifications it would diverge from have each gone to deliberate trouble to make one implementation sufficient. The client's job is layout, not physics.
UX-045 Data-dense interface baseline. Every list and table surface in this document MUST be built from real DOM data structures and MUST provide, without per-screen re-invention:
- Virtualised rendering for any list that can exceed roughly 200 rows, so an empire-wide production table, a full roster, or a long Chronicle scrolls at 60 fps and its row count is bounded by the game, not by the UI.
- Native text input —
<input>/<textarea>— for every field the player types into: game and player names (UX-150, UX-200), city and unit renaming (UX-890), Doctrine, Formation and Posting names (§9), map labels (UX-870), chat (UX-620), and every search box. Full IME composition MUST work, including candidate windows, dead keys, and composition cancel; the client MUST NOT intercept keys during composition (UX-014.4). - Native selection and clipboard — text in tables, reports, and dialogs MUST be selectable and copyable, and tabular selections SHOULD copy as tab-separated text.
- Sortable, resizable, and reorderable columns with sticky headers, persisted per profile, plus keyboard row navigation and type-ahead.
- Semantic table markup so screen readers announce row/column context (UX-1320) and browser find-in-page works over rendered rows.
This baseline binds at least: the Roster (UX-800), the Dispatch (UX-810), the production list (UX-920), the Production Overview (UX-1050), the Empire Ledger (UX-1070), the Requisition panel (§9), the Doctrine list (§9), the after-action report (UX-355), the Board and the Chronicle (§12), the Key Bind dialog (UX-1200), the encyclopedia (UX-1290), and the Load Game list (UX-080).
Rationale: this is the specific reason the interface is DOM. A four-hundred-row production table with sortable columns, sticky headers, live filtering, and IME-capable inline renaming is a solved problem in the browser and a bespoke engineering project in a game engine's control toolkit.
UX-050 Performance. The client MUST sustain 60 fps pan and zoom, and MUST render first feedback for any input (selection highlight, route preview, button press state) within 100 ms, at every map size tier of 01-game-rules.md GR-230 — Duel 40×28 through Vast 240×168 — on the project's reference hardware.
The client's stress fixture MUST be a Vast game: 240×168 tiles on a cylinder, 8 players, the GR-230 default city count of 269, and 3,200 live units, which is the largest population any neighbouring document budgets against (14-victory.md VC-310). Because the browser build is the slowest of the six targets, these numbers MUST be measured in the browser build on the reference device; a passing packaged build does not satisfy UX-050.
This replaces a figure inherited from an earlier architecture note — a 1,000×1,000 "Large" map with 10,000 live units — that no longer corresponds to any tier the rules define. GR-230's largest shipped tier is 40,320 tiles, twenty-five times smaller in area, and GR-720's Surface stacking limit of 1 in the open bounds unit density by tiles rather than by ambition. The client is not being asked to hold less; it is being asked to hold the game that exists. See open question 1 for the reconciliation this still owes 03-architecture.md.
Culling bounds entity work; it does not bound tile work. At the low LOD tiers of UX-390 a large fraction of the map is on screen at once, so the budget MUST be stated per tier. At a reference viewport of 1920×1080 CSS px with a Close-tier tile size of 32 CSS px:
| LOD tier (UX-390) | Zoom | Visible tiles | Share of the Vast fixture | Budget |
|---|---|---|---|---|
| Close | 1.0× | ~2,040 (60 × 34) | 5% | 60 fps, full sprites and animation |
| Mid | 0.5× | ~8,200 (120 × 68) | 20% | 60 fps |
| Chip | 0.25× | ~32,400 (240 × 135) | 80% | 60 fps |
| Strategic | 0.125× | all 40,320 | 100% | 60 fps |
Three budgets follow from the two-layer split, and one requirement follows from the table:
- Map layer — draw calls MUST be batched (tilemap batching plus one texture atlas per LOD tier), so frame cost scales with visible tiles and visible entities rather than with total entities.
- Entity aggregation — at the Chip and Strategic tiers, entities MUST be aggregated to one marker per occupied location, sized and counted (UX-390). This is a legibility requirement before it is a throughput one: forty thousand individual dots is not a picture of anything.
- Allocation — the render loop MUST hold per-frame allocation to a bounded, pooled working set. A garbage-collection pause is indistinguishable from a dropped frame, and this is the budget most easily lost to convenience code.
- DOM layer — HUD and panel updates MUST be driven by state change, never re-rendered per frame, and high-frequency values (hover readout contents, route ETA, minimap viewport, playback cursor) MUST be coalesced to at most one DOM update per frame.
A pre-reduced overview raster for terrain below the Chip tier is permitted and SHOULD be used, but is required only above GR-250's tested envelope (any axis beyond 1,024), where per-tile quads stop being affordable. Custom maps up to GR-120's limit of 2,048 per axis MUST still render under UX-400's fixed memory budget, without a frame-rate guarantee.
UX-055 The core MUST run off the render thread. @everylastcity/core MUST execute in a Web
Worker (the same worker under Tauri), with the UI submitting order records and receiving events and
fog-filtered views; the client MUST NOT call the core synchronously from the render loop. Cascade
computation, Reckoning, replay scrubbing, AI thinking, and map generation therefore MUST NOT block
the frame: while the worker is busy the player MUST still be able to pan, zoom, open panels, and read
reports, and the client MUST show progress rather than freezing (UX-375).
Playback never blocks on simulation, because the Cascade is computed to completion before any frame of it is drawn (TM-2240) and the client animates a finished observation stream at its own pace (TM-2200).
UX-060 Disclosure profiles. On first run the player MUST be offered at least three named profiles, each of which sets a coherent bundle of disclosure and interface settings:
| Profile | Sets |
|---|---|
| Field | Initiative Tier 1 (TM-1040); Sanctions shown as the three presets only (CM-1980); Formations, Groups, Postings and Requisitions hidden (CM-1990); overlays reduced to supply, intelligence and victory sites; the Board summarised |
| Campaign | Initiative Tier 2; the four Sanction dials exposed (CM-570); Formations and Doctrines enabled; the full overlay set |
| Staff | Initiative Tier 3; everything enabled, including Postings, Requisitions, Consignments and the command audit trail (UX-895) |
Every individual setting in a profile MUST remain independently adjustable afterward, and a player MUST be able to enable any hidden system immediately rather than earning it (CM-1980).
The Initiative Tier is a rules setting, fixed at setup and unchangeable during a game (TM-1030, TM-1070). A profile therefore proposes a Tier in the setup wizard and MUST NOT be able to change it once play begins; the client MUST make that distinction plain at the point of choosing.
Rationale: the specification set delivers escalating complexity through several independent disclosure mechanisms — the Initiative Tier, the command layer's presets and optional systems, and the Industry ladder's unlock cards. A player should meet one decision about how much game they want, not four. The profile is that one decision, and it is a starting point rather than a mode.
2. Application shell and screens
UX-070 The client MUST provide at least these top-level screens, reachable from a main menu:
| Screen | Contents | Spec owner |
|---|---|---|
| Main Menu | New Game, Load Game, Multiplayer, Editors, Encyclopedia, Settings, Profiles | this doc |
| New Game | the setup wizard and the Terms (§3) | this doc + 14 |
| Load Game | saved and correspondence games, archives, branch restore | this doc + 03/05 |
| Multiplayer | lobbies, correspondence games, invitations | 05-multiplayer.md |
| Map/Scenario Editor | map painting, scenario setup | 07-modding-content.md |
| Content Editor | unit set and ruleset editing | 07-modding-content.md |
| Workshop / Sharing | maps, scenarios, content packs | 07/08 |
| Settings | gameplay, interface, key binds, audio, video, accessibility, profiles | this doc |
| Encyclopedia | the complete unit and terrain reference (UX-1290) | this doc + 02 |
| In-Game | the phase surfaces (§4–§12) | this doc |
UX-080 The Load Game screen MUST list games with display name, map thumbnail, turn number and phase, participating seats and their commit state, game mode, and last-played timestamp; games MUST be keyed by unique id, not by name. It MUST surface archive creation and branch restore per the save model of 03-architecture.md, presenting restore as non-destructive timeline branching (AR-600) rather than as an overwrite.
UX-090 All settings MUST live in the unified Settings screen with per-profile scopes. Audio settings MUST all appear together in one panel; there MUST NOT be a second settings surface reachable only from outside a game.
UX-100 Player colour identity: each account or profile carries a colour used to tint chrome. In game the current seat's colour governs, out of game the profile default governs, and the player MUST be able to override it in settings. Ownership colour on the map is governed separately by UX-210 and UX-1310.
UX-110 The client MUST remain fully functional offline with a local guest profile; no screen in this document may hard-require a cloud account (accounts in 08-services-platform.md). In the browser build this obligation is concrete: the client MUST be installable (PWA) and MUST cache its application shell, the active content pack's data and assets, and the help and encyclopedia content, so a solo or hotseat game can be created and played with no network.
Local persistence in the browser is evictable, which the packaged builds' filesystem storage is not. The client therefore MUST request persistent storage on first save, MUST tell the player plainly when persistence is not granted, and MUST make export-to-file reachable in one step from the Load Game screen, so a cleared browser profile is never the only copy of a game. Per-browser quota and eviction behaviour against real save sizes is open question 5.
3. Setup: the wizard and the Terms
UX-120 Game setup MUST be presented as a guided wizard — Content → Map → Rules → the Terms → Players → Review — and MUST also offer a single-screen advanced view exposing every panel at once for a player who knows what they want.
UX-130 Content selection MUST come first: the ruleset version and the unit set in force
(02-units-and-industry.md US-1340 ships exactly one, standard; further sets come from
07-modding-content.md). Selecting content MUST load that content's suggested rules and Terms into the
later panels, which the setup player may then override individually.
UX-140 Where changing the content selection would overwrite choices the player has manually modified, the wizard MUST show a diff of the incoming suggestions against current choices and offer apply suggestions or keep my overrides. It MUST NOT silently reset.
UX-150 Game naming: the wizard MUST auto-generate a name from a bundled name list, provide a Generate Name control to reroll, and allow manual entry. The name is a display label; identity is the game id (UX-080).
UX-160 The Map panel MUST expose exactly the setup parameters 01-game-rules.md GR-1480 defines —
size, topology, landRatio, terrainMix, cityDensity, minCitySpacing, riverDensity,
roadDensity, neutralDefence, startCities, minStartSeparation, fogEnabled,
intelligenceDecay — each with its stated default, its legal range, and a one-line plain-language
effect.
- Size MUST be offered as the five named tiers of GR-230 (Duel, Small, Standard, Large, Vast) with
their tile counts, supported player counts, default city counts and typical game lengths shown,
plus an explicit
width × heightentry within GR-120's[16, 2048]bounds. - A custom size between tiers MUST be accepted (GR-240). A dimension above 1,024 on either axis MUST be accepted and MUST be labelled as outside the tested performance envelope (GR-250) rather than refused or silently clamped.
- The panel MUST name the platform ceiling of UX-165 beside the dimension fields.
- A parameter combination that cannot satisfy the fairness invariants of GR-1560 MUST be refused
before generation is attempted, naming the parameter to change (GR-1490) — most commonly a
player count too high for the tier, or a
minStartSeparationtoo large for the map. startCitiesMUST be constrained to GR-1500's range and the panel MUST state the derived limit rather than letting the player discover it at generation.fogEnabled = falseandintelligenceDecay = falseMUST be labelled as teaching and analysis settings and MUST be labelled as non-competitive (GR-1630).
UX-165 Platform map-size ceiling. A large map costs the client memory that a browser tab or a mobile WebView may not have. Concretely:
- Advertisement. The client MUST determine the largest map it can hold and MUST expose that as a measured capability value through the adapter of UX-018, never as a guess inferred from the user agent — a capable desktop browser MUST NOT be refused for belonging to the browser class, and a memory-starved desktop MUST NOT be offered a size it cannot hold for belonging to the desktop class.
- Setup. The wizard MUST NOT offer, and MUST NOT accept, dimensions above the advertised ceiling by any route: the dimension fields, a tier button, a generator preset, a one-click preset, a loaded setup file, or the selection of a pre-made map or scenario.
- Explain, never clamp. A size above the ceiling MUST be shown disabled with the reason and a platform that can hold it named, never hidden and never silently reduced. Rewriting a player's typed dimensions is a defect under this requirement.
- Entry, not only creation. The same check MUST run wherever a game is entered rather than created — the Load Game list, an archive restore, an accepted invitation, a turn-bundle open — which MUST list the game and disable entry with the reason stated, rather than loading it and failing partway.
- Every shipped tier is non-negotiable. All five GR-230 tiers MUST be holdable on every supported platform, because cross-play across Windows, macOS, Linux, iOS, Android and the browser is a promise 05-multiplayer.md makes and this ceiling MUST NOT be used to break. A measured ceiling below Vast is a budget defect to be fixed, not a configuration this UI may normalise. The ceiling exists for custom maps beyond GR-250's tested envelope.
UX-170 Generator options: a selected map generator's options MUST be rendered from the plugin's declared schema (07-modding-content.md), and option configurations MUST be savable to and loadable from named presets. The wizard MUST show a live thumbnail preview of the generated map and MUST display the generation seed with copy and share support. Where the generator exhausts its retry budget without satisfying the fairness invariants (GR-1560), the wizard MUST say which invariant failed and offer the parameter that would relax it, rather than reporting a generic failure.
UX-180 The Terms. The victory setup panel MUST present each Track as a card carrying
exactly the Track's name, one plain-language sentence of at most 140 characters, at most three
parameters with their current values, and the three integer ratings Length, Aggression and
Complexity, each in [1, 5] (VC-1910, VC-1920, VC-1940). A fourth or further parameter MUST live
behind an advanced disclosure with a default the player never needs to change.
Enabling, disabling or re-parameterising any Track MUST immediately regenerate the paragraph (UX-185), the estimate (UX-192) and the lint results (UX-195), without a confirmation step and without navigating away from the cards (VC-1950).
UX-185 The generated Terms paragraph (VC-1960) MUST be shown below the cards, MUST state every quantity in natural units rather than per-mille (VC-1980), and MUST be presented to every player before play begins. It MUST be stored verbatim with the game and re-presented unchanged in the end-of-game summary (VC-2000, UX-1195).
Rationale: 14-victory.md calls this the single most valuable object on the setup screen and it is right. It is also the answer to the worst way a multiplayer game can end — an argument about what the rules were. The client's obligation is to make it impossible to start a game without having been shown it.
UX-190 The Rules panel MUST present every rule setting the specification set defines, grouped as common and advanced, with search, plain-language effect text per setting, and a live summary sidebar. The identical summary MUST be reviewable at any time in game. At minimum it MUST expose:
| Setting | Owner |
|---|---|
| Initiative Tier (1 Standard / 2 Veteran / 3 Campaign), fixed for the game | TM-1030, TM-1040, TM-1070 |
Orders deadline and per-player reserve bank, or None |
TM-480, TM-490, TM-500 |
| Fog and intelligence decay | GR-1630 |
| Progress Visibility — Banded / Public / Own Only | VC-2190 |
| Capitulation policy, the Verdict, the Rally, Defiance | VC-2120, VC-2470, VC-2980, VC-3010 |
| Per-player Industry cap | US-900 |
| Neutral city defence | GR-1480 |
UX-192 Estimated length MUST be presented as an integer turn range from the shipped calibration table (VC-2030, VC-2050), never as a qualitative label (VC-2020). Where the table has no cell for the current configuration the estimate MUST be labelled as uncalibrated (VC-2060); where the table was generated against a different ruleset version it MUST be labelled stale (VC-2070).
UX-195 The setup linter MUST run continuously and MUST present every finding with its concrete numeric message naming the setting that would resolve it (VC-2080, VC-2100). A finding of severity Error MUST prevent the game from starting; a Warning MUST be dismissible and MUST be recorded as dismissed in the game record (VC-2090, VC-3050). The client MUST NOT summarise findings into a count — each is a sentence and MUST be shown as one.
UX-200 Player list: each row is a seat carrying its controller — a local human, an invited remote human, or an AI seat with its plugin choice and schema-driven options, savable as named presets. Two or more local humans constitutes hotseat (UX-275).
UX-210 Per-seat colours: each seat MUST carry a foreground, background and text colour, all changeable later in game, and all subject to the colourblind-safe and pattern requirements of UX-1310.
UX-220 Local human seats MUST be bound to a local profile or account, which stores options, key bindings, and game history (08-services-platform.md); a guest profile satisfies this.
UX-230 Per-seat settings MUST include starting cities (within GR-1500's range) and the seat's Industry cap (US-900), plus any handicap the ruleset defines. Every asymmetry between seats MUST be disclosed to every player, in setup and in the in-game rules review, with its direction stated — which seat it helps and by how much (06-ai.md AI-100). The client MUST NOT present a handicap control whose effect it cannot state in one sentence.
UX-240 Add Seat and per-row Remove Seat controls MUST exist, and MUST be disabled with the reason shown when a pre-made scenario fixes the seat list.
UX-250 Complete setup configurations MUST be savable to and loadable from named files; the Play control MUST activate only when the setup is valid and complete and no linter Error stands; and the last-used setup MUST be auto-saved in full — rules, Terms and seats included — and auto-restored on next entry. The wizard SHOULD additionally offer a shareable setup code or URL.
UX-260 The wizard MUST offer one-click presets. The victory presets MUST be exactly those of VC-2120, presented by name with their estimated length; a player who never opens the Terms MUST receive Standard War (VC-2130). Map and seat presets MUST additionally be offered for the common shapes — a two-player duel, a four-player standard, an eight-player Vast correspondence game.
UX-270 Setup help: the wizard MUST expose the manual, a walkthrough of the setup controls themselves, and a launchable interactive tutorial (§14).
UX-275 Hotseat: where two or more seats are local humans, the client MUST take their Orders phases in sequence and MUST fully occlude the previous player's view before handing over (TM-460). The hand-off screen MUST name the incoming player and MUST require a deliberate action to reveal the board. The rules MUST NOT differ from concurrent play in any respect.
4. The Orders phase
This section replaces the unit-by-unit prompting loop that an earlier draft specified. There is no processing queue, no active unit, no Continue key, and no unit that must be dealt with before another can be touched.
UX-280 The Orders board. During an Orders phase the client MUST present a board that does not change (TM-340) and MUST let the player move freely among their units, cities and command objects in whatever order suits them (TM-330), with no forced sequence and no unit blocking access to another. The client MUST NOT present any "next unit needing orders" affordance; routing attention is the Dispatch's job (UX-810) and duplicating it here would reintroduce exactly the per-unit tax the turn model exists to remove.
UX-285 Revision is free, and it is free until the phase closes. Every order MUST be revisable any number of times, at no cost and with no record beyond the log (TM-360). The client:
- MUST NOT present a per-order confirmation step, a commit-per-unit affordance, or any language implying an order becomes fixed before the phase closes;
- MUST provide an undo and redo of the player's own recent order edits within the phase (Ctrl+Z / Ctrl+Y), bounded only by the phase;
- MUST state plainly, at the commit affordance, that after the phase closes there is no undo of any kind.
Rationale: this retires the retraction machinery an earlier draft specified. Under a sequential model, orders executed as they were issued, so undoing an applied order needed a whole mechanism with purity conditions about fog and randomness. Under Orders and the Cascade nothing executes until the phase closes, so the Orders phase is the undo window, and it is total. The client needs no core retraction support at all, which is a real simplification bought by the turn model and worth naming where a reader will look for the old feature.
UX-290 What changed while I was away. On entering an Orders phase the client MUST offer, in one action, a review of the previous Cascade (TM-2230) and the after-action report it produced (UX-355). It MUST additionally mark every unit whose situation changed during that Cascade — a stack level popped, a Fallback Posture adopted (CM-310), a Reflex still active (CM-1400), an activation that fell back to no action (TM-1400) — so that revision starts from what happened rather than from memory.
UX-295 Readiness and commit. A single always-reachable control MUST mark the player ready,
submitting CommitOrders (TM-220), and MUST remain revocable with RevokeCommit while the phase is
open (TM-420). The control MUST make three facts plain:
- committing does not freeze the player's orders and confers no advantage (TM-390) — a committed player may keep editing;
- committing is not required (TM-290) — a player who submits nothing has a well-defined turn, and the client MUST be able to state, per unit, what will happen if they commit nothing;
- the phase closes as soon as everyone expected to act has committed, without waiting for any deadline (TM-400).
UX-300 Presence. The client MUST show, for every other seat, whether it has committed, whether it is currently connected, and how much time remains (TM-440). It MUST NOT show or imply anything about another player's orders, order count, unit selection, camera position, or edit activity (TM-450). Presence MUST be presented as what it is — ephemeral, non-authoritative, and carrying no fact about the game world.
UX-305 Deadline and reserve. Where a deadline exists the client MUST show the remaining phase time and the player's remaining reserve bank (TM-510), MUST warn before the reserve begins to draw down, and MUST state that a timeout costs nothing but the opportunity to revise — no forfeited movement, no production penalty, no initiative penalty (TM-530, TM-540). In solo, two-player and hotseat games the client MUST NOT display a clock at all unless one was explicitly configured (TM-500).
Rationale: a visible countdown in a game that has no deadline is a source of anxiety with nothing behind it. TM-500 makes the common case clockless; the interface has to honour that rather than showing a disabled timer.
UX-310 The initiative readout. For every unit they own the client MUST show the exact integer Initiative and the integer contribution of every term active at the current Tier (TM-1090, TM-1080), MUST recompute and re-display it live as the player changes Stance or Posture (TM-370), and MUST name the terms that are inactive at this Tier so a player understands what the number is not counting (TM-1060).
UX-315 Own activation order. The client MUST show the player's own units in their relative activation order before committing (TM-380). The order MUST NOT be directly reorderable — it is a consequence, not a control — but a Stance or Posture change MUST visibly move a unit within it, so the player learns the lever by using it. The client MUST NOT show the global activation order, any opponent's position in it, the size of the roster, or any opponent's exact Initiative (TM-1120, TM-2120).
UX-320 Enemy initiative MUST be shown only as the three coarse bands Quick / Steady / Slow of TM-1100, only for units the player currently observes, and a remembered contact's band MUST be shown as of the last observation and marked stale (TM-1110). No finer figure may be displayed, derived, or implied anywhere in the client.
UX-325 The commit gate. Before the phase closes the client MUST present a warning summary that never blocks: the count of unresolved Dispatch items above the urgency threshold, default 120, naming the threshold (CM-1820, CM-1830), suppressible for the remainder of the game in one action (CM-1840). It MUST additionally list, as warnings with deep links and never as blocks:
| Warning | Source |
|---|---|
| Units that are Idle — no Posture set | CM-540 |
| Cities with no eligible production order | US-1250 |
| Cities discarding Works because the Yard is capped | US-1240 |
| Postings whose Requisition shortfall is unmet | CM-1250 |
| Cities with a Stalled production order | US-1040, US-1060 |
Each category MUST be individually configurable as warning or ignored. No category may be configurable as blocking.
Rationale: 13-command.md settles this — blocking is patronising, not warning is negligent — and the client should not relitigate it per category. Under persistent orders (TM-280) an unattended unit is not an emergency; it is doing what it was told to do last turn, which is usually right.
UX-330 Rolling planning. Where 10-turn-model.md §18 is enabled, the client MAY allow a player to begin staging a unit's orders for turn T+1 once that unit has activated during turn T's playback (TM-2550). Such staging MUST NOT reach the core before the next Orders phase opens (TM-2570), MUST remain fully revisable when it does (TM-2560), MUST NOT reveal anything the observation stream withholds or any activation index (TM-2580), and MUST be disableable with no rules effect (TM-2590). Where it is enabled the client MUST distinguish staged-early orders from submitted ones unmistakably, because the two look identical and behave differently.
5. The Cascade
This is a first-class screen and the second half of the game's loop. The Orders phase is where a player thinks; the Cascade is where they find out. The design decision that produced this turn model argues that watching a plan execute is a different pleasure from poking a unit rather than a lesser one, and that claim is this section's responsibility to make true.
UX-335 Playback is presentation of a finished result. The Cascade MUST be computed to completion before any frame of it is drawn (TM-2240). The client MUST NOT present playback as a live simulation, MUST NOT offer any control that could alter it, and MUST NOT solicit input during it (TM-050, UX-030).
UX-340 Fog-filtered playback. The client MUST animate exactly the player's compacted observation stream (TM-2150), in the order it is given (TM-2190), and MUST NOT render, infer, or hint at anything the stream withholds — no gap markers, no "something happened" placeholders, no activation indices, no roster size, no count of activations (TM-2120, TM-2160).
Transits MUST be shown as transits. A unit that crossed an observed corridor and left it MUST be seen to cross it, not merely to have arrived somewhere (TM-2090). Rendering only end positions is the one failure that would make playback a lie, and it is also the cheapest corner to cut.
UX-345 Step-through and instant resolution. The client MUST offer:
- instant resolution with playback fully skippable, in every game shape;
- step-through — advancing one observed event at a time under the player's control — in every game shape;
- skip at any point, landing on the identical end state (TM-2270).
Step-through MUST NOT be usable as a means of pausing a game with more than one human player (TM-2260). It is nevertheless available in every game shape, because in a game with two or more humans the whole observation stream has already been transmitted as one payload before playback begins (TM-2170): stepping through a recording pauses nothing, blocks nobody, and reveals nothing the player does not already hold. In solo, two-player and hotseat games instant resolution and step-through are additionally required by TM-2250 as the amendment that protects the common case.
Rationale: step-through is what gives back the tactile pleasure of seeing one result land, and the outcome is fixed before the first frame, so it costs the model nothing. The only thing that must be guarded is the wall clock of other people, and the single-payload rule already guards it.
UX-350 Pacing and clustering. Playback duration MUST NOT scale linearly with the number of observed events (TM-2280). The client MUST hold playback within a bounded budget by clustering — one marker per engagement, one arrow per column, one summary per contested area — with drill-down into any cluster, and MUST always produce the written report of UX-355 regardless of how much was animated. The clustering threshold and the total budget MUST be player-adjustable, including "never cluster" and "always summarise".
The client MUST instrument the skip rate: if more than half of players are skipping playback by turn 100, the answer is more clustering and a better report, never a longer animation.
UX-355 The after-action report. After playback the client MUST present a written report, navigable and copyable (UX-045), containing at least:
| Content | Source |
|---|---|
| Every observed engagement, with its reconciliation line — predicted band against actual result | CB-2440 |
| Every observed city or work that changed hands | TM-2110 |
| The count of the player's own activations that fell back to no action, with a one-action route to those units | TM-2320, TM-1400 |
| Every own unit whose activation slot was skipped, and which reason applied — destroyed, spent after landing, changed owner | TM-2350 |
| The frozen activation order for the player's own units, with each unit's Initiative and every term that produced it | TM-2340 |
| Every observed reaction, naming the reactor where observed | TM-1710, UX-360 |
| Every Attention Event raised by the Cascade, as Dispatch items | CM-1610 |
Every entry MUST jump the map to its location without stealing keyboard focus from the report, so a player can walk a long report entry by entry.
Rationale: the fallback count is the model's key diagnostic (TM-1440) and the player is the person best placed to fix their own instance of it. "Four of your units had nothing they could do" turns an invisible waste into a specific, actionable list, and it teaches the conditional vocabulary faster than any tutorial.
UX-360 Attribution. Every observed event MUST name the unit that acted. Where the actor is not itself observed — a reaction fired from a tile the player cannot see, which 11-combat.md may still make observable — the actor MUST be shown as unknown rather than the event being omitted (TM-2310).
UX-365 Replay. The client MUST be able to replay any past Cascade from the log for the seat being viewed, reproducing that seat's observation stream exactly (TM-2290), with the same step-through, clustering and report. A spectator seat MUST receive a stream constructed by the same rules, for whatever view the game's spectator policy grants (TM-2300).
UX-370 Playback state — pacing, clustering, skip position, step cursor, camera — MUST be client state and MUST NOT contribute to any hash (TM-2330). The client MUST animate at its own pace and MUST NOT be able to affect the simulation by doing so (TM-2200).
UX-375 Waiting. While the Cascade is being computed, or its stream transmitted, the client MUST show progress and MUST remain fully usable: panels, reports, the encyclopedia, the Board and the previous turn's report all reachable (UX-055). A spinner over an inert screen is not acceptable at any phase boundary.
6. Map rendering
UX-380 The map MUST be rendered by the Phaser 3 WebGL layer (UX-010) as a square-grid tilemap with 8-way adjacency (GR-180), supporting continuous smooth pan and zoom with inertial scrolling. Wheel zooms and drag pans, captured per UX-014 so the page never scrolls or browser-zooms underneath the player.
Because every distance in the specification set is Chebyshev (GR-170), every radius the client draws MUST be drawn as the square it is — a vision radius of 4 is a 9×9 box, a Leash of 4 is a 9×9 box, a reaction reach of 1 is a 3×3 box. Drawing a circle for a Chebyshev radius is a defect: it misstates the rule at exactly the corners where a player is deciding.
UX-385 Renderer robustness. The map layer runs on a resource the browser may take away, and the client MUST survive that:
- Context loss — the client MUST handle
webglcontextlost/webglcontextrestored, rebuild GPU resources, and resume with the camera, unsubmitted order edits, open panels and playback position intact. A lost context MUST NOT end the session or lose a single order edit. Because every panel, table and report is DOM (UX-010), a canvas failure costs the map view and not the game: the client MUST stay usable — the Dispatch, production, the Board and the commit control all reachable — while the context is being restored, and MUST say what happened rather than presenting a black rectangle. - Environment changes — device-pixel-ratio change (monitor switch, browser zoom, UX-1330), viewport resize, and orientation change MUST re-render at the new backing-store size without blurring or losing anchor alignment (UX-016).
- Backgrounding — a throttled or suspended animation frame in a hidden tab MUST NOT be treated as a hang, MUST NOT drop queued input, and MUST NOT let elapsed-time animation jump on resume.
- Degradation — if WebGL2 is unavailable the client MUST fall back to WebGL1, and if no WebGL context can be created it MUST fall back to Phaser's Canvas renderer at a declared reduced tier (Chip and Strategic only, per-unit shader tinting replaced by pre-tinted atlases — UX-440) and MUST tell the player what is reduced instead of failing to start. Whether any target platform actually needs the Canvas path is open question 6.
UX-390 Level of detail: the renderer MUST implement at least these zoom tiers, transitioning smoothly:
| Tier | Approx. zoom | Unit presentation | Terrain presentation |
|---|---|---|---|
| Close | ≥ 1.0× | full sprites, state variants, animation, condition badge (UX-1125), Stance and Posture glyphs | full terrain art, rivers, installations |
| Mid | ~0.5× | simplified sprites, no idle animation, ownership tint, condition badge | full terrain art, simplified decals |
| Chip | ~0.25× | flat chips in the owner's colours bearing the unit's short class label; one marker per occupied location, sized and counted | flat terrain colours, roads and rivers as lines |
| Strategic | ≤ 0.125× | owner-coloured markers only; one marker per occupied location with a count; Formation and Group markers under UX-395 | flat terrain colours only |
UX-395 Command View. The client MUST provide a zoom level at which individual unit icons are replaced by Formation and Group markers, each rendering a Posture glyph, a strength bar, an arrow showing current intent, and any Dispatch pins in that area (CM-1880). Every Formation, Group and Posting order MUST be issuable without leaving it (CM-1900). Dispatch items MUST pin to map locations; hovering an item MUST move the camera to it and resolving an item MUST return the camera to where it was (CM-1890).
Rationale: 13-command.md is right that on a large map individual unit icons stop meaning anything, and that this is not a nicety — it is the only zoom level at which a late-game empire is legible. It is also what keeps the Dispatch from becoming a list a player reads instead of a map they look at.
UX-400 Large maps: map data MUST be streamed and culled in chunks so that any map across GR-120's full range renders within a fixed memory budget that does not grow with map size. Scrolling across unloaded chunks MUST show a neutral placeholder and MUST NOT stall the frame. The budget MUST cover GPU texture memory as well as heap, MUST be sized for the mobile WebView case, which is the tightest of the six targets, and exceeding it MUST evict distant chunks and never crash the context (UX-385).
UX-410 Wrap rendering: on a cylinder or torus map (GR-190) panning MUST be seamless across
each wrapped edge, with entities drawn on both sides of the seam, and the minimap MUST indicate the
wrap. The polar impassable strip a cylinder map carries (GR-220) MUST be visually distinct from
ordinary terrain so a player does not plan a march into it.
UX-420 Fog and the Intelligence Picture. The client MUST distinguish the three tile states of GR-1120 — Unexplored (opaque), Explored (terrain known, contents from memory), Observed (current and complete) — and MUST discharge GR-1190 and GR-1870 in full:
- Remembered is never mistakable for observed. Every remembered unit, garrison, installation and ownership MUST be rendered distinctly from a current observation, and MUST carry its observation age (GR-1170) and its fidelity grade.
- Fidelity is shown, not flattened. A Contact MUST be rendered at exactly the detail its current grade admits (GR-1260): Trace as an unattributed marker with a domain only; Sighting as an owner-coloured marker with a count band — one, a few, many — and no identity; Identification with exact count and classes; Assessment with strength, Stance and condition. Rendering a Sighting as a typed unit icon is a defect under this requirement.
- Position is a region. A Contact whose uncertainty radius is non-zero MUST be drawn as the region GR-1330 and GR-1340 define — the projected centre and its Chebyshev box, minus exclusions — and MUST NOT be drawn as an icon on a single tile. A tracked Contact's region MUST be visually distinguishable from an untracked one, because the difference between the two is the single largest payoff of keeping observers on the map.
- Exclusions are visible. Tiles excluded from a region by later observation (GR-1350) MUST be shown as excluded, so that a patrol which found nothing has visibly narrowed the search rather than appearing to have wasted its turn.
- Decay is legible. A Contact approaching the grade drop or the drop threshold (GR-1300, GR-1320) MUST show that it is about to, so re-scouting is a decision a player can schedule.
UX-425 Perceptible events. A Trace created by a perceptible event (GR-1390, GR-1400) — a battle heard beyond sight, a bombardment, an expendable unit resolving — MUST be rendered as an unattributed, dateable marker that reveals owner, count, class and strength to nobody (GR-1410). The client MUST NOT decorate it with an inference, a guess, or a probable owner.
UX-430 Unit state visuals. A unit MUST be visually distinguishable across every state the rules give it, and the following MUST each be readable at the Close and Mid tiers:
| State | Presentation | Source |
|---|---|---|
Condition — strength and disorder |
one composite badge, never two raw numbers | CB-190, UX-1125 |
| Grade (veterancy) | a rank mark | CB-170 |
Dug in (digIn 0–4) and established entrenchment |
a works mark that grows | CB-100, TM-700 |
| Shaken | a distinct overlay for its duration | CB-100 |
| Stance — March / Advance / Hold / Entrench | a glyph, with the derived value shown where an override replaced it | TM-630, TM-730 |
| Posture — Hold / Garrison / Screen / Survey / March / Attach, or Idle | a glyph; Idle MUST be conspicuous | CM-330, CM-540 |
| Forming Up (age 0–3) | a mark that visibly expires | TM-950, TM-960 |
| Supply grade | a mark in the economy's three-grade vocabulary | EC §13.5 |
| Encircled — no retreat candidate | marked for both owners whenever the unit is visible to the viewer | CB-1410 |
| Reaction capacity armed / spent | a mark; own units always, enemy units only at the disclosure TM-1740 permits | TM-1530, TM-1740 |
| Banked movement | shown as movement points on the panel and marked on the map | CM-1550, CM-1590 |
| Carried cargo | a loaded mark; contents only at the fidelity GR-1060 admits | GR-1060 |
| Air endurance remaining | a countdown mark | US-2000, US-2040 |
Where several marks would collide, the client MUST prioritise by severity — encircled, out of supply, condition, Stance, Posture — and MUST make the remainder reachable from the unit panel rather than dropping them silently.
UX-440 Ownership rendering MUST tint two-tone unit art with the owner's foreground and background colours in a custom Phaser WebGL pipeline. Under the Canvas fallback of UX-385 the same result MUST be produced from pre-tinted atlases generated per seat colour at load time — the visual outcome is required, the technique is not. Ownership MUST NOT be carried by hue alone in any tier (UX-1310).
UX-450 City rendering MUST reflect: the owner's colours and, for a city the player has ever explored, the owner as a public fact that fidelity never withholds (GR-1290, GR-1430); occupancy; Industry level 1–4 where observed, with its observation age (US-810, US-970); Integration and Unrest bands where the city is the player's own; and badges for a Seat, a Landmark, and any city currently published as a Bellwether (VC-2280, VC-3110). A city in the Unsettled state, or holding a Stalled production order, MUST say so on the map.
UX-460 Map overlays. The client MUST provide individually toggleable overlays, each with a control and a bindable key, and each drawn from the player's filtered view only:
| Overlay | Content | Source |
|---|---|---|
| Supply (Reach) | the three supply states across the whole map | EC-1880 |
| Projected supply | the network as it would be with a Depot on a chosen tile, or a chosen city at Integration 50 | EC-1880 |
| Attack odds | every enemy-occupied tile adjacent to the player's units, coloured by the break probability of the best available attack | CB-2430 |
| Intelligence | Contact regions, fidelity grades, observation age, exclusions | GR-1170, GR-1330, UX-420 |
| Industry | the Industry level of every observed city | US-970 |
| Integration and Unrest | per-city bands across the player's own cities | EC-1810, EC-1820 |
| Victory sites | Landmarks, Region boundaries, Seats, and any published Bellwethers | VC-2280, VC-3110 |
| Formations | Formation and Group membership, the Command Reference Point, and the cohesion radius | CM-950, CM-1010 |
| Postures | every own unit's assigned geometry — Screen lines, Survey regions, March routes — and its Leash radius | CM-330, CM-630 |
| Reaction cover | tiles covered by the player's own units holding reaction capacity | TM-1570, TM-1740 |
| Endurance | range and return envelope for the selected air unit | US-2000, US-2040 |
| Grid | cell boundaries | ours |
The reaction-cover overlay MUST show the player's own units only. An enemy unit's reach MUST appear only where TM-1740 permits it — a visible enemy whose class the player has observed — and MUST NOT be aggregated into a coverage field, which would disclose a map of enemy Stance the rules do not publish.
UX-470 A minimap MUST be available showing explored terrain, ownership, the current viewport, and waypoint bookmarks; clicking it centres the main map. It MUST be collapsible, and it MUST indicate wrap (UX-410).
UX-480 All animation MUST be skippable and MUST respect a global animation-speed setting including instant (UX-345). Outcomes MUST be identical regardless of animation settings, because by construction they were computed before the first frame (TM-2240).
UX-490 Camera. Centring on the selection MUST be idempotent: repeated invocation MUST NOT zoom, cycle, or otherwise change behaviour on the second press. The client MUST provide camera bookmarks, a waypoint jump list (UX-870), and a jump-to-location that never steals keyboard focus from the list or report that invoked it.
Rationale: this replaces an inherited quirk in which repeated centring progressively zoomed and then bounced back out. It was a curiosity of another program, nobody asked for it, and a control that does a different thing on the second press is a control players stop trusting.
UX-495 Terrain legibility. The eleven terrain types (GR-260), River edges and Fords (GR-1550), and every installation (Road, Fort, Airfield, Bridge) MUST be visually distinguishable at the Close and Mid tiers, and the hover readout (UX-520) MUST state, for the hovered tile: its terrain, the movement cost for the selected unit's movement class, whether it is enterable at all, its defensive value as the rules compose it, its concealment, and any installation. A player MUST never have to consult the encyclopedia to learn what a tile in front of them costs.
7. The persistent HUD
UX-500 The client MUST provide these HUD regions, each dockable and collapsible except where stated:
| Region | Default position | Content |
|---|---|---|
| Phase indicator | top centre | current phase and turn number (TM-090); where a deadline exists, time and reserve remaining (UX-305) |
| The Watch | docked, never dismissible | one row per player (UX-1150; VC-2340) |
| Empire strip | top left | Works income, Manpower pool and levy rate, next Cadre price, with a route to the Empire Ledger (UX-510) |
| Selection panel | lower left | the selected unit, Formation or city (UX-530) |
| The Dispatch | right | the ranked attention list (UX-810) |
| Location readout | lower right | the hovered tile (UX-520) |
| Commit control | bottom | readiness and commit (UX-295) |
| Command bar | bottom | commands applicable to the current selection (UX-560) |
| Menu | top right | save, load, settings, exit |
UX-510 The empire strip MUST show the core-computed values only (UX-040): Works accrued this phase, the Manpower pool against its cap, the current levy rate, and the price of the player's next Administrative Cadre — which 12-economy.md requires to be permanently visible as one of its six named brakes (EC-030). Each figure MUST link to its decomposition in the Empire Ledger (UX-1070). No figure here may be a bar without a number (EC-050).
UX-520 The location readout MUST populate on hover with the hovered tile's terrain and its derived costs (UX-495), its contents as fogged with age and fidelity (UX-420), the owner of any city present, and the Chebyshev distance from the current selection. On touch devices, long-press inspect substitutes for hover (UX-1390).
UX-530 The selection panel MUST show, for a selected unit, at all times and without a click: its identity and class; its full order stack in plain language (UX-740); its effective Stance and the derived value it overrode (TM-730); its four Sanction values and the level that supplied each (CM-760); its Fallback Posture (CM-310); its exact Initiative with every active term (UX-310); its condition, supply state and endurance; its reaction capacity, spent count and reach (TM-1740); its banked movement (CM-1590); and its next intended action as one plain-language line (CM-1910), or an explicit statement that the next step depends on a contact not yet made (CM-1930).
For a selected Formation the panel MUST additionally show membership and roles, Pace and cohesion radius, the commander, and whether the Formation is Responsive or One turn behind, in words, with the reason (CM-990, CM-1080).
UX-540 The phase indicator MUST name what the current phase permits. Entering the Cascade MUST visibly disable every order-authoring control rather than leaving it live and rejecting the order (UX-030, UX-645).
UX-550 Layout: every HUD region except the Watch MUST be dockable, collapsible and scalable, and side placement MUST be flippable left/right. Layouts MUST persist per profile and MUST be resettable in one action.
UX-560 The command bar MUST present the commands applicable to the current selection, each contextually enabled, and each disabled control MUST state why in one sentence — for a build option the failing clause of US-360 (US-370); for an order the named rejection the core would return (TM-250, AR-320); for an automation decision the numbers, in the form "stopped because sanctioned at ≥700‰; predicted 610‰" (CM-2000). A greyed control with no explanation is a defect under this requirement.
UX-570 Section customisation: each button section MUST support expand and reduce, drag reordering by exchange, and hiding individual commands. A hidden command's bound key MUST continue to function, and the command MUST remain reachable from the palette (UX-610).
UX-580 A single control and key MUST toggle the whole non-essential interface for near-full-map play. The Watch and the phase indicator MUST remain visible.
UX-590 Tooltips: hovering any control MUST show its description and its current key binding. Tooltips MUST deep-link to the relevant help or encyclopedia topic.
UX-600 A context menu listing the selection's currently applicable commands MUST be available on secondary click (desktop) and long press (touch, UX-1400). It is a DOM menu anchored by UX-016 — focusable, arrow-key navigable, and announced by screen readers like any other menu — and the browser's own context menu MUST be suppressed over the map surface only (UX-014.3).
UX-610 Command palette: a searchable palette (default Ctrl+K) MUST list every command in the game, including keyboard-only ones, with descriptions, current bindings, and applicability; invoking an applicable command executes it. Nothing in the client may be reachable only through an undiscoverable binding.
UX-620 Chat: in multiplayer games a chat surface MUST be present; behaviour, retention and moderation are specified in 05-multiplayer.md.
UX-630 No dead states. Whenever nothing is selected and no dialog is open, the client MUST state what the current phase expects of the player — "issue or revise orders; commit when ready", "watching turn 41", "waiting for two players". A screen that appears to be doing nothing, with no explanation, MUST NOT be reachable at any phase boundary.
UX-640 Dialog framework: floating panels MUST be movable and resizable; all dialogs MUST support arrow-key and Return navigation of controls and lists and full focus traversal (UX-1325). Modal dialogs MUST use native top-layer modal semantics (background inert, Escape closing per UX-014.5, focus trapped and restored per UX-1325), and moving and resizing MUST use pointer capture (UX-014.2) so a drag that crosses the canvas does not detach. Size and position MUST persist per profile and MUST be re-clamped into the viewport on resize, browser zoom, and UI-scale change (UX-1330) — a panel may never end up unreachable off-screen.
UX-645 The client MUST NOT present a control that is inert in the current phase. A control requiring the Orders phase MUST be disabled during Cascade and Reckoning with the phase named, and MUST re-enable at the next Orders phase without the player having to re-select anything.
8. Selection, targeting, and issuing orders
UX-650 The selection and targeting model MUST be exactly one model:
| Input | Effect |
|---|---|
| Primary click or tap on a location or unit | select |
| Secondary click, drag from the selection, or an explicit order control on touch | target the location or unit with the currently armed order |
| Primary click on empty space | clear the selection (UX-680) |
| Drag on the map away from the selection | pan |
There MUST NOT be a family of alternative click semantics. A single precision setting (UX-700) MAY make targeting require an explicit armed order rather than a secondary click, for players who mis-click; it MUST NOT change what any order means.
Rationale: an earlier draft carried four mutually exclusive click regimes inherited from another program, and named "check your interface mode" as the usual cause of players finding the game unresponsive. A single model that separates selection from ordering removes the whole category, and under Orders and the Cascade the cost of a mis-click is a wrong order rather than a wasted move — because everything is revisable until the phase closes (UX-285).
UX-660 Every order-authoring gesture MUST have a visible in-progress state naming what it is waiting for — "pick the second corner", "place waypoint 3 of 16", "choose the attack target" — and MUST be cancellable by Escape (UX-014.5), by the same control that started it, and by a visible cancel affordance on touch.
UX-670 Route preview. Before an order is submitted the client MUST show, live, the route the order would take: the projected path, its per-tile movement cost, the turn on which the unit would arrive, and the portion planned through unrevealed terrain visually distinguished as provisional (CM-1540), because that portion is planned optimistically (CM-140) and may not survive contact. The preview MUST come from the core's pathfinder, not from a client re-implementation (UX-040).
UX-680 Clearing the selection MUST be possible three ways: primary click on empty space, a control, and Escape (UX-014.5). Clearing the selection MUST NOT change any order.
UX-690 Navigating among own units MUST be possible by: selecting on the map; the Roster (UX-800); the Dispatch (UX-810); cycling by filter — by Posture, by Stance, by condition, by Formation, by Posting, by "changed since last turn" (UX-290); and cycling in the player's own activation order (UX-315). There MUST NOT be a "next unit awaiting orders" cycle, because under persistent orders (TM-280) no unit awaits orders.
UX-700 Precision setting: an option MUST exist under which a primary click on a friendly unit always changes the selection to it and never targets, for players for whom targeting-by-proximity misfires. This is a comfort setting and MUST NOT alter any order's meaning.
UX-710 Order authoring and revision. Every order the player authors MUST be previewed on the map before submission and MUST be submitted only by a deliberate action. A submitted order remains fully editable until the Orders phase closes (TM-360, UX-285), and the client MUST show, per unit, whether its current stack differs from what it carried at the start of the phase.
There is no post-close undo, and the client MUST NOT offer, imply or reserve one. The client requires no core retraction mechanism.
UX-720 Multi-unit ordering. The player MUST be able to apply, in one action, to a multi-unit selection: a Posture with its geometry where the geometry is shared; a Stance; any individual Sanction; a whole Doctrine (CM-780); a Formation membership change; or a Task. The client MUST:
- show exactly which units in the selection the action will affect and which it cannot, with the reason for each exclusion;
- state the count before the action is taken;
- emit one order record per affected unit (TM-240 keeps records from different players commutative and records from one player last-write-wins);
- make the whole action undoable as one edit within the phase (UX-285).
Applying an edited Doctrine to units already created under it MUST be an explicit, counted action naming N (CM-800), never a silent retroactive change.
UX-730 Keyboard targeting: directional single-step targeting (numeric keypad by default), keyboard route entry by direction, map scrolling keys, and the accessible map cursor (UX-1320.4) MUST all be supported, and every geometry-authoring interaction of UX-735 MUST be completable from the keyboard alone.
UX-735 Geometry authoring. The client MUST provide a direct-manipulation editor for each Posture parameter, with live legality feedback against the stated bounds:
| Geometry | Bounds | Source |
|---|---|---|
| Screen line — an ordered list of tiles | 2–64 tiles, need not be contiguous or passable | CM-440 |
| Survey region — a set of tiles | 1–4,096 tiles | CM-490 |
| March route — ordered waypoints, plus loop mode and follow-on order | 1–16 waypoints | CM-530 |
| Garrison target | a city or work | CM-330 |
| Leash radius | Chebyshev 0–64, drawn as a square (UX-380) | CM-630 |
| Formation facing | one of the eight directions | CM-930 |
While authoring a Screen line the client MUST show which line tiles are currently covered and by which friendly unit (CM-450), and the coverage a candidate position would add. While authoring a Survey region it MUST show the currently unrevealed share.
UX-738 Rejection presentation. An order record the core rejects (TM-250, CM-350, AR-320) MUST be presented at the point of authoring with its named, localised reason and a route to the fix. It MUST NOT be silently dropped, silently queued, or deferred to the next turn (TM-260). Where the rejection is a static illegality — a Task with no end condition (CM-250), a Stance the unit may not take (TM-750), a Screen line shorter than two tiles — the client SHOULD prevent it at authoring time and MUST still handle the rejection correctly if it occurs.
9. Command at scale
This section implements the presentation obligations 13-command.md places on this document (CM-2200). Its subject is a player with three hundred units and thirteen decisions.
UX-740 The order stack. Every unit's complete order stack MUST be inspectable on the unit panel in plain language at all times, and editable at any level without disturbing the others (CM-320). The three levels MUST be presented as what they are (CM-200):
[2] Reflex pushed by the engine — Withdraw, Replenish, Regroup, Return to Post, Take Cover
[1] Task one instruction with an end condition
[0] Posture the standing job
A Reflex MUST show its cause and its pop condition (CM-1400). A Task MUST show its end condition
(CM-260) and its pressHome state (CM-280). A Posture MUST show its parameters and its four
Sanctions. A unit with an empty Posture level is Idle and MUST be conspicuous everywhere it
appears (CM-540).
The complete command inventory the client MUST expose, each as a control, a context-menu entry, a palette entry and a bindable key:
| Group | Commands | Owner |
|---|---|---|
| Posture | Hold, Garrison, Screen, Survey, March (loop mode Once / Shuttle / Circuit), Attach | CM-330, CM-530 |
| Sanctions | Engagement (Never / Defend / Favourable(N) / Free), Leash 0–64, Risk (Expendable / Prudent(N) / Cautious), Seize (Never / Undefended / Free) | CM-580–CM-710 |
| Stance | March, Advance, Hold, Entrench, and clear-to-Derived | TM-630, TM-220 |
| Task | move to, attack, garrison, load, unload, join formation, execute a Posture once — each with an end condition | CM-250, CM-260 |
| Doctrine | create, edit, apply to a selection or a Formation, set as a type default | CM-770–CM-830 |
| Formation and Group | create, assign roles, set facing, Pace, cohesion radius, commander, remove member, disband | CM-840–CM-1120 |
| Posting | create, anchor, weight, Requisition rows, surplus rule; set a city's Consignment | CM-1130–CM-1330 |
| Production | enqueue a unit, industry or works order, reorder, remove, apply a Template |
US-1150–US-1210, CM-1310 |
| Victory | DesignateSeat, DesignateWorkSite, SelectCharter, CastVerdictVote, Concede, ClaimWarrant | VC-320 |
UX-745 Stance control. The client MUST present the four Stances with their exact effects — the
initiative modifier, the movement allowance change, whether the unit may initiate combat, its
reaction capacity, and its defensive treatment in words — drawn from TM-640 rather than restated.
Setting a Stance submits SetStance; it MUST persist until cleared and MUST NOT expire at end of
turn (TM-720), and the panel MUST always show both the effective Stance and the derived Stance it
overrode (TM-730). An illegal Stance MUST be refused with its reason (TM-750).
UX-747 Reactions. The unit panel MUST show reaction capacity, spent count, and reach (TM-1740). The reach of a visible enemy unit MUST be shown only where the player has observed that unit's class (TM-1740). The client MUST make plain the two facts a player most often gets wrong: that a unit in March or Advance has no reaction capacity at all (TM-1700), and that a mover halted by a reaction forfeits the rest of its movement and cannot bank it (TM-1640).
UX-750 Carriage. Loading and unloading along a stated route is automatic and requires no input (CM-535); the client MUST NOT present it as a decision. It MUST show a carrier's manifest, its remaining capacity, and each cargo unit's destination, and MUST let the player author an explicit load or unload Task where they want one (CM-250). Cargo contents of a foreign carrier MUST be shown only at the fidelity GR-1060 admits.
UX-760 Navigating a carrier's cargo MUST be possible from the panel and from the map: select the carrier, select any cargo unit, and step between siblings, without losing the carrier's selection context.
UX-770 A stack and cargo widget MUST show the host and child tree for a selected location or unit, allowing selection of any member and drag-to-move between hosts where the rules permit it. It MUST show the tile's stacking limit and current occupancy (GR-720, GR-780), because a limit of 1 in the open is load-bearing and invisible otherwise.
UX-780 Landing legibility. A unit unloaded during a Cascade is spent for that turn and will not activate again (TM-1840). The client MUST state this at the point the order is authored, not after the fact.
UX-790 The client MUST NOT invent automation. Every automated behaviour the interface depicts MUST be one the core actually performs (CM-100); the client MUST NOT add a convenience behaviour, a smart default that issues orders, or an inferred action of its own. A player who hand-orders every unit MUST get identical outcomes to one who automates identically (CM-050), and the interface must not quietly break that by helping.
UX-800 The Roster. The client MUST provide an empire-wide unit list meeting UX-045 in full: virtualised, filterable and groupable by class, Posture, Stance, condition, supply state, Formation, Posting, progress state and "changed this turn", sortable on every column, selectable in bulk and commandable from the selection (UX-720), and navigable and selectable entirely from the keyboard. Its default grouping MUST be Posting, then Formation, then Posture, which is the order in which a player thinks about an army; every other grouping MUST be one action away.
UX-810 The Dispatch. The client owns the presentation and the ranking; the core owns the events (CM-1610, CM-1620). The client MUST:
- present at most 7 items, player-settable in
[3, 15](CM-1640), with everything below the cut collapsed into a one-line digest carrying per-class counts; - rank by the integer urgency
U = S + T + A + P, ties broken by lowest affected-unit creation sequence number (CM-1660), whereS,TandAcome from the core andPis the client's adaptive per-class weight (CM-1770); - give every presented item at least one one-action resolution and a snooze (CM-1650); an item with no available action MUST be a digest line, not an item;
- keep items sticky across turns until resolved, snoozed, or their condition ceases (CM-1670), and show a retired item once in the digest naming what happened (CM-1680);
- snooze by cause key for a player-set number of turns, default 5, range
[1, 20], re-raising immediately if the item's Stake rises by 30 or more (CM-1690); - adapt
Pper CM-1780, default on, disableable with one setting, with every currentPvisible in a settings panel and a per-class and global reset (CM-1800); - never demote a non-adaptable class (CM-1790) — which includes
CityThreatened, the economy'sRevoltImminent(EC-1910), and victory'sVictoryAlarmandVerdictOffered(VC-3150); - pin every item to a map location, move the camera on hover, and restore the camera on resolve (CM-1890);
- rank and present within 16 ms for up to 200 concurrent live events (CM-2130);
- emit no network traffic whose timing or volume varies with Dispatch content (CM-1860).
The Dispatch MUST be the client's only by-exception surface. No subsystem may add a competing notification feed (EC-1900); toasts, banners and badges MUST all be renderings of Dispatch items or of phase transitions, and MUST be announced through the live regions of UX-1320 at the politeness their severity implies, coalesced so that a large Cascade produces a summary rather than a flood.
Rationale: the whole value of this design is that a player with three hundred units reads five items. A second feed alongside it does not add information, it subtracts the guarantee.
UX-820 Screen and Survey authoring MUST show coverage as the thing being decided: for a Screen, which line tiles are covered now and by which unit, and what a candidate position would add (CM-450, CM-460); for a Survey, the unrevealed share of the region and the reveal a candidate position would produce. An under-strength Screen MUST be visible as such on the map and on the Formation panel (CM-1050, CM-1060), together with the Requisition entry it raised.
UX-830 March routes and Ferry: waypoint editing (UX-735), loop mode, and the follow-on order MUST
all be editable in one panel, and the cached route MUST be displayed including its provisional
portion (CM-1540, UX-670). A route that has been suspended by a Blocked interruption MUST be shown
as suspended, not cancelled (CM-1390), with the obstruction named.
UX-840 Multi-select MUST support drag-box selection with live highlight, additive and subtractive modifier clicks, and selection from the Roster and from any map overlay. Selection MUST be representable in the accessible map view (UX-1320.4).
UX-850 Persistent control groups MUST exist (Ctrl+1–9 assign, 1–9 recall), MUST survive save and load as client state, and MUST NOT be confused with Formations, which are game state and confer different behaviour. The client MUST make the distinction plain the first time a player creates one.
UX-860 Formations and Groups panel. The client MUST show and allow editing of: membership against the span of 8 (CM-850), with a rejected ninth member offered a split or a promotion to a Group; role assignment against the slot caps of CM-900; the facing (CM-930); Pace and cohesion radius (CM-1000–CM-1010); the commander and the succession rule (CM-940); and Group composition up to five Formations across exactly two echelons (CM-860).
It MUST state at all times, in words and with the reason, whether the Formation is Responsive or One turn behind (CM-990), and for a Group member whether its order is up to two commander activations stale (CM-1080). It MUST show role overrides on the affected units' panels — in particular the Van's forced Defend and Cautious (CM-910).
Disbanding a Formation, or removing a unit from one, MUST show what Posture each affected unit will be left holding before the action is taken (CM-1090, CM-1100); it MUST never leave a unit Idle.
UX-870 Map annotation: players MUST be able to place free-text labels on the map, managed from a labels panel; a label MAY be designated a waypoint and bound to a key that centres the map on it (UX-1210). Labels are private to the authoring player unless 05-multiplayer.md specifies shared annotations.
UX-880 Endurance. Air endurance MUST be shown as turns remaining on every air unit at all times,
and the client MUST warn before committing an order that would leave a unit unable to reach any base
(US-2040). The range and return envelope MUST be available as an overlay (UX-460), computed from
enduranceRemaining and the basing sites the player can see (GR-770).
UX-890 Naming: units MUST display their names, and cities, Formations, Groups, Doctrines and Postings MUST support inline renaming. Inline renaming MUST use a native text input with full IME support (UX-045) — a player naming a city in Japanese, Chinese, or Korean gets their system's candidate window, not a keystroke-swallowing custom field.
UX-895 The audit trail. For any automated action the client MUST be able to retrieve and render the core's CommandDecisionRecord (CM-1940) for at least the 20 most recent turns (CM-1950), rendered as one sentence naming the clause and the numbers — "Attacked at 743‰ predicted; sanctioned at ≥700‰" (CM-600, CM-1960) — with the runner-up option and its score available. Where automation declined to act, the same surface MUST state the reason in numbers (CM-2000).
Rationale: this is the feature that converts a bad automated outcome from "the automation is broken, I will never use it again" into "the threshold was too low, I will raise it". It is also the best debugging tool the team will have, and it costs the client a table and a sentence template.
UX-898 Progressive disclosure. On first exposure the client MUST present Sanctions as the three named presets only — Careful, Balanced, Aggressive — and MUST expose the four individual dials at a tier the player can also enable immediately (CM-1980). Formations, Groups, Postings and Requisitions MUST each be independently disclosable, and the game MUST be fully playable with none of them enabled (CM-1990). Every command surface in this section MUST remain fully functional for a player who automates nothing (CM-070).
10. Industry, production, and the economy
12-economy.md makes visibility a requirement rather than a courtesy: a brake that satisfies its Visibility Test on paper and not in the client has failed the direction document's test just as completely as an invisible multiplier would (EC-020, EC-1790). This section is where that debt is paid.
UX-900 The city panel MUST open automatically when a city is captured and on demand for any owned city, and MUST be presented as a non-modal dockable panel rather than a modal that blocks the map. A captured city requires no player input to become productive (US-1030, EC-680) and the panel MUST NOT imply otherwise.
UX-910 The panel's identity area MUST show: location, name (editable inline), city class, traits, Industry level, Integration, Unrest, Loyalty, supply state, and any Seat, Landmark, Unsettled or Bellwether status.
UX-920 The production list MUST show one selectable line per unit definition with: name, class, role sentence, Works price and Manpower price as the ordered pair the economy defines, the turns to completion from the current Yard, and — for any definition not currently buildable — which clause of US-360 fails, in one sentence (US-370). Naval definitions in a non-coastal city MUST be listed and explained rather than hidden.
UX-930 The queue MUST be presented as what it is: an ordered list of at most 8 orders (US-1150) of
three kinds — unit, industry, works (US-1160). The client MUST show that removal is free and
alters nothing (US-1200); that at most one industry order may be queued (US-1180); that at most
one order completes per turn regardless of Yard size (US-1290); and that an order which has begun
funding is not automatically retargeted (US-1210, CM-1270). A Stalled order MUST be shown in
place with its reason and MUST NOT look cancelled (US-1040, US-1060).
UX-940 The Yard MUST be shown as a single integer with its cap and what the cap is derived from (US-1220, US-1240). On any turn a city discards Works because the Yard is capped, the client MUST say so plainly, once, with a route to fix it. A silent discard is exactly the hidden tax the economy forbids.
UX-950 The Industry ladder panel. For any owned city the client MUST show all four levels with, per level: the name, the Works multiplier, the upgrade cost, the Works gain for that specific city, the payback in turns, and the complete set of unit definitions the level unlocks — all inspectable before any Works are spent (US-940, US-820, US-890). Where the player's Industry cap binds (US-900) the panel MUST say so and MUST state that a captured city above the cap keeps its Works multiplier and loses only the buildable set (US-920).
UX-960 Unlock cards and contact briefings. On the turn a city's Industry upgrade completes the client MUST present one unlock card per newly available unit, each carrying the unit's name, role sentence, full stat line, the two units it counters and the two that counter it (US-950). The first time a player observes an enemy unit of a class they have never produced the client MUST present that unit's card unprompted as a contact briefing (US-960). Both MUST be dismissible and both MUST be re-openable from the encyclopedia (UX-1290).
UX-970 Governance: for every city the client MUST state, in one word, whether a Template, a
Requisition, or a manual queue is currently governing its production (CM-1330), with one action to
change it, and MUST show the city's Consignment — a Posting, Auto, or Manual (CM-1160) — and its
surplus rule (CM-1220).
UX-980 A centre and close action MUST close the panel, centre the city on the map, and save pending edits. Any active Dispatch item on the city MUST remain active.
UX-990 A minimise action MUST hide the panel to expose the map and MUST restore it from a persistent affordance or by selecting the city. Because the panel is non-modal (UX-900), minimising MUST NOT lock selection.
UX-1000 Closing a city panel MUST return focus where it came from and MUST NOT clear the map selection (UX-1325).
UX-1010 Reach. The client MUST render the three supply states across the whole map as an overlay (EC-1880), MUST show each unit's grade on its panel and its map badge (UX-430), and MUST show each source's reach as the Chebyshev region it is (UX-380). The consequences of each grade — the movement and combat effects, and attrition where isolated — MUST be stated on the unit panel, not left to the encyclopedia.
UX-1012 Depot siting MUST show the projected network before the order is committed: the supply overlay as it would be with a Depot on the chosen tile, or with a chosen city crossing Integration 50 (EC-1880). Placing a Depot without seeing what it would connect is exactly the guess this overlay exists to remove.
UX-1015 The garrison burden MUST be shown on the city panel as points required against points present, with each contributing unit and its rating, the Integration at which the requirement next drops, and the requirement that will apply as Integration rises (EC-1830). Where the requirement is unmet, the item MUST appear in the Dispatch with its one-action resolution — raise the Requisition target (EC-1920).
UX-1020 The Administrative Cadre. The client MUST show the player's Cadre count, each Cadre's assignment, and the price of the next three Cadres (EC-030, EC-1870), and MUST present the Digestion Forecast (UX-1080) as the answer to what buying one more would do.
UX-1030 Air basing: a city bases up to 4 air units and an Airfield up to 3 (GR-770), and units
with the airbase capability base to their own capacity. The client MUST show basing occupancy
against capacity wherever an air unit could land, and MUST fold endurance state (UX-880) into the
same readout, so "can I get home from there" is answered in one place.
UX-1040 Construction: with a construction-capable unit selected, the client MUST list the installations it can build on its current tile with the exact build time for that terrain, and MUST support authoring an ordered construction path of build and remove locations, rendered as an overlay. Removing an installation that a March route or a supply link crosses MUST require confirmation naming what is affected.
UX-1045 Works strikes. Where a unit may strike a city's industry rather than attack a unit (US-1090), the client MUST show, before the order is committed: the works defence terms and their integer contributions (US-1100), the effect table for 0, 1, 2 and 3+ hits (US-1110), and whether the target has already lost an Industry level within the five-turn floor (US-1120). It MUST also state plainly that a strike may be ordered against a city the player cannot currently see and will resolve against the true state (US-1140).
UX-1050 Production Overview. The client MUST provide an empire-wide, sortable, filterable table of every owned city and producer showing at least: name, location, class, traits, Industry level, Yard against its cap, head order and its ETA, Stalled state, governing source (UX-970), Consignment, Integration, Unrest, garrison state, supply state, and live Dispatch items. It MUST support multi-select bulk assignment, emitting one order per affected location exactly as the per-city panel would (UX-020, UX-720), and it MUST meet UX-045 in full. It complements and MUST NOT replace the per-city panel.
UX-1060 The Cost of Conquest. For any enemy or neutral city in the player's knowledge view the client MUST present the panel 12-economy.md specifies, with every line it names: immediate yield, starting Unrest, garrison required and the unit count that satisfies it, turns to digest, Cadre cost, Levy earned and when the first point arrives, and War Damage after the capture (EC-1850). Every line MUST be computed from the player's knowledge view only and every line derived from a stale observation MUST be marked with its observation age (EC-1860, GR-1170).
Rationale: 12-economy.md calls this the single most important piece of interface in that document, because it is where "visible and plannable" stops being an adjective and becomes a screen. It is the anti-snowball design presented as an offer rather than administered as a penalty, and a player who takes the city anyway has taken a mortgage with the terms in front of them.
UX-1070 The Empire Ledger. The client MUST provide the screen EC-1870 specifies: Works accrued this phase with a per-city breakdown sortable on every column; the Manpower pool, cap, levy rate and the Manpower committed to hulls awaiting men; the per-city Levy breakdown with zero-yield cities listed explicitly; the Cadre count, assignments, and the prices of the next three; every city's Integration and Unrest with its delta; and total garrison points required and present across the empire.
UX-1080 The Digestion Forecast. The client MUST present, for the whole empire, the turn at which every held city reaches Integration 100 under the current Cadre assignment, and the marginal effect of one more Cadre — which city it would take, how many turns it would save, and what it would cost (EC-1890).
UX-1090 The brake audit. For each of the six named brakes of EC-030 — Integration, the Cadre price, the garrison burden, Reach, War Damage, and Manpower not transferring — the client MUST satisfy all four clauses of the Visibility Test at the place the brake applies (EC-020): the brake named in one sentence without the words penalty, scaling or balance; every integer term it consumes displayed before it applies; a forecast of when it will bind and what it will cost; and the counterplay named as an action available this turn.
Additionally, and bindingly: no economic quantity may be rendered as a bar without its exact integer (EC-050), and the client MUST be able to answer "why is this number what it is" for Effective Works, Levy, Integration delta, Unrest delta, garrison requirement and supply state by naming every contributing term and its value (EC-040, EC-1790 through EC-1840).
UX-1100 Manpower and Awaiting Levy: the client MUST show the pool against its cap, the levy rate and its per-city composition, every hull awaiting men and the Manpower each owes (EC-1840), the Emergency Levy with its cooldown, and any Manpower discarded by the cap on the turn it is discarded.
UX-1105 Economic events reach the player through the Dispatch and nowhere else (EC-1900, UX-810).
The client MUST NOT build a second by-exception economic surface, and MUST honour the one-action
resolutions EC-1920 requires for GarrisonShort, CadreUnassigned, AwaitingLevy and SupplyCut.
11. Combat: the preview and the record
UX-1110 The attack preview MUST present its content in exactly the order 11-combat.md CB-2390 fixes:
- The verdict, in plain language, first — "They break — certain."
- Both loss bands as min ── median ── max, with the resulting strength values.
- Odds and reliability as two separate figures.
- Every contributing line item, with its source named.
- The consequence — where the defender would retreat to, whether they are cut off, whether the attacker takes the tile.
- The alternative — a one-click preview of the same attack without each supporter.
Because support and flanking are automatic, the panel MUST enumerate every unit it counted and every modifier it applied (CB-2410). It MUST name the retreat destination and MUST state explicitly when the defender has none (CB-2420) — which is the difference between a fight and a rout.
UX-1112 A single expected value MUST NOT be shown in place of a band, and the falter probability MUST always be shown even when it is 0‰ (CB-2400).
Rationale: an expected value is the specific lie that makes players distrust odds displays, and a risk that appears only when non-zero is a risk players never learn exists until it happens to them.
UX-1115 Prediction under fog. The preview MUST show the target's observation level — L1
Sighted, L2 Adjacent, L3 Identified (CB-2310) — and what each level does and does not tell the
player. Where an input is unobserved the preview MUST show the resulting range of each output and
name every unobserved field (CB-2330). It MUST display at most two pivots, ordered by the span
each induces, largest first (CB-2340, CB-2350), each phrased as the threshold and the fix — "they
break unless they are Veteran or better; grade unobserved; a Recon unit adjacent would tell you".
The panel MUST make explicit that the attacker's own losses are well known even when the outcome
is not (CB-2370).
UX-1118 The client MUST NOT compute any combat number itself; every figure MUST come from a core export (CB-2470), and every per-mille figure MUST be shown in per-mille or as a percentage derived from it (CB-2480).
UX-1120 The resolution trace. The core emits a combat event carrying the Clash Specification, the draw, every intermediate quantity and the resulting state of every participant (CB-2450, CB-2460). The client MUST expose that trace in full in solo and hotseat games, and MUST NOT expose it in a game with two or more human players.
Rationale, stated because this is a design decision rather than an inherited rule: the trace names fields — the defender's exact grade, disorder and dig-in — that the viewer's fidelity grade may withhold (GR-1260), so publishing it to an opponent would reveal through the combat log what observation was supposed to cost. In a solo or hotseat game there is no opponent to leak to, and the trace is the best teaching object the combat model has.
UX-1125 The condition badge. strength and disorder MUST be rendered as a single composite
badge, always available together and always meaning the same thing across every unit in the game
(CB-190, CB-110). Exposing them as two raw numeric columns is a failure of this requirement. The
badge MUST be legible at the Close and Mid tiers (UX-390) and MUST have a non-colour encoding
(UX-1310).
UX-1130 The hostile-tile overlay MUST colour every enemy-occupied tile adjacent to the player's units by the break probability of the best available attack, computed by the same function as the preview (CB-2430).
UX-1135 Encirclement MUST be visible before it is fatal: the client MUST mark any unit with no retreat candidate, for both owners, whenever the marked unit is visible to the viewing player (CB-1410).
UX-1140 After every resolution the client MUST show a reconciliation line stating the prediction and the actual result — "predicted 26–41, actual 35" (CB-2440) — in playback, in the after-action report (UX-355), and in the combat history.
Rationale: this builds trust in the readout faster than any tutorial, because it is evidence rather than a claim, and it makes the invariant that the outcome always lies within the displayed bands visible to the person it protects.
UX-1145 History and graphs. The client MUST provide per-turn charts covering at least: Standing and Tenure (VC-2390), city count, unit count, Works income, Manpower pool and levy rate, and combat losses. Charts MUST be DOM/SVG rather than canvas, MUST be rendered from core-recorded history so that a replay reproduces them, and every chart MUST offer an equivalent data-table view of the same series (UX-045) — which also satisfies UX-1320 without a parallel implementation. Series MUST follow the colourblind-safe palette rules of UX-1310 and MUST be distinguishable by line style as well as hue.
UX-1148 The combat record MUST be filterable by turn, by location, by participant and by outcome, MUST meet UX-045, and MUST jump to a location without stealing focus.
12. Victory: the Watch, the Board, and leaving the game
14-victory.md makes legibility a requirement of the rules rather than a courtesy of the interface (VC-030), and delegates the pixels to this document (VC-3170). It also imposes a discipline this section must not break: the client MUST NOT compute any victory value, and MUST NOT present one at a precision finer than the band published for it.
UX-1150 The Watch. The client MUST present a compact, always-visible, never dismissible strip carrying exactly one row per player (VC-2340). A row MUST carry exactly the six fields of VC-2350 and no more:
| # | Field |
|---|---|
| 1 | Identity — colour, name, and live / capitulated / Reduced / Out of Contention status |
| 2 | Nearest Track — the Track that player is nearest completing, by highest ratio |
| 3 | Bar — drawn from the published value at that player's band |
| 4 | Value — a Rumour word, a quantised Report figure, or an exact Alarm figure |
| 5 | Vigil — VIGIL n/V where one is running; blank otherwise |
| 6 | Tenure — the value and a rising / falling / flat indicator against the previous Seal |
Tenure MUST be shown at full precision to every player at all times and MUST NOT be banded (VC-2360). The Watch MUST carry a one-line footer stating the current Ebb value with its next scheduled change, and the current turn against the Horizon (VC-2370), and MUST answer without any click: who is closest to what, by how much, whether they are rising or falling, and how long the game has left (VC-2380).
UX-1155 Precision discipline. The client MUST render each opponent value at exactly its published band and no finer (VC-2160, VC-3170): Rumour as one of the five words of VC-2210 with a stub bar and no number; Report as the quantised figure, with the report quantum itself shown so the player can see the precision of what they are reading (VC-2260); Alarm at full precision. The client MUST NOT infer, interpolate, animate between, or trend-fit banded values, because a smooth line through two quantised readings reconstructs precision the band exists to remove. Band hysteresis (VC-2220) MUST be respected in presentation as well as in value: a row MUST NOT flicker.
UX-1160 The Board. The full victory panel MUST be reachable from a single keypress (VC-2390) and MUST contain at least: the full matrix of players against Tracks with published value, per-Seal delta and Vigil state; the Ebb schedule drawn as a descending line; each player's count-backwards markers; the Warrant board; the Ascendancy Tally calendar; each player's Charter and Sealed Order counts; Standing and Tenure history; and the Chronicle. It MUST be redrawable for any past turn from the retained Seal records without recomputation (VC-2400), and MUST mark every Track proved DECIDED and every player Out of Contention, naming the turn each was established (VC-2410).
UX-1165 The Alarm. When a player enters the Alarm band the client MUST present the banner 14-victory.md requires, naming the player, the Track, and the number of turns to completion, and MUST publish the Bellwether sites as named, locatable map markers with the live vision granted for the duration and the two-turn grace (VC-2280, VC-2290, VC-2310). Bellwether vision MUST render exactly current occupancy of the named sites and nothing else — no unexplored terrain, no other tile, no past state (VC-2300).
The alarmed player MUST be told, at full precision, exactly what has been published about them and which of their sites were revealed (VC-2230). An Alarm that discloses three of a player's cities without telling them which three converts a fair fight into an ambush.
The Alarm MUST additionally appear as a non-adaptable Dispatch item on every other player's Dispatch
(VC-3150, UX-810.7) and MUST remain in force under every Progress Visibility setting, including Own Only (VC-2190).
UX-1170 The Chronicle. The client MUST present the permanent, one-line-per-event victory log, rendering each entry's turn number, class and plain-language text (VC-2420, VC-2430). Visibility MUST follow VC-2440, and the complete Chronicle MUST be presented to every player without redaction at the end of the game (VC-2450). The rendering MUST be terse enough that the endgame is followable from the Chronicle alone; VC-2460's worked form is the normative target for the classes it exercises.
UX-1175 A player MUST always see their own Track values at full precision, unbanded, including their own Vigil counters and their own sites (VC-2170) — on the Watch, on the Board, and on the map.
UX-1180 Reduced status and the Rally. Where a player is Reduced the status MUST be shown publicly on their Watch row, naming the leader it is measured against (VC-2920). The one-time Survey MUST be presented as what it is — current ownership of every city, and nothing else: no units, no strengths, no terrain reveal, no ongoing vision (VC-2970) — and the client MUST say so, so that the player does not plan an attack on a map that shows no defenders because there is no data rather than because there are none. Rally Warrants MUST be public, including target and holder, from the moment they are issued (VC-2960).
UX-1185 The Verdict. Where a Verdict is offered the client MUST raise it as a non-adaptable
Dispatch item on every eligible voter's Dispatch, persisting until the window closes (VC-2780,
VC-3150), stating the window in turns and stating plainly that abstention counts as "play on"
(VC-2770). Casting a vote submits CastVerdictVote (VC-320).
UX-1190 Exit paths. Save-and-exit MUST be the normal egress. A force quit at any moment MUST never lose submitted orders or corrupt state; the client MUST persist submitted orders at submission time rather than at unload time, because closing a tab, navigating away, and being killed by a mobile WebView under memory pressure are all force quits that arrive with no guaranteed shutdown hook. Unload handlers are a courtesy, never the mechanism. A confirm-on-leave prompt MUST be raised only when unsubmitted staging would be lost, and MUST NOT be used routinely — browsers show generic wording the client cannot control.
UX-1195 Leaving the game. Concession MUST require an explicit confirmation and MUST be presented as irreversible (VC-2840), stating its consequences — the disposition of the player's cities, the frozen Tenure, and the final placing — before it is taken. A capitulated, conceded or eliminated player MUST retain spectator access to their own past Seal records and to the public Chronicle (VC-3090). Where a seat is substituted by AI on departure the client MUST say so plainly and MUST show that Tenure continues to accrue (VC-3060, VC-3070).
UX-1198 The end-of-game screen MUST present the record VC-3050 defines: the winner and the route by which they won; every player's placing, final Standing, final Tenure and the turn they left; the stored Terms paragraph unchanged (VC-2000, UX-185); the complete Chronicle; and every dismissed linter warning (UX-195). It MUST be exportable and MUST be reachable again from the Load Game screen.
13. Keybindings and input maps
UX-1200 Key Bind dialog: a settings screen MUST enumerate every command in the game — it is the authoritative command list — show current bindings, support rebinding with conflict detection, and support multiple named binding profiles per account. Defaults for new profiles come from the account defaults; profiles sync via cloud accounts where available (08-services-platform.md).
UX-1210 Discoverability: commands without a visible control — directional targeting, map scrolling, playback controls — MUST appear in the Key Bind dialog and the command palette (UX-610). Waypoint labels MUST be bindable to keys that centre the map on them (UX-870).
UX-1220 Default keyboard and mouse map. All bindings are rebindable. Defaults MUST satisfy UX-1225 — the browser reserves keys a native client owns outright, and the shipped defaults must be ones that survive in a tab.
| Command | Default binding | Notes |
|---|---|---|
| Commit orders / revoke commit | Ctrl+Return / Ctrl+Shift+Return | UX-295 |
| Undo / redo order edit | Ctrl+Z / Ctrl+Y | within the Orders phase only (UX-285) |
| Clear selection | Escape | reached only after higher layers decline it (UX-014.5) |
| Select next / previous own unit in initiative order | ] / [ | UX-315 |
| Cycle by filter | Shift+] / Shift+[ | UX-690 |
| Stance: March / Advance / Hold / Entrench | Alt+1 … Alt+4 | UX-745 |
| Clear Stance override (Derived) | Alt+0 | TM-720 |
| Posture menu | P | six Postures plus Idle (CM-330) |
| Sanctions panel | Alt+S | four dials or three presets (UX-898) |
| Apply Doctrine | Alt+D | CM-780 |
| Formation panel | F | UX-860 |
| Attach / detach from Formation | Shift+F | CM-330 |
| Task: move to / attack | M / A | attack opens the preview (UX-1110) |
| Directional step target (8 ways) | Numpad 1/2/3/4/6/7/8/9 | UX-730 |
| Dispatch: focus / next item / resolve / snooze | D / Shift+D / Return / S | UX-810 |
| The Board | B | UX-1160 |
| The Roster | R | UX-800 |
| Production Overview | Y | UX-1050 |
| Empire Ledger | L | UX-1070 |
| Cost of Conquest for the hovered city | Shift+C | UX-1060 |
| Command View | Shift+V | UX-395 |
| Overlay menu | O | UX-460 |
| After-action report | Shift+A | UX-355 |
| Playback: play/pause, step, skip | Space / Period / Escape | UX-345 |
| Map scroll | Arrow keys | |
| Zoom in / out | Mouse wheel, + / − | |
| Centre on selection | Home | idempotent (UX-490) |
| Multi-select box | drag with primary | UX-840 |
| Control groups assign / recall | Ctrl+1–9 / 1–9 | UX-850 |
| Toggle interface | F9 | Tab is reserved for focus traversal and MUST NOT be bound (UX-1225.3) |
| Encyclopedia / help | F1 | browser-claimed; UX-1225 guard |
| Command palette | Ctrl+K | browser-claimed in some browsers; UX-1225 guard |
| Chat | T | multiplayer |
| Quick save | Ctrl+S | browser-claimed; UX-1225 guard |
This table is our design, not a reconstruction of anything. Where a binding is unfamiliar the palette (UX-610) and the tooltip (UX-590) are the discovery path, and every one of them is rebindable.
UX-1225 Browser-reserved keys. Running in a tab costs the client keys a native application owns, and this MUST be handled explicitly rather than discovered by players:
- Unavailable keys — combinations the browser does not surrender to the page (new/close/switch tab and window, quit, developer tools, full-screen toggle, and the platform's window-management chords) MUST NOT ship as defaults, and the Key Bind dialog MUST refuse to bind them in the browser build while flagging, not hiding, that they remain bindable in Tauri builds (UX-018).
- Claimable keys — combinations the page can take by suppressing the browser default (the function-key row, Ctrl/Cmd+S, Ctrl/Cmd+K, Backspace, and similar) MAY be bound, but MUST be suppressed only while focus is on the game surface and never while a text field has focus (UX-014.4), so the browser's own save, find, and text editing keep working where a player expects them.
- Tab MUST remain focus traversal (UX-1340) and MUST NOT be bindable to a game command. This is a deliberate divergence from a native client's freedom, and it is why UX-580's default is F9.
- Per-platform conflict detection — the Key Bind dialog MUST validate a binding against the host it will run on and warn per browser where behaviour differs, rather than silently producing a binding that works on one player's machine and not another's.
- A player MUST be able to see, in one place, every command whose default differs between the browser and packaged builds.
The final reserved-key sets differ between Chromium, Firefox, and Safari and change between releases, so this is a policy to be owned and re-tested, not a fixed table (open question 7).
UX-1230 Touch input map:
| Gesture | Action |
|---|---|
| Tap | select (UX-650) |
| Tap with an armed order | target |
| Long-press location | inspect — the location readout (UX-520) |
| Long-press selection | radial command menu (UX-1400) |
| Drag from the selection | route preview and order on release (UX-670) |
| One-finger drag on the map | pan |
| Pinch | zoom |
| Two-finger tap | cancel the current in-progress interaction (UX-660) |
| Persistent on-screen controls | commit (UX-295) and the Dispatch (UX-810) always reachable |
Touch in a WebView carries host gestures the client does not own. Over the map surface the client MUST suppress pull-to-refresh, double-tap zoom, and the long-press selection callout (UX-014.3), and MUST NOT suppress them over DOM panels. Edge swipes MUST NOT be the only way to reach any surface: on iOS a swipe from the screen edge is system navigation and cannot be reliably captured, so every drawer MUST additionally have a visible handle (UX-1370), and on hosts where an edge gesture is unavailable the client MUST simply not offer it rather than binding a gesture that half-works. Per-host edge-gesture behaviour is open question 2.
UX-1240 Gamepad input map (full playability required on gamepad):
| Control | Action |
|---|---|
| Left stick | move the map cursor |
| Right stick | pan; click centres on the selection |
| D-pad | directional step targeting |
| A / Cross | select or target at the cursor |
| B / Circle | cancel / clear selection |
| X / Square | radial command menu for the selection |
| Y / Triangle | Dispatch: next item |
| LB / RB | previous / next own unit in initiative order |
| LT / RT | zoom out / in |
| Start | game menu |
| Select/Back | toggle interface (UX-580) |
| Start + A | commit orders, with confirmation |
In the browser, controllers are exposed through the Gamepad API, which does not report a connected pad until the player presses a button and offers no guaranteed haptics. The client MUST therefore show a "press any button to connect your controller" affordance rather than reporting no gamepad, MUST treat rumble as optional decoration and never as a required signal, and MUST route gamepad input through the command dispatcher (UX-014.6). Tauri builds MAY use native gamepad APIs for parity, but the browser build MUST remain fully playable on a pad without them.
UX-1250 Input coexistence: keyboard, mouse, touch, and gamepad MUST be hot-swappable at any time without a mode switch; on-screen hints MUST follow the most recently used device. External keyboard, mouse and gamepad MUST work on mobile platforms.
UX-1255 The command reference — every command, its binding, its applicability and its availability per platform — MUST be generated from the command catalogue rather than maintained by hand, and MUST be viewable and exportable from the Key Bind dialog. A hand-maintained list is a list that will be wrong by the second release.
14. Onboarding and the encyclopedia
UX-1260 The tutorial system MUST be activatable from setup and from within a running game, and MUST cover at minimum, in this order: the three phases and what each permits; issuing a Posture with its Sanctions; reading the initiative readout and changing it with Stance; reading the combat preview; watching a Cascade and reading the after-action report; using the Dispatch; and reading the Watch.
Rationale: these are the seven things a player cannot work out by clicking, and the first two are the ones that decide whether the turn model feels like a plan or a gamble.
UX-1270 The tutorial ladder MUST follow the specification set's own disclosure structure rather than inventing a parallel one: Initiative Tier 1 → 2 → 3 (TM-1040), Sanction presets → the four dials → Formations → Postings and Requisitions (CM-1980, CM-1990), and the Industry ladder's four levels with their unlock cards (US-950). Each stage MUST be a playable scenario authored per 07-modding-content.md, and each MUST be skippable.
UX-1280 Contextual help: every tooltip (UX-590) MUST link to the relevant help topic; the help system MUST be searchable and hyperlinked, with the full content available offline (UX-110). Help and encyclopedia content are DOM documents, so browser find-in-page, text selection, copy, and deep-linkable topic URLs MUST all work rather than being reimplemented (UX-045).
UX-1290 The encyclopedia MUST be complete from turn one for every player, regardless of their Industry level, of what they have built, and of what they have seen (US-930). It MUST present, per unit definition: the full stat line, capability flags, the one-sentence role, the two units it counters and the two that counter it, its build requirements and the Industry level that unlocks it, and its carriage arithmetic. It MUST present the terrain set with movement costs per movement class, passability, defensive values and concealment. It MUST present every unlock card and contact briefing already seen (UX-960).
It is a reference view only: it MUST NOT expose any fact the player's fog-filtered view withholds about a specific unit or city on the map (UX-020).
Rationale: there is fog over the map, not over the rules. A player who is out-teched should be beaten by tempo and by worse decisions, never by not knowing what the thing that killed them was.
UX-1295 Live derived values. The numbers a player acts on MUST be shown as core-computed live values at the place they act on them, and the client MUST compute none of them (UX-040):
| Derived value | Where it MUST appear | Source |
|---|---|---|
| Effective Works, decomposed into every step | city panel, Production Overview | EC-1790 |
| Levy, decomposed, with the Integration at which the next point arrives | city panel, Empire Ledger | EC-1800 |
| Integration and Unrest, their deltas, every contributing term, and the projected turn | city panel | EC-1810, EC-1820 |
| Garrison requirement, points present, and the next threshold | city panel | EC-1830 |
| Turns to completion for the head queue item | production list, Production Overview | US-1250, US-1270 |
| Industry upgrade cost, Works gain for this city, and payback | Industry ladder panel | US-890, US-940 |
| Initiative and every active term | selection panel, during Orders | TM-1080, TM-1090 |
| Win probability, loss bands, pivots | attack preview | CB-2390, CB-2340 |
| Screen coverage, present and candidate | Screen authoring | CM-450, CM-460 |
| Track progress, threshold, ratio, Vigil remaining | the Watch, the Board | VC-030, VC-490 |
| Cost of Conquest, every line | the Cost of Conquest panel | EC-1850 |
| Endurance remaining and the return envelope | unit panel, endurance overlay | US-2040 |
UX-1300 First run: after profile selection (UX-060) the client SHOULD offer a solo game at the Field disclosure profile, on a Small map, under the Standard War Terms, as the way to learn the game — and MUST make clear that every system hidden by that profile can be enabled immediately.
UX-1305 Unlock cards (US-950) and contact briefings (US-960) are onboarding surfaces: each MUST be dismissible in one action, MUST NOT interrupt the Orders phase more than once per turn in aggregate, and MUST be re-openable from the encyclopedia.
15. Accessibility
Because every surface except the tile map is real DOM (UX-010), accessibility here is not a compatibility layer bolted onto an engine's control toolkit — it is the platform's own semantics, assistive-technology stack, focus model, and zoom. The requirements below take that for granted and spend the effort where it is genuinely still hard: the canvas.
UX-1310 Colour. The per-seat foreground/background/text trio (UX-210) MUST be preserved, and the client MUST ship colourblind-safe default palettes plus optional pattern and shape overlays so that ownership is never encoded by hue alone. The same applies to every graded encoding this document introduces: fidelity grades (UX-420), supply states (UX-1010), condition (UX-1125), victory bands (UX-1155), and chart series (UX-1145) MUST each be distinguishable without colour vision.
A player MUST be able to re-map any seat's colours for their own view in game; the preference is saved with that player's seat and affects only their view. The DOM layer MUST honour the platform's contrast preferences, including forced-colours modes, without losing information, which means no control may carry its meaning solely in a background image or a CSS-only colour. The canvas cannot participate in forced colours; on the map, the palettes and pattern overlays of this requirement are the substitute, and the client MUST enable them by default when a high-contrast preference is detected.
UX-1320 Screen-reader support MUST be delivered through native DOM semantics, not through a parallel description tree:
- Semantic markup first. Every non-map surface MUST be built from real elements — buttons, links, headings, lists, tables, form controls, native modal dialogs — with ARIA only where no native element exists. Every interactive control MUST have an accessible name and MUST expose its state (pressed, expanded, disabled, selected, current). A disabled command MUST expose why (UX-560), so a screen-reader user learns what a sighted player learns from the tooltip.
- Live regions for changing values. The phase indicator, the Watch, the Dispatch, the empire strip and the location readout MUST be announced through live regions at severity-appropriate politeness: routine changes polite, combat results and Alarms assertive. Announcements MUST be coalesced and rate-limited so that a large Cascade produces a readable summary rather than an unfollowable stream — the same obligation the clustering requirement of UX-350 places on the visual channel.
- Tables are tables. The data-dense surfaces of UX-045 MUST expose row and column headers so a screen reader can announce "Nyborg, Industry 2, Yard 14 of 21, producing Armour, 3 turns" while traversing — which is what makes UX-1050 usable non-visually at all.
- The map MUST have a DOM equivalent. A WebGL canvas is opaque to assistive technology, so the client MUST provide an accessible map view: a focusable DOM grid built strictly from the player's fog-filtered view (UX-020), exposing a keyboard-driven map cursor that reports each cell's terrain, movement cost and defensive value, contents with fidelity grade and observation age (UX-420), ownership, and Chebyshev distance from the selection — the same payload the location readout shows (UX-520). Every location-targeting interaction MUST be issuable from it, including all of UX-735's geometry authoring. It MUST be rendered as a bounded window of DOM cells around the cursor rather than as a grid of the whole map, and MUST stay within UX-050's budgets. It MUST stay in sync with the canvas camera in both directions: moving the cursor scrolls the map, and centring the map moves the cursor.
- Playback MUST be followable non-visually. Step-through (UX-345) MUST announce each observed event as a sentence naming actor, action and location, and the after-action report (UX-355) MUST be the complete non-visual equivalent of the animation — a player who never watches playback must lose nothing but the animation.
- Verification is a gate, not a checklist. Automated accessibility checks MUST run in CI over every screen, and the client MUST be manually verified each release against a named matrix of at least NVDA on Windows, VoiceOver on macOS and iOS, and TalkBack on Android. The granularity that actually reads well in those three is open question 8.
UX-1325 Focus management MUST be explicit and centrally owned:
- There MUST be exactly one focus authority; no component may move focus on its own.
- Opening a modal moves focus into it and traps it; closing restores focus to the control that opened it. Opening a non-modal panel MUST NOT steal focus, which matters because most of this client's panels are non-modal by requirement (UX-900).
- The map canvas MUST be a single focusable element in the tab order; cell-level focus lives in the accessible map view (UX-1320.4).
- Button sections and toolbars MUST use roving focus — one tab stop per section, arrow keys within it — so a player does not tab through fifty commands to reach the map.
- A visible focus indicator MUST be present on every focusable element, MUST meet contrast requirements against both the DOM background and the map beneath transparent regions, and MUST NOT be suppressed anywhere for aesthetic reasons.
- Skip links MUST jump directly to the map, the command bar, the Dispatch and the Watch.
- There MUST be no keyboard trap other than an intentional modal trap, and every trap MUST be escapable per UX-014.5.
UX-1330 Zoom and scaling. Two independent mechanisms MUST both work:
- Browser/OS zoom — the interface MUST remain fully functional from 50% to 500% browser zoom with no loss of function, no horizontally clipped controls, and no overlapping panels. Layout MUST therefore be built on relative units and reflow, not fixed pixel geometry, and the client MUST respect a browser or OS minimum font size instead of overriding it.
- In-app UI scale — global UI scale MUST remain adjustable from 75% to 200% with independent text scaling, composing with browser zoom rather than fighting it, and MUST apply to the DOM layer only; map zoom is a separate control (UX-380).
The canvas MUST render at the current device-pixel ratio and re-render when it changes, including when it changes as a side effect of browser zoom (UX-385). Touch targets MUST be at least 44×44 CSS px at 100% scale.
Rationale: browser zoom is the single most-used accessibility feature on the web and the one an engine-rendered UI cannot offer at all; making it a first-class requirement is a direct dividend of the DOM decision.
UX-1340 Full keyboard operability: every function in this document MUST be operable without a pointer — panels and menus by focus traversal, map targeting via keyboard targeting (UX-730) and the accessible map view (UX-1320.4), every map-anchored menu (UX-016), and every geometry-authoring interaction (UX-735). Tab order MUST follow visual order, Tab MUST NOT be rebound (UX-1225.3), and no command may be reachable only by pointer.
UX-1350 Reduced motion and audio cues: a reduced-motion setting MUST disable non-essential animation and MUST default to on when the platform reports a reduced-motion preference rather than waiting to be found in settings. With reduced motion on, Cascade playback MUST default to instant with the after-action report (UX-345, UX-355) rather than being removed. All information conveyed by sound MUST have a visual counterpart, and every per-unit state of UX-430 MUST be readable on the unit panel and not solely as a map badge.
UX-1360 Localisation: every UI string MUST come from the localisation system (07-modding-content.md); layouts MUST tolerate 40% string expansion and RTL scripts. In the DOM layer this is largely the platform's job and MUST be left to it — text renders as real text nodes so the browser performs line breaking, bidi resolution, and font fallback, the document root carries the active language and direction, and RTL is achieved with logical layout properties rather than a mirrored stylesheet.
Two obligations do not come free:
- Fonts — coverage MUST include at least Cyrillic, Japanese, Chinese, Hangul, Arabic and Devanagari scripts, and font resources MUST be subset and loaded per locale against a stated download budget. CJK coverage is large enough that shipping it to every player is not acceptable in a browser-first product.
- Text on the map — the canvas inherits none of the above. Map-anchored text (labels and waypoints, UX-870) MUST be rendered as DOM overlays (UX-016) rather than drawn into the canvas, so it is localisable, selectable, and readable by assistive technology. The Chip-tier short class labels (UX-390) are drawn on the canvas and MUST use a glyph atlas generated for the active locale's labels; a pre-baked Latin atlas is insufficient for a translated or modded unit set. The per-locale font budget and atlas strategy are open question 9.
16. Mobile and touch adaptation
UX-1370 Responsive layouts: the interface MUST adapt by breakpoint — phone (< 600 CSS px width): the command bar becomes a horizontally scrollable strip, the Dispatch and menus collapse into drawers, the phase indicator and Watch merge into a compact top strip that keeps one row per player; tablet (≥ 600 CSS px): near-desktop layout with docking intact (UX-550). No layout may horizontally clip a command — everything remains reachable by scroll or drawer, and every drawer MUST have a visible handle rather than depending on an edge gesture (UX-1230). Breakpoints MUST be evaluated against the surface the interface actually occupies, so the same logic serves a narrow browser window, a phone, and a Tauri window (UX-018); layouts MUST respect display safe-area insets. Because these are DOM layouts they MUST hold at every browser-zoom step of UX-1330, not only at device widths.
The Watch MUST remain visible at every breakpoint (UX-1150). Where six fields per row do not fit, the client MUST reduce fields by the priority identity, value, Vigil, nearest Track, Tenure, bar and MUST make the full row reachable in one action — it MUST NOT drop a player's row.
UX-1380 Touch-first fidelity: the selection and targeting model of UX-650 is already touch-phrased and MUST work identically on touch; the drag-to-order path (UX-670) is the touch default. Every geometry-authoring interaction (UX-735) MUST have a touch-complete form, including multi-tile Survey painting and 16-waypoint March routes.
UX-1390 Hover replacements: everything the desktop client delivers on hover — the location readout, control tooltips with bindings, route ETA — MUST be reachable on touch via long press (UX-1230) or an inspect toggle.
UX-1400 Radial context menu: long press on the selection MUST open a radial or sheet menu of its applicable commands, equivalent to the desktop context menu (UX-600), with the same disabled-with-a- reason behaviour (UX-560).
UX-1410 Virtual directional pad: an optional on-screen 8-way pad MUST provide directional step targeting on touch devices (UX-730).
UX-1420 Panels on phones: modal and large panels — the city panel, the attack preview, the Board, the Production Overview — MUST present as full-screen or bottom-sheet views on phone-class devices, preserving every control. When the on-screen keyboard opens for a text field (UX-045) the client MUST track the visual viewport so the focused field and its confirm control remain visible, and MUST restore scroll position when the keyboard closes.
UX-1430 Mobile performance and battery: the client MUST support a frame-rate cap, a reduced-effects mode, and background suspension that preserves all unsubmitted state; resuming MUST restore the exact interface state. Suspension MUST be driven by page visibility, and unsubmitted state MUST be written to local storage on backgrounding rather than on termination, because a mobile WebView may be killed without notice (UX-1190). On resume the client MUST assume the WebGL context may have been discarded and recover per UX-385. The memory ceiling of the mobile WebView, not the device's RAM, is the budget UX-400 must fit inside.
UX-1440 Correspondence hand-off. On mobile, an incoming your-turn notification (05-multiplayer.md, 08-services-platform.md) MUST deep-link into the correct game and MUST land on the previous Cascade's review (UX-290) rather than on a bare map, because the first question a returning correspondence player has is what happened while they were gone. Deep links and notification delivery go through the adapter of UX-018, so the same link resolves whether it is opened as a URL in a browser tab or handed to a Tauri build by the OS.
UX-1445 Correspondence is a first-class shape, not a degraded one. On any platform the client MUST make a whole turn completable in one sitting with no live opponent present: enter, review the last Cascade, revise orders across the whole empire, commit, leave. Nothing in this document may require a second visit within a turn, and no surface may require a live connection to another player to be usable.
17. Contracts on other documents
Obligations this document places on subsystems it does not own, and the obligations it discharges, recorded so that a change on either side is detectable.
UX-1450 10-turn-model.md MUST provide: the explicit phase value (TM-090); each unit's exact Initiative with its per-term breakdown and the active tier (TM-1080, TM-1090); the player's own relative activation order (TM-380); the coarse enemy band (TM-1100, TM-1110); effective and derived Stance (TM-730); the compacted per-player observation stream (TM-2150) delivered as one payload in multi-human games (TM-2170); the count of own activations that fell back to no action (TM-2320); the per-unit skip reason (TM-2350); and the frozen own-unit activation order after a Cascade (TM-2340). This document discharges TM-2720.
UX-1455 13-command.md MUST provide: Attention Events carrying class, cause key, affected
entities, the integer terms S, T and A, a plain-language summary and an ordered list of offered
resolutions (CM-1610), with the client owning P, ranking, deduplication and presentation
(CM-1620); the next-intended-action line as a pure function of committed state and the knowledge view
(CM-1910, CM-1920, CM-1930); and CommandDecisionRecords retrievable for at least 20 turns
(CM-1940, CM-1950). This document discharges CM-2200 and presents the Command View of CM-1880 in
UX-395.
UX-1460 11-combat.md MUST provide the preview through the single resolution function (CB-2140) with the content and ordering of CB-2390, the fog-limited prediction of §17 with its pivot analysis (CB-2330, CB-2340), and a combat event sufficient to reconstruct a resolution (CB-2450, CB-2460). This document discharges CB-2830, and adds one decision of its own: the resolution trace is exposed in solo and hotseat games only (UX-1120).
UX-1465 12-economy.md MUST provide the decompositions of its §14.1, the Cost of Conquest lines (EC-1850), the Empire Ledger content (EC-1870), the supply and projected overlays (EC-1880), the Digestion Forecast (EC-1890), and the economic Attention Events (EC-1900). This document discharges EC-2350, and additionally binds itself to EC-020 and EC-050 in UX-1090: no economic quantity rendered as a bar without its integer, and every brake named, numbered, forecast and counterable at the place it applies.
UX-1470 14-victory.md MUST provide the Seal record sufficient to redraw the Board for any past turn without recomputation (VC-250, VC-2400), the published band per player per Track (VC-2160), the report quantum (VC-2250, VC-2260), the Bellwether set (VC-2280), the generated Terms paragraph (VC-1960), the calibration estimate (VC-2030) and the lint findings (VC-2100). This document discharges VC-3170 and computes no victory value.
UX-1475 01-game-rules.md requires this document to render remembered contents distinctly from observed contents with age and fidelity, and to draw Contact uncertainty as regions rather than icons (GR-1190, GR-1870). UX-420 discharges both, and extends them with exclusion rendering (GR-1350) and decay warning (GR-1300).
UX-1480 02-units-and-industry.md MUST provide, in addition to what it already specifies: a short display label per unit definition for the Chip tier (UX-390) and an icon key, neither of which the current schema (US-070) carries; the greyed-build explanation naming the failing clause (US-370); the unlock card and contact briefing content (US-950, US-960); and the endurance warning (US-2040). This document discharges US-2190.
Note on citations: US-2120 declares every US-NNN identifier from the withdrawn
02-unit-sets-content.md stale, including the ones an earlier revision of this document cited. Every
US- citation in this revision has been re-resolved against the current 02-units-and-industry.md.
UX-1485 03-architecture.md owes this document two reconciliations:
- Map tiers and the reference fixture. AR-690's tiers (Standard 200×200, Large 1,000×1,000, Extreme 4,096×4,096) and AR-760's reference game (1,000×1,000, 2,000 cities, 10,000 units) do not correspond to any size 01-game-rules.md defines. GR-230's shipped tiers top out at 240×168 with 269 cities, and GR-120 caps any map at 2,048 per axis. UX-050 states this document's budgets against GR-230 and will adopt whatever single fixture 01 and 03 agree; the client MUST NOT be gated on two different fixtures.
- Retraction is no longer required by the client. AR-330's
RetractOrder, and the retraction soundness property AR-950(f) tests, exist to serve an undo affordance this document no longer has. Under TM-360 revision is free untilCloseOrdersand after that there is no undo (UX-285, UX-710). 03-architecture.md may retire the mechanism as far as this document is concerned; the citations to UX-710 and UX-720 it currently carries should be re-read against those requirements' new content.
03-architecture.md MUST continue to provide: the host-agnostic core (AR-010) and its worker execution (AR-780) for UX-055; typed order records and typed rejections (AR-245, AR-260, AR-320) for UX-738; structural view filtering (AR-420) and incremental fog (AR-480) for UX-020; view serialisation (AR-500) for the client's transport; and deterministic pathfinding (AR-750) for UX-670.
UX-1490 05-multiplayer.md owns transport of order records and observation streams, lobbies and invitations, the presence channel this document renders in UX-300, single-payload stream delivery (TM-2170), correspondence hand-off and turn notifications (UX-1440), and spectator policy (UX-365). 08-services-platform.md owns accounts, profiles and binding sync (UX-220, UX-1200). 07-modding-content.md owns localisation (UX-1360), generator option schemas (UX-170), content editing (UX-070) and tutorial scenario authoring (UX-1270). 06-ai.md owns the AI seat contract and the disclosure of any AI advantage this document renders in UX-230.
UX-1495 No document may require this one to present a value at a precision, or from a source, that its owning document does not publish. Where a presentation requirement here appears to exceed what a document publishes, this document is wrong and MUST be corrected here rather than by widening disclosure there.
Open questions
Every question below is one we own. There is no external artefact to check any of them against.
The reference fixture. UX-050 gates the client on a Vast game — 240×168, 8 players, 269 cities, 3,200 live units — chosen to match GR-230's largest shipped tier and the largest unit population any neighbouring document budgets against (VC-310). 03-architecture.md's AR-690 tiers and AR-760 reference game describe a much larger world that no rules document defines. One fixture has to win, and it should be the one the rules produce. Until 01 and 03 agree, the client's measured budgets and the core's are not comparable (UX-1485).
iOS. Tauri 2 mobile maturity for App Store distribution, WKWebView performance headroom for both layers, and empirical edge-gesture capture behaviour (UX-018, UX-1230). This is the platform where the canvas budget and the DOM budget compete for the same ceiling.
How much of a Cascade should be animated? TM-2280 requires that playback not scale linearly with observed events and delegates the presentation here. UX-350 sets clustering as the mechanism but not the threshold. The right number is almost certainly a function of how much the player observed rather than how large the Cascade was, and it needs playtest at turn 20 and at turn 150, because a sixty-second animation is a pleasure at one and an obstacle at the other. The signal to watch is the skip rate.
Which overlays default on? Eleven overlays (UX-460) is more than any player will discover by toggling. The disclosure profiles (UX-060) reduce the set, but the choice of which three are on for a new player is a real design decision that has not been made. Supply, intelligence and victory sites is the current guess.
Browser storage. Per-browser quota and eviction behaviour for event-sourced saves (UX-110), and whether a first save should require an explicit export or cloud-sync opt-in rather than merely offering one.
The Canvas fallback. How often does WebGL context loss actually occur on target mobile GPUs, and is the reduced Canvas tier of UX-385 worth maintaining, or is it dead weight guarding a case that does not happen?
Browser-reserved keys. The per-browser, per-platform reserved sets and the final default binding table (UX-1220, UX-1225). The sets differ across Chromium, Firefox and Safari and move between releases, so this needs an owned, re-tested policy rather than a one-time survey.
Accessible map granularity. What cursor-window size and announcement format actually read well in NVDA, VoiceOver and TalkBack (UX-1320.4)? A DOM grid is necessary and not sufficient, and this needs testing with screen-reader users before the design is fixed. The harder half is UX-1320.5: what a Cascade sounds like when it is announced rather than watched.
Localisation budget. What per-locale download size is acceptable for CJK coverage in a browser-first product, and does the Chip-tier short-label atlas need per-locale generation at load time or can it be pre-built per shipped unit set (UX-1360)?
Map performance at the Strategic tier. UX-050 asserts 60 fps with the whole Vast map on screen using entity aggregation and, above GR-250's tested envelope, an overview raster. Whether Phaser 3's tilemap can host those two mechanisms or whether they need a bespoke instanced render path is the largest technical unknown the stack choice creates. It is a much smaller unknown than it was when the target was a million tiles.
Worker transfer. Whether whole fog-filtered view snapshots for the UX-050 fixture cross the worker boundary fast enough for interactive play, or whether the incremental
ViewDeltaform is required on the client too (UX-055). At 40,320 tiles this is probably a non-question; it is recorded because the answer changes if custom maps near GR-120's ceiling become common.Does rolling planning help or confuse? UX-330 permits staging next-turn orders during playback. It removes dead time in correspondence play and it introduces a state where two kinds of order look identical and behave differently, which is the sort of thing that produces bug reports about orders that "did not save". The safe answer is to ship it off by default and measure.
The Dispatch on a phone. Seven items with one-action resolutions is calibrated for a desktop panel. On a phone the same seven items are the whole screen. Whether the phone cut should be lower, or whether the digest should lead and the items follow, is unresolved (UX-810, UX-1370).
Does the two-pivot limit read? CB-2350 caps the preview at two pivots and this document presents them (UX-1115). Two thresholds plus two loss bands plus a verdict is already a dense panel, and on a phone it may be too dense to act on quickly. The alternative — one pivot on small screens — loses exactly the case where the second pivot was the one that mattered.
Shared annotations. Should multiplayer-visible map annotations and pings ship, or do labels remain private (UX-870)? It depends on 05-multiplayer.md's scope and on whether a game with no diplomacy wants a channel for coordination that is not chat.
Decisions recorded here because an earlier revision left them open
- Centring is idempotent (UX-490). The inherited progressive-zoom-then-bounce behaviour is removed rather than reproduced. A control that does something different on the second press is a control players stop using.
- One selection and targeting model (UX-650), not four click regimes. The cost of a mis-click is now a wrong order, and every order is revisable until the phase closes.
- No post-close undo, and no retraction mechanism (UX-285, UX-710, UX-1485). The Orders phase is the undo window and it is total.
- No processing queue and no "next unit awaiting orders" (UX-690). Orders persist (TM-280) and attention is routed by the Dispatch.
- Step-through is available in every game shape (UX-345), because in a multi-human game the whole observation stream is already held before playback begins, so stepping through a recording pauses nobody.
- The client never simulates. A "dry run" of the coming Cascade was considered and rejected: it would require a second implementation of the engine (forbidden by UX-040) and would leak resolution against hidden state. The next-intended-action line (CM-1910) and the initiative readout (UX-310) are the answer, and they are honest ones.
- The Roster's default grouping is Posting, then Formation, then Posture (UX-800).
- The full combat resolution trace is solo and hotseat only (UX-1120), because the trace names fields a viewer's fidelity grade may withhold.