- Sidequest patrol contracts now pay an XP reward alongside credits, fixed at accept as the experience worth of defeated contacts (contacts × wave total × roster xp) and granted via XpManager with a 'sidequest' source tag on claim
- Cargo sales earn 1 XP per credit above each bay's recorded purchase cost; bays with no cost basis or sold at/below cost earn nothing, granted via the same pipe with a 'trade' source tag
- New CargoHold.costsFor() returns FIFO per-bay costs for the profit basis without mutating the hold
- Job board payout/claim lines and console toasts display both credits and XP when present; old saves without an xp field derive it from the encounters spec or fall back to 0
- CargoHold gains a parallel `costs` ledger (credits/unit actually paid, or null when no cost is on file), kept aligned across add/remove/set/capacity/dropFirstNot and serialized via toJSON/fromJSON
- ShopView stamps each bought bay with the trade's unit price so the console can compare exit prices against what was really paid
- ShipScreenModel/ShipWindow surface per-bay costs, a total-cost hold meta line, and a "PAID" chip + reference line in the bay manifest; bays with no cost read "NO COST" instead of inventing one
- GameScene passes the bay's recorded cost into scoutGood() so exit profits re-baseline against the purchase (falling back to the catalog value when absent), and restore/quest-handoff paths load null costs cleanly for legacy saves
- Adds arid-shop-02 video asset and updates cargo/saves/ship-window/shop-ui tests to cover the new ledger
- Split each row into a title line (name, "N left"/"SOLD OUT" strip, price,
stepper with `stock.gap` padding between blocks) and a full-width desc line.
- Add `buyList.stock` config (w, gap, fontSize, color) for the inventory strip
and use it for sizing/truncation so the name never collides with the strip.
- Update `_btnXs`, `_nameMaxW`, `_descMaxW` layout helpers to place the right
block on the title line and give line 2 the row's full text width.
- Refresh shop-ui tests to assert title-line padding, truncation budget, and
desc wrap within the full row width.
- Replace producer/demand class pricing with strict 1:1 origin assignment;
each settled system sells exactly its signature good at a discounted origin
price, and the catalog remainder becomes a hub pool resold by stations/capitals
- Make banded hop distance the dominant sell-price factor (x1.0 to x7.0),
with planet-type demand as a secondary +10% modifier, and cap every shop's
sell price at its own buy price to eliminate the v1 infinite-credit loop
- Add Stock.js for the time-gated daily batch (6 units per origin per wall-clock
day) — the economy's only stateful piece; wire it through GameScene,
SaveData capture/restore/reset, and the shop UI stepper clamp + SOLD OUT strip
- Update Economy.js with assignOrigins pass, new buyPrice/sellPrice/shopFor
logic, tradePreview stock-cap backstop (buyback exempt), and distanceFactor
- Rework shop UI: BuyList shows "N left"/"SOLD OUT" per batch-limited row and
clamps the stepper; ScoutPanel shows MADE IN (origin system) instead of GROWN ON;
ShopView wires stockRemaining/stockCapsFor/spendStock into preview and confirm
- Update all dev tests (economy, cargo, shop-ui, saves) to assert model v2
invariants including the anti-loop sweep, batch clamp/backstop, and save round-trip
- Every `dev/*.test.mjs` now imports the Phaser stub loader, so plain
`node dev/<name>.test.mjs` works without `--import`.
- New `dev/run-all.mjs` runs the whole suite in isolated Node processes
with filtering, concurrency, timeouts, and aggregated failure output.
- Update README and adjust dynamic imports/paths for headless test reliability.
- New `js/galaxy/PlanetClasses.js` deals non-home planets from one shared deck so every class lands at ~1/N of the galaxy while star type still flavors local systems
- Config knob `systems.planetClassBalance` (enabled + bias) blends an even split with the star-mix preference; bias 0 = exactly even, 1 = old free preference
- Stamp `galaxy.planetClasses` is read back by content generator, frame pass, economy, and type preview so lazy/eager generation agree
- `rollSystemComposition` accepts optional stamped classes; falls back to per-type weighted draws when balance is off or stamp absent
- Fix arid-surface video path in landing.json (was pointing at ice surface)
- Update shop-ui and trade-scout tests to pick rows the current system actually wants, and handle home-world exit resolution
The Shop button on the deck is now always live — the shop sub-bar opens on every world. The "Special Delivery" capital-landing witness now gates only the shop's Buy/Sell tab (ShopView reads GameScene.isTradeUnlocked, which replaces isShopUnlocked), so trading stays dark until the player lands on the neutral capital while the rest of the shop UI is reachable everywhere.
- Rename GameScene.isShopUnlocked → isTradeUnlocked and update all references (SaveData, SurfaceScene, quests.json comments)
- Remove the deck-slot enable guard in SurfaceScene.toggleShop / setBarLocked; the slot now re-arms with the rest of the deck
- Add ShopView.isTradeUnlocked + gate checks in setTab / _onTabTap; gray the Buy/Sell tab via _refreshTabs until the gate lifts
- Update dev probes (scout-*, shop-*) to lift gs.story.capitalLanded so the trade panel is reachable
- Extend shop-ui.test.mjs with a TRADE GATE case covering closed/lifted states
- Trade scout best-exit rows now open a "SET DESTINATION" confirm dialog
(SurfaceScene.askSetDestination + ConfirmOverlay), confirming sets the
GameScene destination; local-system exits are rejected with a note.
- Surface scene builds a corner credits/minerals readout over the shop clip,
refreshed after trades and torn down on leave.
- Sell-bay selection restyled: green stroke + tinted fill + upper-left
checkmark badge (data/shopui.json slot config), replacing the cyan ring.
- Add lava/capital-neutral music, videos, and landing data entries; remove
tresle.png in favor of capital-neutral-surface.png.
- New dev probes: scout-dest, shop-hud, shop-select-shot, map-dest-shot,
cdp-cast; extend shop-ui.test.mjs for selection visuals and exit taps.
- Pressing the open tab now closes the panel (back to entry state) instead of
rebuilding it; pressing again re-opens with the selection restored, so hiding
the panel no longer loses the player's pending trade.
- Save the SELL/BUY maps onto ShopView (_pendingSell/_pendingBuy) on toggle-close
and tab switch; _restorePendingSelection() re-applies them when the panel is
rebuilt. The sub-bar stays live while closed: NET shows the delta, Clear
cancels the saved trade, Confirm executes it.
- preview()/clear()/_afterTrade()/_hasAnySelection() now treat a null tab with a
pending selection as an active trade, so the buyback path is no longer shadowed.
- Rename ScoutPanel.data to report: Phaser's GameObject reserves .data (its Data
manager), and a plain object there crashed GameObject.destroy(). Update all
callers in ShopView, tests, and dev/shop-shot.mjs.
- Simplify the scout price line to credits only ("9 CR") and drop the now-unused
priceSell style from shopui.json; update goods.png/psd.
- Add setSellMap()/setBuyMap() to TradeGrid/BuyList to restore a saved selection
onto freshly rebuilt panels, and cover the toggle flow in shop-ui.test.mjs plus
a toggle probe in dev/shop-shot.mjs.
- Tap a BUY-list row's box (outside the stepper) to swap the cargo grid for a strategy report showing the good's identity, producer/demand classes, and top-N charted exits ranked by sell price with per-unit profit.
- New pure TradeScout module computes the report from galaxy data and visited systems; new ScoutPanel renders it over the SELL grid area.
- BuyList gains tap detection (distinguishing taps from drags), a focus glow on the scouted row, and an onRowTap hook.
- ShopView wires open/close/swap semantics: same box toggles off, different box swaps report, dead-space or panel tap closes and restores cargo grid; sell-grid input is disabled while scout is shown.
- Add Trestle character art, Carol Q2 speech lines, shopui.json scout config section, and tests for both the pure logic and UI integration.
- Widen the tab sub-bar from a button-width pill to the full deck span (left/right edges of the main action bar), centering it on that span.
- Pack tabs at the left edge and Clear/Confirm + NET readout at the right edge, compressing gaps rather than button widths when the deck is narrow.
- Right-anchor the NET value (origin 1,0.5) and reserve its widest slot so growing totals never shift the label or overflow the bar.
- Extend SurfaceScene.shopAnchor to expose left/right edges for the sub-bar layout.
- Add sub-bar probe assertions in dev/shop-shot.mjs and unit tests in dev/shop-ui.test.mjs covering span, alignment, non-overlap, and NET math.
- Add carol character image and lava-land video assets.
- Rows that spill past the viewport edge used to render whole, ghosting over
the BUY title or onto the Confirm bar; now only elements fully inside the
window render (this Phaser build can't crop glyphs), and row plates draw
just their in-window slice for a clean cut.
- Clipped-out steppers are no longer tappable: hit-testing gates on element
containment, not just row visibility.
- Add a right-edge scrollbar (track + drag/jump thumb) that tracks scroll
position and hides when content fits the viewport.
- Extend shop-shot/dev tests to verify the clip invariant, dead-click
behavior, and scrollbar geometry; add a geckodriver screenshot helper.
The panel was centered as a group; now the SELL grid's left edge sits at
the surface HUD's planet-text left edge (landing.hud.marginX, 24px —
same key the HUD name/line use) so the cargo grid lines up vertically
with the planet name above it, and the BUY list stretches from just
past the grid all the way to the right edge minus that same padding
(1280px window: BUY list 560 -> 770px wide; the 440px floor stays as
the narrow-window fallback). The 16px gap between the panels is
unchanged, and the sub-bar still anchors to the deck's Shop button.
shopui.json's buyList.w is now only the headless fallback width.
The center column is gone: Confirm + Clear move onto the tab sub-bar
([tabs] | [Clear][Confirm] | NET value), serving both the trade and
buyback tabs, so the two panels sit side by side with just a bit of
padding between (grid.centerW 208 -> 16). The row description keeps the
smaller font, word-wraps inside the text column (wrap width stops
before the price), and the char-count budget now spans two lines so the
headless path stays bounded; rowH grows 64 -> 72 to fit both lines.
The buttons persist across tab switches (they were content before and
died with it), Confirm stays grayed while the trade is invalid, and the
stub gains setX/setY so the bar layout is testable.
description + price + stepper) instead of the 4×4 icon grid
Each stock good is now ONE WIDE ROW — the good's icon, its name + a
one-line description (the good's flavor), and the price + a −/qty/+
stepper on the right — the way the user wanted the shop list to read.
The stock (12–16 goods) outgrows the panel, so the list SCROLLS:
drag on the row area or the mouse wheel; rows outside the window are
clipped (row-based alpha clipping — this Phaser build has no
ScrollContainer/camera clip), and hidden rows' steppers are
non-interactive. The selection is data (goodId→qty), so stepping a
good works whether its row is on screen or not.
js/ui/shop/BuyList.js new — the scrollable BUY list (rows, drag +
wheel scroll, per-row steppers, flavor text)
js/ui/shop/TradeGrid.js SELL-only now — the BUY mode (steppers, stock
rendering, buy-selection data) is stripped;
one grid, one purpose
js/ui/shop/ShopView.js composes TradeGrid (SELL) + BuyList (BUY);
the BUY panel sizes itself to the SELL grid's
height (top- AND bottom-aligned)
data/shopui.json buyList row/typography knobs (name, desc,
price, row bg/border, selection accent)
dev/shop-ui.test.mjs BUY assertions rewritten for the list + a new
SCROLL section (scroll clamps, row clipping,
stepping off-screen rows)
dev/shop-shot.mjs BUY probe → the row's inline text (no tooltip)
docs/ECONOMY.md §12 the BUY bullet + mockup now describe the list
The BUY side's name + price used to live in a hover tooltip because a
grid cell is too small for text; a wide row carries them inline, so the
tooltip stays on the SELL side only. All headless suites green
(shop-ui, economy, cargo, builds, ship-behavior).
The pending trade's credit delta now sits at the right end of the
Buy/Sell + Buyback sub-bar, past a hairline divider — tabs AND the
credit math on one console, one glance apart (entry state dims it to
0 cr). The center column keeps Confirm/Clear (re-centered); the
buyback panel's Confirm moves up to sit just below its rows.
Knobs: data/shopui.json → preview (bar sizing, zeroColor, divider).
Docs: docs/ECONOMY.md §12 mockups + bullets follow the new layout.
Tests: dev/shop-ui.test.mjs asserts the bar's NET readout mirrors the
pending trade (+N cr while picking, 0 cr after Confirm).
- TradeGrid cells now render only the good's icon; hovering a usable cell reveals a shared per-grid tooltip with the name and "sell/buy for N cr" price line, anchored above (or below on the top row) and clamped to the panel.
- Per-cell name/price text objects are removed; a single `label` remains only for locked/empty state ("locked", "—").
- data/shopui.json: add `tooltip` section (bg, border, pad, gap, font sizes) and bump `iconScale` to 0.62 so icons fill the now-cleaner cell.
- Apply theme fonts throughout the shop UI — Ethnocentric for section titles, Centauri for body text, tooltip, stepper, and buttons via `fontStack()`.
- dev/shop-shot.mjs: replace old layout probe with a hover-tooltip probe (structure check, programmatic + real mousemove hovers on sell/buy cells, pointerout hide) and await web fonts before boot so screenshots use the real typefaces.
- dev/shop-ui.test.mjs: new HOVER TOOLTIP suite asserting tooltip visibility, text content, locked/empty suppression, unhover, buy-side pricing, and theme font application.
- Shop now opens on the tab sub-bar only (shop video plays); clicking Buy/Sell reveals the trade panel — showcases the clip before the UI takes over.
- Tab bar and Confirm/Clear are now MenuButtons in a console-blue sub-bar that rests above the command deck, anchored to the deck's Shop button (MenuSubBar recipe), replacing the old top-of-screen custom buttons.
- Fix trade panel vertical placement: a Container's (x,y) is a pure translation of local space (verified via dev/cbox-test.html), so the grid position IS the panel top-left; setting it to topY + h/2 treated it as a centered origin and clipped bottom rows. Panel now anchors just under the HUD planet text via SurfaceScene.shopTopY().
- Parent TradeGrid plate/title to the grid container so they render above the shop video (scene-level depth-0 objects drew behind it in this build).
- Add dev probes (dev/cbox-test.html, dev/shop-shot.{html,mjs}) and update phaser-stub/test stubs for setSize/setBlendMode/rectangle; document entry state + sub-bar in docs/ECONOMY.md.
- Introduce CargoHold (pure class) as the ship's trade-goods carrier: fixed bays, one good each, capacity = unlocked bays, save/restore round-trip. Ship.cargo + setCargoHold/cargoRoom wired into stats.
- Add three credit-costed cargo builds (cargo-bay-8/12/16) that expand the hold 4→8→12→16 (MAX wins). BuildWindow affordability now covers both minerals and credits; GameScene.beginBuild debits credit lines and _applyCargoUpgrade expands the hold on completion.
- Add tradePreview/resultingCargo to Economy.js as the shop's pure calculator: sell/buy pricing, net credits, the two caps (can't go negative, end hold fits capacity), and atomic sell-to-free-room-for-buy.
- New ShopView + TradeGrid compose the buy/sell screen over the shop clip: SELL grid of cargo bays (tap to check), BUY grid with quantity steppers, live NET preview, Confirm/Clear, and a session-local Buyback tab (neutral undo at the sale price, locked until a sale). Wired into SurfaceScene's SHOP button.
- data/shopui.json drives the shop layout/animation knobs; economy.json gains cargo.maxSlots (16-slot grid); ship.json base cargoHold drops to 4 unlocked bays.
- Headless tests: dev/cargo.test.mjs (hold rules + trade preview caps), dev/shop-ui.test.mjs (construct, sell, buy, caps, buyback, clear), plus updated builds/ship-behavior suites; phaser-stub gains Geom.Rectangle for the hitArea path.