--- name: scanner-mobile-checkout classification: feature success_metric: | On a phone, opening /scanner goes straight into a full-bleed camera; each successful identify lands in a local cart (not the database); the user can open a checkout sheet, select cards, and commit them to My Collection or a List without leaving the session. skip: [] status: shipped created: 2026-08-14 depends_on: - scanner-rebuild - redesign-scanner-flow model_policy: default_session: auto roles: role-conductor: composer-2.5-fast role-architect: composer-2.5 role-ia-architect: composer-2.5-fast role-ux-reviewer: composer-2.5-fast role-ui-designer: composer-2.5-fast role-implementer: composer-2.5-fast role-reviewer: cursor-grok-4.5-high role-security-auditor: gpt-5.6-terra-medium role-design-system-auditor: cursor-grok-4.5-high role-a11y-auditor: cursor-grok-4.5-high role-doc-writer: auto escalate_to: claude-sonnet-5-thinking-medium escalate_to_premium: claude-4.6-opus-high-thinking never_premium: - role-reviewer - role-security-auditor - role-design-system-auditor - role-a11y-auditor - role-ui-designer - role-doc-writer design_direction: source: role-ui-designer skill: ui-ux-pro-max skill_version: "2.5.0" version: 1 locked_at: 2026-08-14 product_type: mobile camera scanner checkout cart trading cards glassmorphism pattern: Immersive camera overlay + bottom-sheet checkout style: Liquid Glass (repo canonical) stack: nextjs layout_reference: image-93781104-69e3-4f0b-b25f-df7c750ef046.png --- # Convoy: scanner-mobile-checkout Turn the scanner into a mobile checkout: scan everything first, then commit the cart. Layout reference is the last attached mock (full-bleed camera, glass overlays, review as a bottom sheet). ## Why The overnight `scanner-rebuild` got us a three-phase machine (Setup → Scanning → Review), but the phone experience is still chrome-heavy: Layout's sidebar, top search bar, and bottom nav compete with the viewfinder; a destination form blocks the camera; and `handleCardScanned` writes each match to the database immediately (rebuild D3). That fights the actual table-scan job — hold the phone, sweep cards, then check out once. Users want supermarket-checkout semantics: open scanner → scan all the things → open the cart → add selected cards to My Collection or a List → leave. No per-card "Add" tap. No setup screen in the way. ## Scope ### In scope - **Immersive scan chrome (mobile).** Hide Layout sidebar, TopSearchBar, and MobileNavigation while the camera is live. Camera is full-bleed. Overlay only: back, title, optional gallery, detection brackets + status around the tracked card, bottom glass bar (flash, scan status, Review N). - **Cart, not auto-route.** Successful identifies enqueue locally. Nothing POSTs to `user_cards` / collections / decks until checkout. Reverse rebuild D3 for this flow. - **Scan peek.** After a match, a compact non-interactive preview slides up briefly (name + thumbnail + confidence) and auto-dismisses. Tapping it opens the cart. No condition / foil / Add buttons on the peek. - **Checkout sheet.** Bottom sheet over the still-live (but paused) camera. List of cart items with selection, qty, confidence, overflow menu (remove / condition / foil). Primary: add selected to My Collection. Secondary: add selected to a List (picker). Copy from `lib/collection-vocabulary.js` — never "binder" as a button label (mock says "Save to binder"; product vocab is List). - **Camera swap.** Front / rear toggle in the bottom bar. Flash stays rear-only and hides when `torch` is unsupported (iOS Safari). - **Desktop.** Same cart semantics. At `md+`, cart can be a persistent side panel instead of a sheet; do not hide the desktop sidebar. ### Out of scope - Identify / OCR / Gemini pipeline (`lib/scanner-card-identify.js`, `pages/api/scan/identify.js`, OpenCV detection). Layer UI on the existing hooks. - Schema / new tables. Cart is client-side session state. - Deck Mode, game pre-filter, scan-history list on the setup screen (rebuild Setup). Revisit as a later convoy if needed. - Estimated total value row from the mock, unless identify payloads already include `market_price` with no extra fetch. - Deleting leftover `components/CameraScanner.js` / `ScannerPageView.js` (pre-rebuild). Separate cleanup. - Changing desktop visual-diff homepage baseline except as a side-effect of Layout's new immersive prop. ## Roles invoked 1. `role-ia-architect` — scan → peek → cart → commit flow; where destination choice lives now that Setup is gone. 2. `role-ui-designer` — lock the mobile overlay + checkout sheet against the last mock, using Liquid Glass tokens (ui-ux-pro-max is installed). 3. `role-ux-reviewer` — cart selection, low-confidence handling, back with unsaved cart, pause-vs-kill camera under the sheet. 4. `role-architect` — briefs. Likely: (1) Layout immersive + camera chrome, (2) stop auto-route + cart model, (3) checkout sheet + destinations. `slice_dependencies` must mark what can run in parallel. 5. `role-implementer` — per brief. 6. Audit fan-out: reviewer + security-auditor + design-system-auditor + a11y-auditor. ## Todos - [x] IA: route/flow/screen inventory for immersive scan + checkout sheet - [x] UI Designer: lock overlay recipe (tokens, not hex) from last mock - [x] UX: cart selection, low-confidence, back-guard, camera pause - [x] Architect: briefs + whether Layout gets `chrome="immersive"` - [ ] Stop auto-route in `use-scanner-queue.js`; scans stay `processed: false` until checkout - [ ] Full-bleed camera; hide app chrome on small viewports - [ ] Bottom bar: flash, status, Review N; add facingMode swap - [ ] Non-interactive scan peek; checkout sheet with bulk + per-card commit - [ ] Pause identification while the sheet is open (`verificationPausedRef`) - [ ] Tests for cart enqueue (no network) and checkout commit paths - [ ] Visual-diff: scanner surfaces + Layout immersive; refresh baselines if chrome changes ## Predecessor `scanner-rebuild` (2026-06-13, overnight) shipped the phase machine, flash hook, toasts, count pill, and review list. This convoy keeps those hooks and **replaces the UX contract**: skip Setup, immersive camera, cart-then-commit instead of auto-route. ## Conductor notes (build shape) Likely file ownership for Architect to refine: | Area | Files | | --- | --- | | Immersive shell | `pages/scanner.js`, `components/Layout.js`, `components/MobileNavigation.js` | | Camera chrome | `components/scanner/ScannerCamera.js`, `lib/use-camera-scanner.js`, `lib/use-scanner-flash.js` | | Cart model | `lib/use-scanner-queue.js`, `lib/scanner-session.js` | | Peek + sheet | new `ScannerScanPeek.js`, rewrite `ScannerReview.js` as a sheet; reuse `ScannerDisambiguation.js` | | Copy | `lib/collection-vocabulary.js` (`ADD_TO_MY_COLLECTION`, `ADD_TO_LIST`) | Do not rewrite identification. `handleCardScanned` should enqueue only. ## Multitask dispatch Planning is serial: IA → UI Designer → UX → Architect. After architect: implementer fan-out only if briefs have `depends_on: []` and disjoint `files:`. Cart-model (stop auto-route) likely blocks the checkout sheet; immersive Layout may be parallel with cart-model if they do not both own `pages/scanner.js`. After PR draft: `/multitask` audit fan-out `role-reviewer + role-security-auditor + role-design-system-auditor + role-a11y-auditor` (group id: `audit-scanner-mobile-checkout-`). ## IA ### Affected routes - `[modified]` `/scanner` — sole scanner surface. Entry skips Setup and lands on full-bleed camera; cart and checkout live as in-page overlays/sheets on this route (no new URLs). Phase machine collapses from setup → scanning → review into scanning-first with optional cart sheet open/closed. - `[impacted]` `/login` — unauthenticated `/scanner` visits still redirect here (unchanged gate in `pages/scanner.js`). Post-login return target is TBD (see open questions). - `[impacted]` `/dashboard`, Layout sidebar, command palette — entry links to `/scanner` unchanged; users now arrive directly in camera instead of Setup. - `[impacted]` `/my-cards`, `/collections` — not part of the scan flow, but natural post-checkout destinations if the user navigates away after commit. No route or nav changes required. No `[new]` routes. Destination choice (My Collection vs List) moves from pre-scan Setup into the checkout sheet on `/scanner` only — **no route changes**. ### User flow ```mermaid flowchart LR A["Open /scanner"] --> B["Live camera"] B --> C["Card identified"] C --> D["Scan peek"] D --> B B --> E["Review N"] E --> F["Checkout sheet"] F --> G["Add to My Collection / List"] G --> H["Exit scanner"] ``` ### Screen inventory | Screen | Path | New/modified | Notes | | --- | --- | --- | --- | | Scanner — live camera | `/scanner` | modified | Default view on open (mobile: immersive, no Layout chrome). Bottom glass bar: flash, status, camera swap, Review N. Detection brackets + status around tracked card. | | Scan peek | `/scanner` (overlay) | new | Brief non-interactive slide-up after identify (name, thumbnail, confidence). Auto-dismiss; tap opens checkout sheet. No per-card Add / condition / foil on peek. | | Checkout cart sheet | `/scanner` (overlay) | modified | Replaces full-page `ScannerReview` as primary cart UI on mobile. Camera stays mounted but paused under sheet. Selection, qty, confidence, overflow (remove / condition / foil). Primary: Add to My Collection; secondary: Add to List (picker). | | Disambiguation sheet | `/scanner` (overlay) | impacted | Existing `ScannerDisambiguation` stays in overlay stack above camera; blocks identify until resolved, then card enqueues to cart. | | Scanner Setup | `/scanner` (phase) | impacted | Removed from default entry path (out of scope to delete component). Game filter, deck mode, pre-scan destination, and scan history no longer gate camera start. | | Auth loading | `/scanner` | impacted | Spinner while `useAuth` resolves; immersive chrome applies once authenticated (see open question on loading shell). | Desktop (`md+`): same `/scanner` route; cart may render as persistent side panel instead of sheet; Layout sidebar remains visible. ### Content / data model deltas - **Copy (write / wire):** checkout CTAs from `lib/collection-vocabulary.js` — `ADD_TO_MY_COLLECTION`, `ADD_TO_LIST` / `ADD_TO_LISTS`; bottom-bar "Review N" (N = cart count); flash and camera-swap `aria-label`s; empty-cart and commit-success toasts. Never use "binder" or "Save to binder" as a button label. - **Copy (retire from entry path):** Setup destination picker, game pre-filter, deck-mode selector, and scan-history list as pre-scan gate copy (component may remain for later convoy). - **Client session state:** cart is in-memory queue on `/scanner` — cards stay `processed: false` until checkout commit. Reverses rebuild D3 auto-route; no persistence across refresh or navigation away. - **API calls (commit-time only, unchanged endpoints):** `POST /api/user-cards` (My Collection), `POST /api/collections/:id/cards` (List), existing `GET /api/collections` + `GET /api/decks` for destination lists. Optional `GET` batch-ownership during cart review. No schema / table changes. - **Immersive Layout:** new chrome mode (likely `chrome="immersive"` on `Layout`) affects how `/scanner` composes global nav — content delta only on this page, not a new route. ### Open IA questions 1. **Post-checkout exit:** Product intent says "then exit" — does the user return to the prior page (`router.back()`), land on `/my-cards` or `/collections`, or stay on `/scanner` with an empty cart and live camera? 2. **Post-login deep link:** If an unauthenticated user hits `/scanner`, should login return them to `/scanner` (camera) or a safer default (`/dashboard`)? 3. **Desktop Setup:** Is Setup skipped on `md+` as well, or only on mobile viewports? 4. **Deck destination:** Deck mode and add-to-deck are out of scope — confirm deck is fully absent from checkout destinations (not just hidden behind Setup). 5. **Gallery import:** Convoy mentions "optional gallery" in the top overlay — is this in-scope as a file-picker overlay on `/scanner`, or deferred? 6. **Partial commit:** After adding a subset to My Collection / a List, does the user remain in the sheet with remaining items, or does any successful commit close the session? ## Decisions (post-IA) Locked 2026-08-14 from product intent (parent session). UX / UI / Architect treat these as settled. | # | Decision | | --- | --- | | D1 | **Stay on camera after commit.** Successful checkout removes committed rows from the cart. If the cart is empty, close the sheet and resume scanning. Back (header) is the only exit from `/scanner`. | | D2 | **Login returns to `/scanner`.** Keep the existing auth gate; after login, send the user back to the camera, not `/dashboard`. | | D3 | **Skip Setup on all viewports.** Desktop uses the same scanning-first entry; cart is a side panel at `md+`, not a return of the Setup form. | | D4 | **No deck destination in checkout.** My Collection and List only. Deck Mode stays out of this convoy. | | D5 | **Gallery is in-scope.** Top-right control opens a file picker; chosen image goes through the existing identify path and lands in the cart like a live scan. | | D6 | **Partial commit stays in the sheet.** Uncommitted rows remain. Sheet closes only when the cart is empty or the user dismisses it. | | D7 | **Persist cart in `sessionStorage`.** Survive refresh within the tab; do not persist across browser sessions. | ## Design direction Locked v1 — 2026-08-14. Layout authority: last mock (`image-93781104-69e3-4f0b-b25f-df7c750ef046.png`). Generator query: *"mobile camera scanner checkout cart trading cards glassmorphism"*. ### Summary Deck Hearth's mobile scanner should feel like a **supermarket checkout lane over a live viewfinder**: full-bleed camera, ember-bracketed detection, glass overlays that float above card art, a brief non-interactive peek after each match, and a bottom-sheet cart for bulk commit. Warm ember/flame accents cast light onto glass — never fill entire panels with brand orange. ### Pattern + style | Field | Value | | --- | --- | | Product type | Mobile TCG collection scanner with session cart + checkout | | App pattern | **Immersive camera overlay + bottom-sheet checkout** (generator's "Bento Grid Showcase" rejected — marketing grid, not camera UX) | | UI style | **Liquid Glass** — repo canonical (`docs/DESIGN_TOKENS.md`). Generator's "Exaggerated Minimalism" / oversized typography rejected. | | Stack notes | Next.js Pages router, React 18, Tailwind + CSS variables. Next.js stack query returned 0 rows — follow existing scanner components + `components/ui/` primitives. | | Desktop (`md+`) | Same cart semantics; cart as persistent side panel; Layout sidebar stays visible. Immersive chrome is mobile-only. | ### Screen 1 — Live camera (mobile default) Full-bleed `