281 lines
23 KiB
Markdown
281 lines
23 KiB
Markdown
# UI Phase 6 — Checkout & money
|
||
|
||
> **Mission:** money UI is where trust is won or lost — and today the checkout total is a `subtitle2` row
|
||
> buried inside a breakdown card, the payment "confirmation" has no reference code a user could quote in a
|
||
> dispute, the top-level «کیفپول» tab is permanently empty for every card payer, and BNPL plans hide the
|
||
> numbers people decide with. Restructure checkout around "who am I paying for / how much", turn the
|
||
> confirmation into a real screenshot-worthy receipt, fill the wallet into the customer money hub, and make
|
||
> the BNPL comparison honest — without touching one rial of the money pipeline itself.
|
||
>
|
||
> **Track:** frontend · **Depends on:** [Phases 0–2](ui-phase-2-shells-and-navigation.md) ·
|
||
> [Phase 5](ui-phase-5-booking-lifecycle.md) recommended · **Unlocks:** the money moment finally earns the trust the ledger deserves
|
||
> **Before you start, read [../../phases/_shared/agent-operating-rules.md](../../phases/_shared/agent-operating-rules.md) and invoke the `frontend-designer` skill — both are mandatory.**
|
||
|
||
## 1. Context — where this sits
|
||
|
||
Balinyaar's pitch is "pay on-platform because your money sits in escrow until the visit is confirmed." The
|
||
checkout → confirmation → wallet → invoice chain is where a family decides whether to believe that. The
|
||
plumbing is excellent; the presentation treats the payment moment like any other form. Diagnosed current
|
||
state (all verified in code):
|
||
|
||
1. **The total is visually a footnote.** C6's page h1 is a plain `variant="h6"`
|
||
(`…/bookings/checkout/page.tsx:159`); the total — the most important number on screen — is a `subtitle2`
|
||
row inside `PriceBreakdown` (`components/PriceBreakdown/PriceBreakdown.tsx:62-63`); the pay CTA is the
|
||
last element of a scroll column (`page.tsx:202-225`), not sticky, not paired with the amount.
|
||
2. **A live race between two payment paths.** During pay-initiate, `busy` (`page.tsx:148`) disables only the
|
||
card CTA (`:207`); the «پرداخت اقساطی» button (`:214-224`) stays tappable mid-redirect.
|
||
3. **The confirmation is not a receipt.** `checkout/confirmation/page.tsx:51-113` renders only total +
|
||
variant + nurse name — no کد پیگیری, date-time, method, or booking number — and the amount panel is
|
||
`{summary ? … : null}` with no loading/error fallback: a failed fetch silently erases the paid amount.
|
||
The wire can't serve a receipt yet (`PaymentOutcomeDto`, `services/payment/types.ts:104-110`).
|
||
4. **«کیفپول» is an empty promise.** `wallet/page.tsx` renders only `WalletInstallments` — provider-reported
|
||
BNPL plans. For card payers (the default; BNPL is mock-gated) the tab shows «طرح اقساط فعالی ندارید»
|
||
forever, wasting 1 of 5 bottom-nav slots — and it self-constrains to `maxWidth: 560`
|
||
(`WalletInstallments.tsx:24`) while checkout/confirmation/invoice run at the shell's 800.
|
||
5. **BNPL hides the deciding numbers.** `BnplPlanCard.tsx:77-98` renders the down payment as a percent +
|
||
`LinearProgress` bar — a static fact styled as a loading indicator — with no Toman figure and no total
|
||
repayment cost; `PlanStep.tsx`'s «مبلغ کل» header silently swaps on plan tap (`plans.find(…) ?? plans[0]`);
|
||
providers are picked from two-letter text glyphs; `EligibilityStep.tsx:163` runs a multi-second credit
|
||
check behind a bare disabled button; `bnpl/return/page.tsx:100-104`'s invalid-link CTA says
|
||
«پرداخت با کارت» but navigates to the bookings list.
|
||
6. **Spinner-stacked waits, forked terminal cards.** `checkout/return/page.tsx:139-147` shows title +
|
||
`CircularProgress` + a `pending` chip — three redundant signals at the flow's most anxious moment — and
|
||
its `StateCard` / C6's `MessageCard` are copy-pasted four times across the card & BNPL flows.
|
||
7. **Currency-unit ambiguity.** Breakdown rows and installment amounts render bare grouped numbers; only the
|
||
total carries «تومان» (`PriceBreakdown.tsx:53` vs `:63`, `InstallmentScheduleRow.tsx:57`) — the classic
|
||
Toman/Rial second-guess, on wire amounts that are Rials displayed as Toman.
|
||
|
||
**What already exists (do not rebuild):**
|
||
|
||
- The money pipeline: `utils/money.ts` (BigInt, `formatIrrToToman`, `parseIrr`), IRR digit-strings on the
|
||
wire, Toman display-only, and `PriceBreakdown`'s dev reconciliation guard (`PriceBreakdown.tsx:35-42`).
|
||
- The full C6 → return → confirmation flow and the 4-step BNPL wizard: idempotency-key-per-attempt,
|
||
benign-409 convergence, bounded outcome polling, every state (skeleton/error/empty/expired) covered.
|
||
- `EscrowNotice` (product-mandated verbatim copy), `CountdownTimer` (server-frozen, LTR clock),
|
||
`RefundStatusCard` + `RefundEtaBanner`, `StatusChip`, `TrustBadge`, `PaymentStatusBadge`; the invoice's
|
||
print mechanics — visibility-scoped print area, `insetInlineStart` anchoring, dark→light token flip so
|
||
dark-mode users print on paper colors (`invoice/page.tsx:98-113`).
|
||
- Phase 0's theme/`AppIcon` work, phase 1's primitives (`<Money>`, PageHeader, state views, StatusTimeline),
|
||
phase 2's shells. Consume them; extend a foundation file minimally (noted in your report) — never fork a copy.
|
||
|
||
## 2. Required reading (do this first)
|
||
|
||
- [audit/checkout-money.md](audit/checkout-money.md) — the 18-problem inventory with file/line evidence, the
|
||
8 opportunities this scope is drawn from, and the keep-list §5 repeats.
|
||
- [../../../.claude/skills/frontend-designer/SKILL.md](../../../.claude/skills/frontend-designer/SKILL.md) —
|
||
the design contract (two-layer tokens, `App*` wrappers, icon registry). Invoke it.
|
||
- Code (paths abbreviate `client/src/app/[locale]/(private-routes)/(customer)` as `…`):
|
||
`…/bookings/checkout/page.tsx` (+ `return/`, `confirmation/`), `…/bookings/checkout/bnpl/page.tsx` (+ the
|
||
four step files, `return/`, `gateway/`), `…/wallet/` (both files), `…/bookings/[id]/invoice/page.tsx`;
|
||
components `PriceBreakdown`, `BnplPlanCard`, `InstallmentScheduleRow`, `EscrowNotice`, `RefundStatusCard`;
|
||
services `payment` (types + hooks + both `PaymentApi` impls), `bnpl`, `refunds`; `components/config.ts`
|
||
(`CONTENT_MAX_WIDTH`) and `src/layout/config.ts` (bottom-bar dimensions, for the sticky bar).
|
||
- Product truth: [escrow-ledger.md](../../../product/payments/escrow-ledger.md) (what escrow actually
|
||
promises — the explainer must not overclaim),
|
||
[bnpl-landscape.md](../../../product/payments/bnpl-landscape.md) (provider-financed; the agreement is
|
||
customer ↔ provider), [iranian-payment-reality.md](../../../product/payments/iranian-payment-reality.md)
|
||
(gateway redirects, receipt culture, Toman/Rial). Plus `client/CLAUDE.md` "Golden rules" + Project Structure.
|
||
|
||
## 3. Scope — build this
|
||
|
||
### 3.1 C6 checkout: real hierarchy + sticky pay bar
|
||
|
||
- **Prominent total.** Lift the total out of the breakdown into an unmissable figure near the top (an
|
||
`h4`-weight `<Money>` with «تومان»); replace the `h6`-as-h1 with the phase-1 PageHeader treatment.
|
||
`PriceBreakdown` keeps its total row — the page-level figure is the same served `totalIrr`, never recomputed.
|
||
- **Sticky pay bar.** A page-level bar pinned above the customer bottom nav: total + pay CTA, always
|
||
co-located on mobile. Respect `env(safe-area-inset-bottom)` and the bottom-bar height from
|
||
`src/layout/config.ts` — do **not** edit `layout/` (phase 2 owns it); this is page composition.
|
||
- **Identity moment.** Give `EngagementSummary` the nurse's avatar + verified `TrustBadge` so "who am I
|
||
paying for" is answered at the moment of payment. `CheckoutSummaryDto` serves neither field — file the REQ
|
||
(§4) and serve them from the payment mock meanwhile.
|
||
- **Kill the race:** `disabled={busy}` on the BNPL branch button too — both CTAs freeze during initiate.
|
||
- **Back affordance.** The payable state gets an explicit «بازگشت به درخواست» text link — a user under a
|
||
ticking countdown needs an in-page path back to the request, beyond phase 2's header chrome.
|
||
- **Trust line at the CTA:** lock icon + «پرداخت امن از طریق درگاه بانکی» caption under the pay bar so the
|
||
gateway redirect is expected, not alarming. Real gateway/Shaparak logos are (DEFERRED — until licensed
|
||
assets exist; never fake a bank's mark).
|
||
- **Un-bake the arrows.** `payment.cta_pay` embeds «←»/«→» in the translated string (fa/en.json:577) — move
|
||
the arrow to an `endIcon` slot (register a direction-aware chevron in `AppIcon/config.ts` if phase 0/1 didn't).
|
||
|
||
### 3.2 Confirmation as a receipt
|
||
|
||
- Rebuild `…/checkout/confirmation/page.tsx` as a screenshot-worthy receipt card (Iranian users screenshot
|
||
receipts — design for that): **کد پیگیری** in a copyable `dir="ltr"` row with a copy button, Shamsi payment
|
||
date-time, method («کارت بانکی» / «اقساطی — {provider}»), booking reference, the paid total as a `<Money>`
|
||
hero, and the escrow reassurance via the `EscrowNotice` component — never rewritten copy.
|
||
- **No silent vanish:** the summary fetch gets real loading (skeleton) and error (retry) states. Add a
|
||
compact "what happens next" 2-step strip (اطلاعرسانی به پرستار ← ویزیت و ثبت ورود) via the phase-1
|
||
StatusTimeline idiom, extending trust past the payment.
|
||
- The wire serves none of کد پیگیری/paidAt/method today — file the REQ (§4), render from the mock now, and
|
||
degrade gracefully on the real path (hide the row rather than show a fake code). Web-Share/save-image
|
||
actions are (DEFERRED — the copyable code is the contract).
|
||
|
||
### 3.3 Wallet as the money hub
|
||
|
||
- Restructure `…/wallet/` into segmented sections (MUI Tabs or a segmented control — one pattern, both
|
||
locales): **پرداختها** (payment history: every card/BNPL transaction with amount, Shamsi date,
|
||
`PaymentStatusBadge`, deep-link to the booking — needs the customer payment-transactions list REQ, §4;
|
||
build mock-tolerant behind `services/payment`), **اقساط** (the existing `WalletInstallments` content,
|
||
semantics unchanged — provider-reported, never a Balinyaar ledger), **استردادها** (reuse `RefundStatusCard`
|
||
entries; `services/refunds` has by-booking/status reads but no "all my refunds" list — REQ if confirmed
|
||
missing, §4), **رسیدها** (invoice deep-links derived client-side from succeeded payments — no endpoint).
|
||
Each section keeps four-state discipline with money-hub-specific empty copy («هنوز پرداختی نداشتهاید» —
|
||
not the installments placeholder).
|
||
- **Normalize money-surface width:** drop the wallet's local `maxWidth: 560`; all money surfaces (checkout,
|
||
confirmation, wallet, invoice) read at one width system — the shell's `CONTENT_MAX_WIDTH`. If a narrower
|
||
measure is deliberately wanted, make it a named constant in `components/config.ts` applied to **all four**.
|
||
|
||
### 3.4 BNPL honesty + polish
|
||
|
||
- **Plan cards decide with Toman, not percent.** Each `BnplPlanCard` shows: پیشپرداخت (امروز) in Toman,
|
||
قسط ماهانه, and **مجموع بازپرداخت** with the fee delta vs interest-free explicit («+۴۵۰٬۰۰۰ تومان کارمزد»
|
||
in terracotta / «بدون کارمزد» neutral). Replace the `LinearProgress` down-payment bar with a plain labelled
|
||
amount row — a static fact must not look like loading. All figures are **served** by the plan DTO; if one
|
||
isn't, REQ it — never compute percent × total client-side.
|
||
- **PlanStep's swapping total explained.** The «مبلغ کل» header names the plan it reflects
|
||
(«مبلغ کل با طرح {plan}») and shows the fee delta when it changes — no silently morphing number.
|
||
- **Provider logos strategy (decide and document):** build a small `BnplProviderLogo` registry component —
|
||
`providerCode` → bundled SVG when an asset exists, falling back to a *designed* neutral chip (full provider
|
||
name + tinted monogram, not the current two-letter glyph). Asset-tolerant: real logos drop in without
|
||
touching call-sites.
|
||
- **Eligibility gets feedback.** During the credit check the button swaps to «در حال استعلام اعتبار…» with a
|
||
spinner (mirror C6's `state_initiating` pattern); the prefilled mobile field becomes `readOnly`
|
||
presentation (normal contrast), not `disabled`.
|
||
- **Fix the mislabelled CTA** on `bnpl/return`'s invalid-link state: it navigates to the bookings list, so
|
||
label it «رزروهای من» (reuse C6's `bd_my_bookings` invalid-link pattern) — or route to an actual card
|
||
checkout if a `request_id` is recoverable. Label and destination must agree.
|
||
- **Guard the dev harness.** `…/checkout/bnpl/gateway/page.tsx` is a test harness reachable in production —
|
||
env-gate it with `notFound()` outside development, mirroring the card-gateway harness refinement phase 4
|
||
deleted. Also remove the dead `CHECKOUT_GATEWAY` constant in `src/constants/routes.ts:24` (verify unreferenced).
|
||
|
||
### 3.5 Invoice: fiscal grade
|
||
|
||
- Extend `…/bookings/[id]/invoice/page.tsx` toward a document a family can file: buyer name, service
|
||
description + visit date(s) (composable client-side from the booking detail read — a UI join, not money
|
||
math), booking + transaction references, payment method, and a seller fiscal-identity block (legal name,
|
||
economic code, address). `InvoiceDto` serves none of the fiscal fields — REQ (§4); render what the client
|
||
can compose now, reserve labelled slots for the rest.
|
||
- Add an A4 print stylesheet: `@page` margins, a document footer (invoice number + issue date + مودیان
|
||
reference when present), type sized for paper. **Keep** the existing print mechanics untouched.
|
||
- Delete the stale comment at `invoice/page.tsx:148-149` (it claims fa `common.brand` reads «بلینیار»; the
|
||
catalog actually has «بالین یار»). The brand-spelling unification itself is (DEFERRED →
|
||
[phase 12](ui-phase-12-copy-motion-and-polish.md), which owns the catalog sweep).
|
||
|
||
### 3.6 Designed wait states + one terminal-state component
|
||
|
||
- Replace `checkout/return`'s pending spinner+chip+title triple with a staged 2-node progress:
|
||
«بازگشت از درگاه ✓» → «در انتظار تایید بانک» (active, calm animated indicator) + expected-duration copy
|
||
(«معمولاً کمتر از یک دقیقه طول میکشد»). Keep the manual «بررسی دوباره» escape hatch and the bounded poll.
|
||
- Extract the four copy-pasted terminal cards (`MessageCard` in `checkout/page.tsx:255-286` +
|
||
`bnpl/page.tsx`, `StateCard` in `checkout/return/page.tsx:151+` + `bnpl/return/page.tsx`) into **one**
|
||
shared `src/components/PaymentStateCard/` (co-located test, barrel export) covering
|
||
icon/tone/title/body/actions — the card and BNPL flows can no longer drift.
|
||
|
||
### 3.7 Money-display sweep
|
||
|
||
- Adopt the phase-1 `<Money>` primitive on every money row in this area — `PriceBreakdown` rows **and**
|
||
total, confirmation, `MethodStep`, `PlanStep`, `EligibilityStep`, `WalletInstallments`, `BnplPlanCard`,
|
||
`InstallmentScheduleRow` — so **every** amount carries «تومان» and identical typography. Bare grouped
|
||
numbers on a Rial-wire/Toman-display product are the exact ambiguity users second-guess. If `<Money>` lacks
|
||
a needed variant (e.g. strike-through for fee comparison), extend the phase-1 component minimally + its
|
||
test, and note it in your report.
|
||
- An expandable «چطور کار میکند؟» escrow explainer **around** `EscrowNotice` — a 3-step visual
|
||
(پرداخت ← امانت نزد بالینیار ← آزادسازی پس از تایید پایان ویزیت) plus the cancellation/refund implication,
|
||
available from checkout and confirmation. The mandated `EscrowNotice` sentence is **untouchable** — the
|
||
explainer wraps it, never edits it. Ground the steps in
|
||
[escrow-ledger.md](../../../product/payments/escrow-ledger.md); never promise timing the ledger doesn't guarantee.
|
||
|
||
## 4. Mocks & seams in this phase
|
||
|
||
**No new mocks or seams.** Everything stays behind the existing `services/payment`, `services/bnpl`, and
|
||
`services/refunds` seams (`USE_PAYMENT_MOCK` / `USE_BNPL_MOCK` unchanged); the UI is mock-tolerant — real-path
|
||
gaps degrade gracefully, never faked data.
|
||
|
||
Backend gaps become REQ entries appended to
|
||
[../../shared-working-context/frontend/requests/for-backend.md](../../shared-working-context/frontend/requests/for-backend.md).
|
||
REQ-001…038 are taken and parallel UI phases are also filing — **check the tracker at execution time and take
|
||
the next free numbers.** File (verify each gap against the current types first):
|
||
|
||
1. **Checkout/receipt enrichment** — `nurseAvatarUrl` + `nurseVerified` on `CheckoutSummaryDto` (§3.1);
|
||
tracking/reference code, `paidAt`, and payment method on the payment outcome or a confirm-read for the
|
||
§3.2 receipt (`PaymentOutcomeDto` today: `types.ts:104-110`; extends REQ-016/017).
|
||
2. **Customer payment-transactions list** — card+BNPL transactions (amount, status, date, bookingId) for the
|
||
wallet «پرداختها» section (§3.3); receipts derive from it client-side.
|
||
3. **Customer refunds list** — an "all my refunds" read for «استردادها» (§3.3) if `services/refunds` truly
|
||
has only by-booking/status reads (extends REQ-021).
|
||
4. **Invoice fiscal fields** — buyer name, payment method + transaction reference, seller fiscal identity
|
||
(legal name, economic code, address) on `InvoiceDto` (§3.5; extends REQ-018).
|
||
|
||
## 5. Critical rules you must not get wrong
|
||
|
||
- **`EscrowNotice` copy is product-mandated — never edit it.** Wrap and explain around it; verbatim, one
|
||
shared component.
|
||
- **Money values are served IRR digit-strings, formatted via the BigInt util. The UI never computes a
|
||
figure** — no percent × total, no client-side VAT, no float anywhere on the money path. `PriceBreakdown`'s
|
||
dev reconciliation guard stays.
|
||
- **Idempotency-key-per-attempt and benign-409 convergence stay exactly as implemented** (`page.tsx:54-57`,
|
||
`:137-143`) — a 409 on initiate routes to the outcome read, never a toast.
|
||
- **`CountdownTimer` semantics:** server-frozen deadline, LTR-forced tabular clock — restyle, never recompute.
|
||
- **BNPL honesty architecture stays:** D5 installments are provider-reported (never a Balinyaar ledger),
|
||
ownership notes at point-of-choice and contract step, consent checkboxes gate credit check and contract,
|
||
every decline path offers the card fallback. `RefundEtaBanner`'s honest 7–10-day BNPL window is untouched.
|
||
- **Terracotta is the single money accent** — pay CTA, BNPL selection, outstanding balance, breakdown total.
|
||
Never as body-text color; teal/neutral carries everything else. **Invoice print mechanics + dark→light
|
||
print flip are do-not-regress** (`invoice/page.tsx:98-113,128-136`).
|
||
- Design-contract non-negotiables that bite here: every string in **both** `en.json`/`fa.json`; tokens, never
|
||
hexes; logical/RTL-safe props only (`dir="ltr"` islands for codes and clocks); both color schemes verified;
|
||
MUI v9 API only; co-located tests for shared components; `clientFetch`/cookie rules untouched; **frontend
|
||
lane only** — never edit `server/`.
|
||
|
||
## 6. Definition of Done
|
||
|
||
On top of the shared [definition-of-done.md](../../phases/_shared/definition-of-done.md):
|
||
|
||
- [ ] `npm run check` green; `npm run test:ci` green for every touched shared component (`PriceBreakdown`,
|
||
`BnplPlanCard`, `InstallmentScheduleRow`, `PaymentStateCard`, `BnplProviderLogo`); `en.json`/`fa.json` in sync.
|
||
- [ ] C6 answers "who + how much" without scrolling at 375px: identity block with avatar + TrustBadge,
|
||
prominent total, safe-area-aware sticky pay bar; **both** CTAs disabled during initiate; back link.
|
||
- [ ] Confirmation renders a receipt: copyable LTR کد پیگیری, Shamsi date-time, method, booking reference,
|
||
escrow line — with real loading/error states (mock shows all fields; real path hides unserved rows).
|
||
- [ ] Wallet has the four sections with four-state coverage each; a card-paying user sees payment history
|
||
(mock) instead of an installments-only empty tab; money surfaces share one width system.
|
||
- [ ] BNPL plan cards show down-payment/monthly/total-repayment in Toman with the fee delta explicit; no
|
||
`LinearProgress` as a static fact; PlanStep's total names its plan; eligibility shows in-progress
|
||
feedback; the `bnpl/return` invalid-link CTA label matches its destination.
|
||
- [ ] `bnpl/gateway` returns 404 outside development; dead `CHECKOUT_GATEWAY` constant removed.
|
||
- [ ] Every amount in the area carries «تومان» via `<Money>`; `PriceBreakdown` still reconciles (dev guard
|
||
fires on a deliberate mismatch). Invoice prints as an A4 document (footer, paper colors from dark mode).
|
||
- [ ] REQs filed with the next free numbers; visual verification on all four axes (`/fa` + `/en` × light +
|
||
dark), mobile **and** desktop, for checkout, confirmation, wallet, BNPL wizard, invoice.
|
||
|
||
## 7. How to test (what a human can verify after this phase)
|
||
|
||
1. Login as a seeded customer with an accepted request → `/fa/bookings/checkout?request_id=…` at 375px:
|
||
total figure at top, sticky pay bar above the bottom nav with total + «پرداخت»; nurse avatar + TrustBadge
|
||
on the summary; tap pay → both CTAs disable, label swaps to «در حال شروع…».
|
||
2. Complete the mock gateway round-trip → the return page shows the staged 2-node wait
|
||
(«بازگشت از درگاه ✓ → در انتظار تایید بانک») with duration copy — no bare spinner+chip stack.
|
||
3. Confirmation: کد پیگیری renders LTR with a working copy button; Shamsi date-time, method, booking
|
||
reference, escrow line present; reload with the network blocked → skeleton then error+retry, never a
|
||
silently missing amount.
|
||
4. Open «کیفپول» → four sections; پرداختها lists the payment just made (mock) linking to the booking;
|
||
استردادها shows `RefundStatusCard` entries after cancelling a paid booking; رسیدها links to the invoice.
|
||
5. Back on checkout, tap «پرداخت اقساطی» → provider rows show designed logos/chips (no two-letter glyphs);
|
||
plan cards show پیشپرداخت/قسط ماهانه/مجموع بازپرداخت in Toman with «+… تومان کارمزد» on fee plans, no
|
||
progress bar; selecting a plan names the header total; the eligibility check shows progress while pending.
|
||
6. `/fa/bookings/checkout/bnpl/gateway` in a production build → 404; in dev it still works. Open a paid
|
||
booking's invoice → buyer/service/reference rows present (mock); print preview shows an A4 document with
|
||
footer; triggered from dark mode → paper colors.
|
||
7. Repeat 1–5 on `/en` (LTR) and in dark mode: no clipped RTL/LTR islands, no unreadable tokens, «تومان»
|
||
(fa) / unit label (en) on every amount.
|
||
|
||
## 8. Hand off & document (close the phase)
|
||
|
||
- Update `client/CLAUDE.md` (Project Structure) for new components/route changes (`PaymentStateCard`,
|
||
`BnplProviderLogo`, wallet sections); note any minimal extension made to a phase-0/1 foundation file.
|
||
- Write the report at `dev/shared-working-context/reports/ui-phase-6-report.md`: what shipped per §3 item,
|
||
the provider-logo and money-width decisions, four-axes screenshots, and the exact REQ numbers filed.
|
||
- Save a memory note per operating-rules §8: checkout/receipt/wallet/BNPL end state, REQ numbers, and gotchas
|
||
(sticky-bar vs bottom-nav layering, print stylesheet interactions) the next phase should know.
|