or scroll · next
Product design · feature proposal

Trade, made real.

Dealers don't just sell cards — they trade them, usually with cash making up the difference. Today Cardboard has a "Trade" button that quietly does nothing but relabel a cash sale. This deck proposes the math that makes it honest, three ways to put it on screen, and how it shows up at the point of sale.

0
Lines of trade logic today
1
New rule to get right
3
Layout options to choose from
$0
New infra — rides the current stack

Cardboard · thecardboard.app · concept only — nothing here is wired to real code yet. Reworks MarkSoldScreen, PosSaleScreen, and POS Mode.

The gap

"Trade" is a cash sale wearing a costume

It's a real chip in the sell flow today — dealers tap it expecting a trade. Here's everything that sits behind it:

Live code · fees.js
{ id:'trade', label:'Trade',
feePct:0, feeFlat:0 }

That's the whole feature. It books the outgoing card as sold for cash at 0% fees — identical to the Cash chip, just with a different word on it.

What a trade actually needs — and none of it exists

  • No value on the other side. Their card is worth something; the app never asks what.
  • No incoming card. You walk away owning a new card that never enters your inventory.
  • No cash math. The "+ $100 to even it out" — the entire point of most trades — has nowhere to go.
The stakes: a trade mis-recorded as a cash sale corrupts two numbers at once — your realized profit and the cost basis of a card you now own and may resell. Both wrong, quietly.
The money model · the one rule

A trade is a sale + an acquisition, bridged by cash

No new financial primitive. The card you give leaves as a sale (existing math, unchanged). The card you get enters as an acquisition (existing cost field). The one rule that keeps it honest:

The incoming card's cost = what you truly gave up — your card's value, plus cash you paid, minus cash you took. Never the incoming card's own appraisal. Fair trades make the two agree; lopsided ones shouldn't.
Example 1 Your $100 card for their $200 card
Give Ja Morant base$100
Get Wembanyama auto$200
Cash you pay them$100
Their card books at$100 + $100 = $200
Example 2 Your $200 card for their $100 card + cash
Give Wembanyama auto$200
Get Ja Morant base$100
Cash they pay you$100
Their card books at$200 − $100 = $100
Multi-card needs no new math. The POS already splits a haggled bundle across lines to the cent (allocateBundle). Your outgoing lines split the sale total by ask-weight; their incoming lines split the total cost by appraised weight. Same function, both directions.
The proof · why cost ≠ appraisal

The rule earns its keep on a bad trade

In Examples 1 & 2 the trades were fair, so cost and appraisal happened to match. Watch what the rule does when a trade isn't fair — a dealer overpays because they just want the card.

Lopsided trade You give a $100 card for their $80 card, no cash
Give your card's value$100
Get their card appraises at$80
Books at cost$100  (not $80)

Why book the higher number?

Because $100 is what you actually surrendered. If the app booked it at its $80 appraisal, it would silently pretend the trade was even.

  • Resell it later at its real $80 and the app honestly shows a −$20 — the true cost of wanting it.
  • Book it at $80 and that loss vanishes. The ledger would lie to protect your ego.
Same instinct already in the codebase: fees.js and consign.js both refuse to fudge — store real inputs, derive honest money. Trade extends that line, it doesn't cross it.
Part 1 · the one-card trade

Three ways to put it
on the phone.

This is the everyday case — one card out, one card in, in the regular app's sell flow (MarkSoldScreen). Three honest options, escalating in ambition. Same math underneath all three; what changes is how it reads.

A · Inline expand B · Two-sided Trade mode C · The balance beam
Option A

Inline expand — the trade grows in place

Mark sold
2021 Prizm Ja Morant Silver
Cost $40.00
CashVenmoCardTrade
GettingNew
2023 Bowman Wembanyama Auto
Scanned just now
Comp $200 · tap to edit
$200
Add manually
You pay$100
Their $200 − your $100 = $100 cash
Editable — a suggestion, not a lock
Profit on your card+$60
Their card's cost$200
Log tradeSells yours · adds theirs

The Trade chip you already have simply expands the same screen. The big "Sold for" number gives way to a "Getting" card, then the cash bridge. One column, one scroll, nothing new to navigate.

  • Smallest build — reuses MarkSoldScreen almost wholesale.
  • Zero new navigation; the chip you tap is the one that's already there.
  • Familiar: it's the sell screen the dealer already knows, plus a section.
  • Gets tall and busy — two economic directions stacked in one column.
  • The "sale" framing lingers; a trade still visually reads like a sale-with-extras.
  • Multi-card (their side has 2+) strains the single-column layout.
Best forshipping the feature fastest
Option B Recommended

Two-sided Trade mode — "You give / You get"

New trade
You give
2021 Prizm Ja Morant Silver
Cost $40 · from your inventory
$100
You getNew
2023 Bowman Wembanyama Auto
Comp $200 · tap to edit
$200
Scan their card
You pay$100
Evens out a $200 ↔ $100 trade
Profit on your card+$60
Log trade1 out · 1 in · $100 cash

Trade gets its own focused screen with two labelled panels — You give and You get — and the cash bar between them. It stops pretending to be a sale and reads as what it is: an exchange.

  • Clearest mental model — the two sides are explicit, not implied.
  • Scales cleanly to multi-card; each panel is just a list.
  • Same shape as the POS trade (Part 2) — one model, two surfaces.
  • A genuinely new screen + a nav entry to build and wire.
  • Slightly more taps than expanding in place.
Why this one: it's the only option that stays identical from the phone to the show table. Build the two-sided model once, reuse it in the cart and the register. Graft in C's fairness read as a small chip and you get the best of both.
Option C

The balance beam — see fairness at a glance

New trade
Your side
$100
Their side
$200
Add $100 cash to your side to level it
Ja Morant Silver
Yours
$100
Cash
Your side
$100
Wembanyama Auto
Theirs
$200
Even trade
Log tradeBalanced · $100 cash in

A literal scale. Two pans — your value vs theirs — and the cash is the weight that levels them. The pivot slides off-center until the sides balance, so fairness is a picture before it's a number.

  • Instant "is this fair?" read — the whole point of haggling, visualized.
  • Memorable and differentiated; nothing else in the app looks like it.
  • Cash-on-either-side is natural — it's just weight on a pan.
  • Most design + build effort of the three.
  • Metaphor can fight precision on a small screen — the beam is charming until you need exact cents.
  • Novel interaction = more to test and get right.
Best fora signature moment, if we have the runway
Part 1 · the call

Ship B, borrow from C

All three run the exact same money model, so this is purely a UX bet — not a technical one.

DimensionA · InlineB · Trade modeC · Balance beam
Build effortLowestMediumHighest
Clarity of the modelReads as a sale+Explicit two sidesVisual, instant
Multi-card readyStrainedNativeAwkward past 2–3
Matches the POS tradeNoIdentical modelDiverges
Precision at the centExactExactMetaphor blurs it
DistinctivenessLowMediumHigh
Recommendation: build Option B. It's the only layout that stays byte-identical from the phone to the register, so we design the two-sided model once and reuse it everywhere. Then graft C's fairness read — the little "even trade / off by $20" balance chip — on top of B. You get the clear model and the signature glance, without C's full build cost.
Part 2 · trade at the point of sale

Where's the POS
trade? Right here.

Trades happen at the show table more than anywhere — someone hands you a card mid-deal. So the two-sided model from Option B carries straight into both POS surfaces: the phone cart in a live show, and the iPad register in POS Mode.

Phone cart · PosSaleScreen iPad register · POS Mode
POS · phone cart (live show)

Your side / Their side, N-for-M

New sale
CashVenmoCardTrade
Your side2 cards
Ja Morant RC
Chronicles · #201
$30
Luka Dončić Silver
Prizm · #79
$70
Their sideNew
Wembanyama Silver #136
Comp $200 · tap to edit
$200
Your side
$100
Their side
$200
Gap$100 to them
$200 − $100 = $100 cash
Log trade$100 cash · 2 out · 1 in

The cart already handles many cards and a haggled bundle total. Trade adds a second list — Their side — with its own scan button, and the gap between the two side-totals becomes the cash line, settled exactly like a bundle total today.

  • Your side reuses the existing cart line-for-line — same prices, same bundle-haggle.
  • Their side scans cards in (creates new inventory), instead of matching stock you already own.
  • The cash gap auto-computes and stays editable — a lopsided trade is one tap away.
  • "Log sale" becomes "Log trade": your side sells, their side enters stock, one atomic write.
Reused wholesale: allocateBundle for both sides, saleProfit for your side, the cost field for theirs. The cart barely changes shape.
POS Mode · the iPad register

A trade in the Square-style register

In POS Mode the sale is a two-column register. A trade keeps that skeleton — the for-sale grid on the left, the cart on the right — but the cart splits into Your side / Their side and swaps the Charge button for the cash gap.

Active show · Summer Slam — $1,240 · 18 sold D
2023 Prizm Wembanyama Silver
$180
2018 Ohtani Update RC
$95
1986 Fleer Jordan RC
$70
1996 Topps Chrome Kobe RC
$410
2020 Prizm Silver Luka RC
$150
2017 Prizm Mahomes RC
$325
Reads as a register, behaves as a trade. Buyer gives $90 in cards toward a $150 pair, covers the $60 gap in cash — the two Kobe/Mahomes cards sell, the Zion enters inventory at a $90 cost, all in one tap.
Engineering · what it actually takes

Mostly reused parts, one honest new piece

Already exists — reused as-is

  • The sale half: saleProfit / saleFees value the card you give — no change.
  • The acquisition half: incoming cards use the cost field every card already has.
  • Multi-card split: allocateBundle pro-rates each side to the cent, both directions.
  • Scan-in: the identify → Card Hedge comp pipeline every card-add already runs — no new API load.

Genuinely new

  • The cash-delta reconciler — the small function that turns two side-totals into a signed cash figure and the incoming cost basis.
  • The two-sided UI (Option B), reused across phone + cart + register.
  • One additive column: trade_id (uuid, null-default) stamping both sides of one trade — same pattern as sale_txn_id, so a future "trade history" reads as one event.

What it does not need

  • No new pricing source — comps come from Card Hedge, already wired.
  • No change to portfolioStats — cards stay the source of truth.
  • No new sync engine — trade_id rides the existing card sync.
  • No new npm dependency.
1
New schema column
2
Screens reworked
~1
New lib module (trade math)
Rough size: comparable to the original Card Show POS build — real work, but almost entirely composition of parts that already exist and are already tested.
The takeaway

Make the Trade button
tell the truth.

One honest rule

Incoming cost = what you gave up. It keeps profit and cost basis correct even when a trade is lopsided — the same no-fudge instinct already in the codebase.

One model, three surfaces

Option B's two-sided layout is identical on the phone, the live-show cart, and the iPad register. Design it once, reuse it everywhere.

Cheap to reach

One additive column, one small math module, two screen reworks. No new pricing, no new sync, no new dependency.

Proposed next move: approve Option B, build it first in the regular app's MarkSoldScreen (lowest-risk place to prove the math + the UX), then lift the same two-sided component into the POS cart and the register. React on this board first — copy, colors, which layout — before any of it becomes real code.

Cardboard · Trade feature proposal · concept board, not shipped code · thecardboard.app