The Movement Athlete · React Native · 5 August 2026
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 →| # | 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 |
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.
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.
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.
$ followed by ₱58,000.00 — same root cause.| 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.htmlalso 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 |
<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.tiers prop, order [annual, quarterly]. The wiring example in the build notes is about fifteen lines.trialDays from the introductory-offer period. This is the only genuinely new logic in the job.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.
| 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.
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.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.