# TMA — Paywall screen rebuild · React Native package

**Scope: the "Upgrade to Premium" screen, above the fold only.**
Do not touch the coral header. Do not rebuild anything below "What you get".

## What's in here

```
src/PaywallScreen.tsx      drop-in component — layout, copy, styles, selection logic all done
src/SealShield.tsx         the guarantee shield, ready (react-native-svg)
fixtures/tiers.mock.ts     RUN THE SCREEN BEFORE WIRING ANYTHING — see the build notes' first section
assets/                    members-strip (480×98) · members-wall (960×438) · partner-logos (624×71)
                           — all three shipped at @1x / @2x / @3x, correctly named
spec/INTERACTIVE_SPEC.html open in a browser — tap the tiers. This IS the acceptance behaviour.
spec/WHY_THIS_CHANGE_...   the audit that triggered the rebuild (context, not required reading)
PAYWALL_BUILD_NOTES.md     wiring example, rules, App Review requirements, on-device checklist
COPY_FIXES_BELOW_FOLD.md   seven broken strings on sections we are NOT redesigning
```

**Fastest path:** render `<PaywallScreen {...mockProps('full')} />` first — the screen works
end-to-end on fixtures, so layout review and QA can start before RevenueCat is touched.

## This is a UI-ONLY change to an EXISTING screen

Purchase, restore, terms/privacy, analytics and RevenueCat are **already there and already working**.
Nothing in this package asks you to wire, integrate or re-plumb any of it. You are changing what the
screen *renders* — nothing about what it *does*.

### Two ways to use this — pick whichever is less work in your codebase

**A · Lift the render body (usually cheaper on an existing screen).**
Take the JSX between `<SafeAreaView>` and `</SafeAreaView>` plus the `StyleSheet` block at the bottom,
paste into your existing screen, and keep every hook, handler and state you already have. Rename
`p.onPurchase` → your existing purchase call, `p.onRestore` → yours, and so on. Delete the `Props`
type; you don't need it.

**B · Use it as a component.** Drop the file in, pass your existing handlers as props. Only worth it if
your screen is already thin.

Either way the layout, copy, styles, selection behaviour and timeline binding are done.

### The only genuinely NEW logic in the whole job

Three derived display strings, computed from product objects **you already have in hand**:

- **per-day** — price ÷ **365** (annual) · **91** (quarterly) · **30.4** (monthly)
- **per-week** (annual only) — price ÷ **52**
- **zero-price** — `Intl.NumberFormat(locale,{style:'currency',currency:product.currencyCode}).format(0)`
  *(Android/Hermes: enable the Intl build flag)*

That's it. Everything else on screen is a price string you're already fetching, or static copy.

### One decision

The Day-5 reminder line — see `PAYWALL_BUILD_NOTES.md`. Either schedule a single local notification, or
swap to the fallback string we've supplied. Please don't ship the claim without one of them.

## 🔴 Read this once — wired is not the same as bound

The SDK was already wired on this screen **and the prices were still wrong.** `$8 / week` and
`$6.4 / month` are hardcoded strings sitting on the same card as a correctly localised charged amount —
one bound to the store, one not. They render identically on a UK device and on a device charging in
pesos, while the real price changes underneath.

So the rule for this rebuild: **no price literal anywhere in the UI.** Every figure derives from the
store product at render time; if offerings fail to load, render the retry state rather than a fallback
price. Worth naming explicitly because stubbing literals in during development and removing them later
is, as far as we can tell, exactly how the current defect arrived.

## One placeholder to replace

- The `nav` block is a stand-in for the existing coral header. Delete it **only if** that header already
  carries a Restore control; otherwise keep this one. (Restore also appears in the legal row, so you're
  covered either way — just don't end up with zero.)

Questions on anything here: ask before assuming. Every value in the component matches the interactive
spec deliberately.
