The Movement Athlete · React Native · 5 August 2026

# Paywall rebuild — start here

A UI-only change to the existing “Upgrade to Premium” screen. Purchase, restore, terms, analytics and RevenueCat are already wired — nothing here asks you to integrate anything. The component is written; you swap the layout and map data you already have.

Step 0 · Download the package Everything below lives in this zip — the component, the fixtures, the assets, the build notes. Grab it before you read on. TMA_PAYWALL_RN_PACKAGE_2026-08-05.zip ↓ The design contract — open this first Three interactive states. Tap the plan tiers and the dock and timeline re-price — that behaviour, and every measurement, token, string and figure inside it, is what “done” means. Open the interactive spec →
Estimate: 5 hours max — here's how we got there

# | Milestone | Est.
--- | --- | ---
1 | Unzip, drop the component in, render it on the fixtures | 30 min
2 | Reconcile with the existing screens — keep your hooks; delete the stand-in nav if the coral header already has Restore | 60 min
3 | Route all three pricing pages at the one component via `variant`; map offerings to `tiers`; point existing handlers + analytics at its props | 60 min
4 | The three derived strings + `trialDays` — the only genuinely new logic | 45 min
5 | The seven copy fixes below the fold | 20 min
6 | On-device pass: all three states, two currencies, airplane mode, VoiceOver, payment-sheet parity, restore | 60 min
| Total | ~4 h 15

It's short because four things that normally eat time are already done: the component is written, the fixtures mean QA isn't blocked behind the wiring, the assets ship at three densities, and the wiring example is in the notes. 🔴 That number assumes the three pages go onto one component — rebuilt separately it is closer to double. If you think the estimate is wrong, say so before you start, and please don't push past 5 hours without coming back to us first.

## 01 Scope — exactly

In: everything from under the coral header down to the docked button, plus one copy line in the “What you get” section, plus seven broken strings listed in `COPY_FIXES_BELOW_FOLD.md`.

All three pricing pages — full price, 30% off, 50% off. They already exist and their offers are already configured; we are not creating anything, only updating the UI on each.

🔴 Please put all three on ONE component
The three pages are the same screen with a different price source. Drive them from `variant: 'full' | 'off30' | 'off50'` rather than applying the new layout to each page separately. It is less work — one layout to build and review instead of three — and three copies drifting apart is how the current bug survived: prices went stale on some surfaces and not others, with no single place to look. If that is awkward in the existing structure, tell us before starting.

Out: the coral header stays. Everything below “What you get” stays — testimonial carousel, transformation photos, feature panels, FAQ. If a change seems to need something outside that boundary, stop and tell us rather than absorbing it.

## 02 Why we’re doing it

Each plan currently shows a large unit price — `$8 / week` and `$6.4 / month`. Those strings are byte-identical on a UK device and on a device charging in pesos, while the amount actually charged changes underneath them. They were hardcoded into the layout and never bound to the store product.

It isn’t cosmetic
As displayed, the quarterly plan reads as 5.4× the yearly. What we actually charge makes it 1.26×. The screen argues against the plan we most want people to choose. The removed Lifetime card also rendered a stray `$` followed by `₱58,000.00` — same root cause.

## 03 What’s in the folder

File | What it’s for
--- | ---
`src/PaywallScreen.tsx` | Drop-in component — layout, copy, styles, tier selection, timeline binding all done
`src/SealShield.tsx` | The guarantee shield, ready (`react-native-svg`)
`fixtures/tiers.mock.ts` | Run the screen before wiring anything — all three states on mock data
`assets/` | Three images at @1x / @2x / @3x, correctly named — no need to regenerate
`spec/INTERACTIVE_SPEC.html` also live at the link above | 🔴 THE UX/UI CONTRACT. Open it and tap the tiers — that behaviour is the acceptance criteria, not an illustration of it. Measurements, tokens, copy strings and every figure are inside
`PAYWALL_BUILD_NOTES.md` | Wiring example, the five rules, App Review requirements, on-device checklist
`COPY_FIXES_BELOW_FOLD.md` | Seven broken strings on sections we are not redesigning
`spec/WHY_THIS_CHANGE_audit.md` | The full audit — context, not required reading

## 04 The job, in order

1. Download the package and unzip it.
2. Open the design contract and use it — tap the tiers, scroll each phone. Then render it on fixtures: `<PaywallScreen {...mockProps('full')} />` — the whole screen works, including tier selection and re-pricing. Check it against the contract side by side before touching the store.
3. Map your existing offerings to the `tiers` prop, order `[annual, quarterly]`. The wiring example in the build notes is about fifteen lines.
4. Point your existing handlers and analytics at the component’s props — the three events are already called inside it.
5. Add the derived strings — per-day, per-week, localised zero, and `trialDays` from the introductory-offer period. This is the only genuinely new logic in the job.
6. Work the on-device checklist at the end of the build notes, including a second currency. That last one matters more than it looks.

## 05 The one rule everything hangs off

No price literal anywhere in the UI

Every figure — per-day, per-week, charged amount, struck-through list price, renewal price — derives from the localised store product at render time. If offerings fail to load, render the retry state; never a fallback price.

Worth naming explicitly: the SDK was already wired on this screen and the prices were still wrong, because two display strings bypassed it. Please don’t stub literals in during development and rely on removing them later — as far as we can tell, that’s how the current defect arrived.

Four more, all in the build notes: a percentage discount must map to a real configured offer · a discount and a free trial can’t both be the introductory offer, so the promo states carry no trial copy · the timeline and dock bind to the selected tier · one percentage per state, applied to both tiers.

## 06 Three states, and what they charge

State | 12-Month | 3-Month | Trial | On-card saving
--- | --- | --- | --- | ---
full | $159.99 | $49.99 | 7 days | 20% · $39.97/yr vs paying quarterly
off50 | $79.99 | $24.99 | none | 50% · $80.00 vs list
off30 | $111.99 | $34.99 | none | 30% · $48.00 vs list

One component, `variant: 'full' | 'off30' | 'off50'`. US price points shown so you can check the arithmetic — every other storefront renders its own localised figures. All three offers are already configured in App Store Connect, and all three pages already exist — this pass updates the UI on each.

## 07 Two placeholders, and one thing to raise if it bites

- The `nav` block stands in for the existing coral header. Delete it only if that header already carries a Restore control — there’s also one in the legal row, so either way just don’t end up with none. App Review requires it reachable.
- Everything else is finished. If something looks like a decision rather than an instruction, it isn’t — ask us.

If the drop-in doesn’t drop in
The component assumes a screen thin enough to swap wholesale. If the existing file is more tangled than that, take the JSX and the `StyleSheet` and paste them into what’s already there, keeping your own hooks — that’s usually the cheaper path. Either way is fine; tell us which you took.

Questions on anything here, ask before assuming — every value in the component matches the design contract deliberately. Prices shown are US; all figures render localised from the store on device.