# Ship Class Calculator — UX Phase 30 fuses the ship-class designer and a calculator into one sidebar tool (`lib/sidebar/calculator-tab.svelte`). It replaced the standalone designer view/route from Phases 17/18. All numeric math lives in `pkg/calc` and is reached through the `Core` WASM bridge; the calculator holds input state and orchestrates, it never computes. ## Modes - **Calculator** (`ship`): the full tool — design area, derived results, planet build, goal-seek. - **Modernization**: reuses the design area and shows per-block and total `BlockUpgradeCost` from the current tech to an editable target tech. The design-area component is extracted (`lib/calculator/ship-design-area.svelte`) so the future ship-group upgrade flow can reuse it. The `path` mode from the original plan was dropped (MVP path-finding is brute force); reach circles on the map replace it. `bombing` is folded in as a per-ship result rather than a separate mode. ## Areas 1. **Ship Class design area** — five blocks (drive, armament, weapons, shields, cargo) and four tech levels (drive, weapons, shields, cargo). Tech defaults to the player's current tech and shows a lock icon once overridden; clicking it resets to the default. 2. **Calculator area** — derived results: empty/loaded mass, empty/ loaded speed, attack, defence, bombing (per ship), cargo capacity. A load toggle (empty / full / custom) sets the cargo load that the loaded-column results use. 3. **Planet area** — when an own planet is selected on the map, shows its MAT (overridable) and the single-turn build rate (ships per turn, turns per ship). The realistic multi-turn forecast with CAP/COL supply is Phase 34. ## Locks and goal-seek Two distinct lock semantics share one icon (a closed padlock; it only appears once a value is pinned, click to release): - **Override locks** on inputs that have a default — the four techs and the planet MAT. Editing one overrides the default; the lock resets it. Any number may be overridden at once. - **Goal-seek locks** on derived results. Pinning a result back-solves the single input it claims, which then renders read-only (computed): | result | claims | | ------------- | ------------- | | attack | weapons block | | defence | shields block | | empty speed | drive block | | loaded speed | drive block | | empty mass | cargo block | | loaded mass | cargo load | Only **one** result may be locked at a time (the others' lock affordances disable with a tooltip). An unreachable target — e.g. a speed at or above the stripped-hull ceiling `20 × driveTech`, or a solved block that fails the value rules — leaves the locked cell in a red error state and does not apply. Inverse solving lives in `pkg/calc/solve.go`; the bisection for defence → shields is the only non-analytic case. ## Create / load / delete The name field is a combobox over the player's existing classes. Picking an existing class loads it as a template (so you can tweak and Create a new one); Create is disabled while the name is invalid or duplicate (reusing `lib/util/ship-class-validation.ts`). When a saved class is loaded, a Delete affordance appears. Create / Delete reuse the existing `createShipClass` / `removeShipClass` order-draft flow, so the optimistic overlay reflects the change immediately. Ship classes are immutable after creation (per `game/rules.txt`), so there is no edit — only Create-new and Delete. ## Reach circles When an own planet is selected in calculator mode, the calculator publishes the planet origin and the design's loaded speed to a shared store (`lib/calculator/reach.svelte`). The map view (`lib/active-view/map.svelte`) reads it and draws 1–3 thin concentric reach circles (`map/reach-circles.ts`) for 1/2/3 turns. The ring count shrinks as speed grows: a ring is dropped once the previous one reaches the torus wrap-midpoint (half the shorter side) or the no-wrap map edge (farthest corner). The circles clear when the selection clears or the design is invalid. ## State preservation and history Calculator inputs are component-local state. The sidebar keeps the tab mounted while the player navigates between active views, so inputs persist across view switches per the global state-preservation rule (`ui/docs/navigation.md`). Tech levels track the rendered report, so in history mode the calculator computes against the viewed snapshot's tech. The ship-classes table and the view/bottom menus open the calculator via a shared request store (`lib/calculator/load-request.svelte`): the in-game layout flips the sidebar to the calculator tab and the calculator loads the requested class (or starts a fresh design). ## Layout and mobile Everything stacks vertically to fit the 18 rem sidebar; the design and result rows use compact two-column (ship/tech, empty/loaded) grids. On mobile the sidebar is the existing bottom-sheet/overlay; the calc bottom tab opens it. ## Runtime note The new bridge functions are only present after `make wasm` rebuilds `ui/frontend/static/core.wasm` (needs TinyGo). Vitest injects a fake `Core` (`tests/fake-core.ts`) mirroring `pkg/calc`, so unit/component tests do not need the rebuild; the Playwright suite and the live app do.