- 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
- 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
- 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.