These are mockups, not a working app. The data is a fixture and nothing is saved. They exist to settle design questions before anything gets built — so parts are deliberately unfinished, and this page tells you which.
catalog-product-create.html#state=dupe. The address bar
always names the state you are on.Every product version in one sheet — 30 columns, identity pinned while the rest scrolls, four saved views, and filters for brand and for what needs a decision. Click any row to open the product record.
Open Catalog Products → catalog-list-v2.html
The eleven formulas, one row per formula at one unit spec. Marine Collagen 500 appears twice, at 90 and 60 capsules, on purpose: weight and dimensions only exist once a count does. Six formulas need a decision.
Open Source Formulas → source-formulas-list.html
The product-type taxonomy — what a product is, as against the Benefits that say what it does. Create, rename, move and retire a node; a node in use, or one with children, cannot be deleted and the control says which before you reach for it. Lives in Settings, not on the main nav.
Also addressable: a node blocked from deletion twice over — in use and with children · five products on one leaf: blocked by use alone · the branch that is not a supplement, and is empty · the one node that deletes — unused and childless · a retired node, still carried by the product nobody re-filed
Open Categories → categories.html
One catalog product: identity, versions, images, benefit tags and Supplement Facts. Where a source formula owns the physical facts they are read-only with an SF-0007 ▸ link back; a product that arrives finished owns them, picks its own label panel, and has its purchase orders and COAs below.
Also addressable: ShipStation needs you: the run bar shows both systems and every control opens the drawer on that state (MR-385) · the Setup & gaps drawer open on the record; untick the Active marks and its rows follow (MR-386)
Open The product record → catalog-product-edit-v6.html
What the product still owes before it can be listed — and the two automatic jobs, QuickBooks and ShipStation, that create its records. Facts a person owes are a table; the two machines are cards, because each runs a lifecycle (checked · you decided · created · proved) that a table cell cannot hold. It opens as a drawer off the product record, and shows only what is outstanding — settled intake fields fold to one line.
Also addressable: the ShipStation component that did not read back: sent against stored, and Send the weight again · the QuickBooks decision in the drawer: three candidates, one refused, and the cost conflict behind FP-0231 · CP-0102 rewound to state 1 — the product record opens it complete (MR-389) · after the decision: linked to FP-0231, keeping our cost · settled after the decision: minted by a person who checked 3 candidates · left unlinked to check, with the note · the settled drawer, opened · the product record carrying the processing banner · the drawer with work outstanding · CP-0044 arrives finished, a supplement: Supplement Facts and its batch are its own rows (MR-353) · CP-0151 topical: a received batch with no COA is a reminder that blocks nothing · CP-0152 hard good: a Product details panel row, and its POs with QuickBooks columns only
Open Setup & gaps → catalog-product-setup.html
The record that owns every physical fact — count, weight, dimensions, bottle, cost, ingredients. Read-only everywhere else; edited through a drawer that opens over whatever screen you are on. Creating one mints its RW SKU in the background.
Also addressable: the record mid-mint, with the running banner · the drawer stopped on two RW candidates, waiting on a decision · the setup drawer with optional fields outstanding · the settled drawer, opened on the proof
Open Source formula → source-formula.html
Creation is a readiness checklist, not a wizard: three fields mint the product and Create is live from step 1. Sourcing comes first: a product that arrives finished never has a raw SKU or a formula, so its facts and label panel are asked here. Pressing Create product opens the new product with its Setup & gaps drawer, where the gaps and the QuickBooks item are settled.
Also addressable: Arrives finished · a supplement: Supplement Facts typed on the create form (MR-356) · Arrives finished · a topical: an ingredient list, research by Category · Arrives finished · a hard good: Product details, research by Guidance · Arrives finished · a real product from ingredientlist.com: 27 Supplement Facts rows, 6 marked Active (MR-368) · step 6 · Ready to create: what is done and what is not (MR-390) · after Create with gaps: the drawer opens on ✓ created, the form keeps a status bar (MR-386, MR-387) · after Create with nothing blocking: the drawer opens on ✓ created and walks to complete (MR-388) · after Create, ShipStation needing you: the form’s status bar, resolved in the drawer (MR-388)
Open Add a product to the catalog → catalog-product-create.html
Every image the system holds, across the three libraries that own them — browse them, tag them, and administer the two dropdown lists the tags come from. Four structured facets (product, sale, sale type, image type) plus a free keyword pool; the pickers on the product, formula and channel screens still open only the one library that belongs to them.
Also addressable: the administration tab on its own · the catalog-product bucket
Open Image library → image-library.html
Where a bundle meets a box: the package table with its tares, the USPS tier × zone rate matrix, Bundle Mapping (source formula — or the product, where it has none — × pack size) and Combo Mapping (one row per mixed-product box), and the ShipStation automations generated from all of it. A row is Tested, Estimated or Pending, and one switch decides whether an estimate is allowed to name a box to ShipStation. An Estimated row opens How this was estimated: every pack size’s calculation side by side, and a Tested row — or combo — opens the same view as the estimate against the warehouse’s choice.
Also addressable: a Tested row: the estimate against the warehouse, 4 of 6 matched
Open Shipment Packaging → packaging.html
The four Channel Accounts, one row each. Two status columns, not one: active is your switch, connection is a system fact — and the row that matters is the one that is both active and broken. Promoted out of SCREENS.md’s SKIP because it owns the margin floor, the channel fee and the ad rate, which nothing else could set.
Open Sales channel accounts → sales-channel-accounts-list.html
One account screen for every channel (MR-395). A shared top — identity, the connection card, margin floor, sales channel fee, Compliance and its listings — then the tabs its channel has: eBay’s listing defaults and ladder models; Shopify’s New job defaults, Money and Store behaviour. The connection runs a lifecycle — checked · authorised · scopes · proved — in five states, one set of nodes.
Also addressable: CA-01 Vital Depot (Shopify) — New job defaults · CA-01 — Money: the gateway pair and CheckoutChamp’s rates, with a worked order · CA-01 — Store behaviour: measured facts, not settings · CA-02 eBay — the ladder model library · arriving from the list on the broken account · the alarming state on its own
Open Sales channel account → sales-channel-account.html
Every listing on a channel, one tab per channel. The two grains differ and that is why they are two tabs: an eBay row is a listing, a Shopify row is a product that opens into its variants. Carries MR-027’s bookkeeping columns — qty · listed · by · sold · views — which were declared a year of decisions ago and never drawn. The Shopify tab carries two views — grouped by product, or flat by variant.
Also addressable: the ended predecessor, reached from its relist · a combo that was built and never listed — two of nine aspects filled, each blank with its reason · the storefront preview: a complete, buyable-looking page for a variant that is not in the cart · a Shopify product that is active, syncing and published nowhere
Open Sales channel listings → sales-channel-listings.html
Settings › Users & access — the part Cloudflare Zero Trust cannot do: a user row the app can write on a record, an API credential an agent can act through as a person, and an audit log with a definition and somewhere to read it. The API credential is a Cloudflare service token, minted here or in the dashboard and then assigned; the local row is authoritative, so disabling someone puts them out at once and the three Cloudflare calls that follow are a repair — which is why one of them failing reads not yet in sync (2 of 3) and not error.
Also addressable: Paul — the admin, two live tokens, nothing owing · one person’s history, the way a History link arrives · a record’s history — the &entity= form
Open Users & access → settings-users.html
Settings › Compliance rules — the house rules every listing on every sales channel follows, written out in one Markdown editor and saved. Every content generation and compliance check reads them together with the account’s own Compliance text; a channel can only add. No rule lists, no word patterns, no approval, no history.
Open Compliance rules → settings-compliance.html
Settings › Integrations — one grid of every connection to Shopify, eBay, QuickBooks, ShipStation and CheckoutChamp, one row each, worst first. Check cells are only ✓, ✗ or —; click a row for the reasons, what it is connected to, where its credentials live, Check now, Sign in again or Reconnect, and its last ten checks. A red banner while anything is failing.
Also addressable: CheckoutChamp’s drawer: the allowlist reason, and Sign in again that fails again · eBay — outlet’s drawer: Reconnect turns it working · an operator on the QuickBooks production row: Connect is for admins
Open Integrations → settings-integrations.html
One listing job end to end for an eBay account — products, packages, the SKU plan, brief, generated drafts, images, the ShipStation mirror, and a final review. Stage 3 is the package rule stack (was its own screen until MR-077): you type the one-bottle price and the ladder derives every bundle and combo from it.
Its sibling: the Shopify builder
Also addressable: the final review as a grid — every listing about to be published on one page · the job before its first generation — Generate drafts is the only control · the agent pushed some listings and not others · Template 1 failed twice; the job can still go to the agent
Open Listing builder — eBay → listing-builder.html
One listing job end to end for a Shopify store — a sibling of the eBay builder, not a switch inside it. Tabs 1–6 write nothing outside the app; tabs 7 and 8 are execution, either side of the one human gate, and they share a display whose columns are the artifacts that now exist rather than the process steps. The CheckoutChamp sync has no progress to show and says so.
Its sibling: the eBay builder
Also addressable: handed to the outside agent — the export copied, nothing pushed back yet · the agent pushed some products and not others, and says which · the agent rewrote and pushed back — one approved in its harness · generation failed on the tab 4 approval; the gate reports it rather than claiming to be clear · the agent refreshed after the build — every product marked content drifted, with one repair · four already published and holding superseded copy — the drift is read before the publish state · mid-run — four lanes building, Eroluve halted on a handle collision · the hard stop before the sync, with the unresolved lane blocking it · during the sync — elapsed time, no progress bar, the CRM session · a row expanded onto its eleven steps with real ids · every variant, one row each · the storefront preview — one chip per variant, and the one a shopper cannot buy · four published and one outstanding — the job stays open
Open Listing builder — Shopify → listing-builder-shopify.html
Generated from mockups/ui/ by scripts/build-mockup-site.sh on 2026-09-15.
Superseded screens and the original wireframe study are deliberately not published here.
Design decisions are logged in mockups/ui/DECISIONS.md.