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.
Cardboard · thecardboard.app · concept only — nothing here is wired to real code yet. Reworks MarkSoldScreen, PosSaleScreen, and POS Mode.
It's a real chip in the sell flow today — dealers tap it expecting a trade. Here's everything that sits behind it:
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.
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:
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.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.
Because $100 is what you actually surrendered. If the app booked it at its $80 appraisal, it would silently pretend the trade was even.
fees.js and consign.js both refuse to fudge — store real inputs, derive honest money. Trade extends that line, it doesn't cross it.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.
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.
MarkSoldScreen almost wholesale.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.
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.
All three run the exact same money model, so this is purely a UX bet — not a technical one.
| Dimension | A · Inline | B · Trade mode | C · Balance beam |
|---|---|---|---|
| Build effort | Lowest | Medium | Highest |
| Clarity of the model | Reads as a sale+ | Explicit two sides | Visual, instant |
| Multi-card ready | Strained | Native | Awkward past 2–3 |
| Matches the POS trade | No | Identical model | Diverges |
| Precision at the cent | Exact | Exact | Metaphor blurs it |
| Distinctiveness | Low | Medium | High |
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.
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.
allocateBundle for both sides, saleProfit for your side, the cost field for theirs. The cart barely changes shape.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.
saleProfit / saleFees value the card you give — no change.cost field every card already has.allocateBundle pro-rates each side to the cent, both directions.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.portfolioStats — cards stay the source of truth.trade_id rides the existing card sync.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.
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.
One additive column, one small math module, two screen reworks. No new pricing, no new sync, no new dependency.
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