/**
 * TMA — "Upgrade to Premium" paywall · above-the-fold rebuild · 5 Aug 2026
 * Design source of truth: PAYWALL_ABOVE_FOLD_REDESIGN_2026-08-05.html (dev-ready, post-Fable review)
 *
 * DROP-IN COMPONENT. Wire exactly three things, marked // TODO(wire):
 *   1. offerings fetch (RevenueCat)   2. purchase + restore   3. openTerms/openPrivacy
 *
 * HARD RULES (from the audit — these are why the old screen is being replaced):
 *  R1  Every price string on screen comes from the store product (pkg.product.priceString etc).
 *      If offerings fail to load, render the retry state — NEVER a hardcoded price literal.
 *  R2  A % discount must map to a real configured offer/product. No arithmetic-only strikethroughs.
 *  R3  PROMO mode has NO trial copy (one introductory offer per subscription group).
 *  R4  Timeline + dock ALWAYS re-bind to the selected tier (handled below — do not simplify away).
 */

import React, {useEffect, useMemo, useState} from 'react';
import {
  View, Text, Image, Pressable, ScrollView, StyleSheet, SafeAreaView,
} from 'react-native';
// import Purchases, {PurchasesPackage} from 'react-native-purchases'; // TODO(wire)
type PurchasesPackage = any; // remove once react-native-purchases is imported

// ─── tokens (do not restyle inline — edit here) ──────────────────────────────
const T = {
  bg: '#0E1526', card: 'rgba(255,255,255,0.045)', navy: '#1A2540', teal: '#00D4AA',
  gold: '#FFB000', goldPress: '#F59E0B', ctaText: '#0A0A0A',
  txt: '#FFFFFF', txt66: 'rgba(255,255,255,0.66)', txt55: 'rgba(255,255,255,0.55)',
  txt45: 'rgba(255,255,255,0.45)', bd: 'rgba(255,255,255,0.10)',
  tealBd: 'rgba(0,212,170,0.45)', tealSoft: 'rgba(0,212,170,0.10)',
  gutter: 24, radius: {hero: 16, ramp: 12, cta: 12, seal: 15},
  stars: '#FFC107',
};

// ─── static copy (single source — no strings inline in JSX) ──────────────────
const COPY = {
  navTitle: 'Upgrade to Premium', // matches the existing header
  restore: 'Restore',
  // Sentence case, NOT Title Case and never ALL CAPS — caps strip word-shape and slow reading.
  headline: ['Get strong, lean, mobile & ', 'pain-free', ' for life'], // [white, teal (no internal break), white]
  sub: 'while learning over 100 calisthenics skills at home',
  wallCaption: 'A few of the 100,000+ members',
  pressTitle: 'AS FEATURED IN',
  ratingLine: 'member rating',             // rendered after the bold 4.8 — never platform-attributed
  membersLine: '100,000+ members',         // VERIFY before ship (see build notes)
  dueToday: 'due today',
  remindA: 'we remind you before it ends',            // ONLY with the scheduled notification (build notes) — else use remindAFallback
  remindAFallback: 'cancel anytime before day 7',
  day5: 'We remind you the trial is ending.', // see build notes: needs ONE scheduled local notification, or swap to day5Fallback
  day5Fallback: 'Still deciding? You keep full access to day 7.',
  // anchor: compares only against a tier that is ON the page — no unsourced third-party price claims
  heroDesc: 'The full transformation — and your price locked all year.',
  rampDesc: 'One full phase',   // rendered under the ramp name at 10px, sentence case
  shorterPlans: 'START ON A SHORTER PLAN',
  // First-year cost comparison — both figures computed from real prices, never hardcoded
  // Savings block is STRUCTURED, not a sentence: figure first, then a two-row ledger.
  // The losing option is muted — it is a comparison, never a second offer.
  // SAVE chip on the hero. Full-price measures against paying quarterly; promo states against LIST.
  // 🔴 Never show two different percentages on one card.
  saveChip: (pct: number, amount: string, perYear: boolean) =>
    `SAVE ${pct}% · ${amount}${perYear ? '/YR' : ''}`,
  // Competitor anchor — LIVE copy from the m7 quiz paywall. The PT figure is a RANGE, deliberately:
  // it is a category rate, not a claim about a named provider. Do not narrow it to a single number.
  compareA: 'A personal trainer runs $60–100 a session · AI coaching apps $149/mo · your plan:',
  tlTitleA: 'HOW YOUR FREE TRIAL WORKS',
  day5Fallback: 'Still deciding? You keep full access to the last day.',
  tlTitleB: 'WHAT HAPPENS WHEN YOU TAP',
  // 🔴 Trial state and promo states carry DIFFERENT guarantees — only Apple can refund an IAP,
  // so the web's 'one email, no questions, no forms' is not true in-app. See build notes.
  sealH_trial: "You won't be charged for 7 days",
  sealH_promo: 'Feel stronger in 60 days — or your money back',
  sealP_trial: 'Full access from today. Cancel any time before your trial ends and you pay nothing at all — no part-charge, no catch.',
  sealP_promo: "Train for two months. If you don't feel the difference, email us and we'll make it right.",
  sealBy: '— Jesse, Head Coach',
  guarantee: "60-day money-back guarantee — email us and we'll make it right",
  terms: 'Terms of Use', privacy: 'Privacy Policy',
  // WHAT YOU GET — reference strings for the EXISTING section's one-line change. NOT rendered by this component:
  whatYouGet: [
    ['In-depth assessment', 'to create a personalized training program.'],
    ['Progressions & guidance', 'a life-long system of gamified progressions across 100+ calisthenics and gymnastics skills.'],
    ['Daily training programming', 'that adapts to you.'],
    ['Achievements journey', 'track progress & unlock harder skills as you go along.'],
    ['Full workout customisation', 'choose time, equipment, and skills you want to work on.'],
  ],
};

// ─── data contract ───────────────────────────────────────────────────────────
type TierKey = 'annual' | 'quarterly' | 'monthly';
type Tier = {
  key: TierKey; pkg: PurchasesPackage;
  label: string;                 // "12-Month plan" / "3-Month plan" / "1-Month plan" — ALWAYS this pattern
  priceString: string;           // pkg.product.priceString — LOCALISED, never computed
  perDay: string;                // derived: pkg.product.price / days, formatted in product currency
  perWeek?: string;              // REQUIRED on annual — renders 'undefined/week' if omitted
  perMonth?: string;             // REQUIRED on annual — the coach-anchor line uses it
  zeroPriceString?: string;      // REQUIRED on annual — localised zero, e.g. Intl.NumberFormat(locale,{style:'currency',currency:pkg.product.currencyCode}).format(0). Android/Hermes: enable the Intl build flag.
  billedLine: string;            // "billed once a year" etc
  promo?: {priceString: string; pct: number}; // ONLY if a real configured offer exists (R2)
};
type Props = {
  tiers: Tier[];                       // annual first
  variant: 'full' | 'off30' | 'off50'; // THREE shipped states. 'full' carries the trial; both promo
                                       // states carry NO trial (one introductory offer per group).
                                       // The % applies to BOTH tiers — never mix rates across tiers.
  promoEndsDays?: number;              // computed from a REAL campaign date — never hardcoded
  promoSaving?: string;                // e.g. '$80.00' — list minus promo price, computed
  annualVsRampSaving?: string;         // e.g. '$39.97' — (ramp × 4) minus annual, computed
  trialDays: number;                   // 🔴 READ FROM THE PRODUCT'S INTRODUCTORY OFFER PERIOD — never hardcode.
                                       // Drives the trial-end date, the "Day N" timeline labels AND the reminder day.
                                       // Change the trial in App Store Connect and every string below follows.
  onPurchase: (pkg: PurchasesPackage) => void;   // TODO(wire): Purchases.purchasePackage
  onRestore: () => void;                          // TODO(wire): Purchases.restorePurchases
  onTerms: () => void; onPrivacy: () => void;     // TODO(wire)
  track: (event: string, props?: object) => void; // paywall_viewed / tier_selected / purchase_started
};

import SealShield from './SealShield';

const AVATARS = require('./assets/members-strip.png'); // Group-1000004751.png @1x/2x/3x
const WALL = require('./assets/members-wall.webp');    // checkout pack members-wall-960.webp — after the seal
const PRESS = require('./assets/partner-logos.png');   // logo-collection-mono.png (white marks for dark ground) — right after the wall

export default function PaywallScreen(p: Props) {
  const [sel, setSel] = useState<TierKey>('annual');
  const tier = useMemo(() => p.tiers.find(t => t.key === sel) ?? p.tiers[0], [sel, p.tiers]);
  const annual = p.tiers[0];

  // R1: if products didn't load, the caller must render the retry state instead of this screen.
  if (!annual?.priceString) return null;
  // R2 fail-soft: promo mode without a configured annual promo renders as full price, never a fake strikethrough.
  const promoMode = p.variant !== 'full' && !!annual.promo;
  const pct = p.variant === 'off50' ? 50 : p.variant === 'off30' ? 30 : 0;

  // All trial-dependent copy derives from ONE number, so it can never drift from the store config.
  const trial = useMemo(() => {
    const end = new Date(); end.setDate(end.getDate() + p.trialDays);
    return {
      days: p.trialDays,
      remindDay: Math.max(1, p.trialDays - 2),
      endLabel: end.toLocaleDateString(undefined, {weekday: 'long', day: 'numeric', month: 'long'}),
    };
  }, [p.trialDays]);

  useEffect(() => { p.track('paywall_viewed', {variant: p.variant}); }, []);

  const select = (k: TierKey) => { setSel(k); p.track('tier_selected', {tier: k}); };

  // R4: dock + timeline derive from `tier`, never from literals.
  const dockTitle = promoMode
    ? (tier.promo ? `${tier.promo.pct}% off — ${tier.promo.priceString} today` : `${tier.label} — ${tier.priceString} today`)
    : `FREE for ${trial.days} days — ${annual.zeroPriceString} today`;
  // Renewal price is disclosed on the hero card + timeline; the dock stays clean (Aga, 5 Aug).
  const dockSub = 'cancel anytime';
  const ctaLabel = promoMode ? (tier.promo ? `Get ${tier.promo.pct}% off` : 'Continue') : 'Try free';

  return (
    <SafeAreaView style={s.root}>
      {/* nav — delete ONLY if the existing coral header carries a Restore control; otherwise KEEP this Restore
          (or add a small 'Restore purchases' text link above the legal row). App Review 3.1.2 requires it reachable. */}
      <View style={s.nav}>
        <Text style={s.back}>‹</Text>
        <Text style={s.navTitle}>{COPY.navTitle}</Text>
        <Pressable onPress={p.onRestore} hitSlop={12}><Text style={s.restore}>{COPY.restore}</Text></Pressable>
      </View>

      <ScrollView style={s.body} contentContainerStyle={{paddingBottom: 22}} showsVerticalScrollIndicator={false}>
        <Text style={s.h1}>{COPY.headline[0]}<Text style={s.h1Teal}>{COPY.headline[1]}</Text>{COPY.headline[2]}</Text>
        <Text style={s.sub}>{COPY.sub}</Text>

        {/* proof row — avatars 28pt, two-line lockup, must never truncate */}
        <View style={s.proof}>
          <Image source={AVATARS} style={s.avatars} resizeMode="contain"
                 accessibilityLabel="Five Movement Athlete members — bar work, handstands, wall bars"/>
          <View>
            <Text style={s.r1}><Text style={{color: T.stars}}>★★★★★</Text>  <Text style={s.r1b}>4.8</Text> {COPY.ratingLine}</Text>
            <Text style={s.r2}>{COPY.membersLine}</Text>
          </View>
        </View>

        {/* HERO — annual */}
        <Pressable style={[s.hero, sel !== 'annual' && s.heroDim]} onPress={() => select('annual')}
                   accessibilityRole="radio" accessibilityState={{selected: sel === 'annual'}}
                   accessibilityLabel={`12-Month plan, ${annual.priceString} per year${promoMode && annual.promo ? `, ${annual.promo.pct}% off` : ''}`}>
          {sel === 'annual' && <View style={s.chip}><Text style={s.chipTxt}>✓ SELECTED</Text></View>}
          <View style={s.heroRow}>
            <Text style={s.heroName}>12-Month plan</Text>
            <Text style={s.saveChip}>{COPY.saveChip(
              promoMode ? pct : 20,
              promoMode ? p.promoSaving! : p.annualVsRampSaving!,
              !promoMode)}</Text>
          </View>
          <Text style={s.heroDesc}>{COPY.heroDesc}</Text>
          <Text style={[s.heroTrial, sel !== 'annual' && {color: T.txt55}]}>{promoMode
            ? <>50% off your first year{p.promoEndsDays != null && <Text style={s.heroTrialSuffix}> · ends in {p.promoEndsDays} days</Text>}</>
            : `Try it FREE for ${trial.days} days`}</Text>
          <View style={s.zeroRow}>
            <Text style={s.zero}>{promoMode ? annual.promo!.priceString : annual.zeroPriceString}</Text>
            <Text style={s.zeroCap}>{COPY.dueToday}</Text>
            {promoMode && <Text style={s.struck}>{annual.priceString}</Text>}
          </View>
          <Text style={s.heroSub}>
            {promoMode
              ? <>that's <Text style={s.b}>{annual.perDay}/day</Text> for a full year</>
              : <>then <Text style={s.b}>{annual.perWeek}/week</Text> · billed {annual.priceString}/year — <Text style={s.b}>{COPY.remindA}</Text></>}
          </Text>
          <Text style={s.heroDate}>
            {promoMode
              ? <><Text style={s.b}>Renews at {annual.priceString} from year two.</Text> Cancel anytime.</>
              : <><Text style={s.b}>Free until {trial.endLabel}.</Text> Cancel before then and pay nothing — no charge at all.</>}
          </Text>
        </Pressable>

        <Text style={s.divider}>{COPY.shorterPlans}</Text>

        {/* ramps */}
        {p.tiers.slice(1).map(t => (
          <Pressable key={t.key} style={[s.ramp, sel === t.key && s.rampOn]} onPress={() => select(t.key)}
                     accessibilityRole="radio" accessibilityState={{selected: sel === t.key}}
                     accessibilityLabel={`${t.label}, ${t.priceString}${t.promo ? `, ${t.promo.pct}% off` : ''}`}>
            {sel === t.key && <View style={s.rchip}><Text style={s.rchipTxt}>✓</Text></View>}
            <View><Text style={s.rampName}>{t.label.toUpperCase()}</Text><Text style={s.rampDesc}>{COPY.rampDesc}</Text></View>
            <View style={{marginLeft: 'auto', alignItems: 'flex-end'}}>
              <Text style={s.rampDay}>{t.perWeek} <Text style={s.rampDayCap}>/wk</Text></Text>
              {/* R1 applies here too: the ramp must state what is CHARGED, not only a unit price */}
              <Text style={s.rampCharged}>
                {t.promo ? `${t.promo.priceString} today, then ${t.priceString}` : t.billedLine}
              </Text>
            </View>
          </Pressable>
        ))}
        <Text style={s.compare}>
          {COPY.compareA} <Text style={s.compareYours}>{annual.perDay} / day ✓</Text>
        </Text>
        

        {/* timeline — R4: derives from `tier` */}
        <View style={s.tl}>
          <Text style={s.tlTitle}>{promoMode ? COPY.tlTitleB : COPY.tlTitleA}</Text>
          {promoMode ? (
            <>
              <TlStep on label="Today" text={`${tier.promo ? tier.promo.priceString : tier.priceString} charged. Full access straight away.`}/>
              <TlStep last label={renewTitle(tier)} text={`Renews at ${tier.priceString}, unless you cancel.`}/>
            </>
          ) : (
            <>
              <TlStep on label="Today" text="Full access. Nothing to pay."/>
              <TlStep on label={`Day ${trial.remindDay}`} text={COPY.day5}/>
              <TlStep last label={`Day ${trial.days}`} text={`${tier.priceString} charged, unless you cancel.`}/>
            </>
          )}
        </View>

        {/* guarantee seal */}
        <View style={s.seal}>
          <SealShield size={26} />
          <View style={{flex: 1}}>
            <Text style={s.sealH}>{promoMode ? COPY.sealH_promo : COPY.sealH_trial}</Text>
            <Text style={s.sealP}>{promoMode ? COPY.sealP_promo : COPY.sealP_trial}</Text>
            <Text style={s.sealBy}>{COPY.sealBy}</Text>
          </View>
        </View>

        {/* members wall — the belonging proof */}
        <View style={{marginBottom: 26}}>
          <Image source={WALL} style={{width: '100%', height: undefined, aspectRatio: 960/438, borderRadius: 14}}
                 accessibilityLabel="Dozens of Movement Athlete members — handstands, bar work, the app in hand"/>
          <Text style={{color: T.txt55, fontSize: 11.5, textAlign: 'center', marginTop: 10}}>{COPY.wallCaption}</Text>
        </View>

        {/* press logos — directly after the wall (Aga: "in the right spot, after that") */}
        <View style={{alignItems: 'center', marginBottom: 26, paddingTop: 22, borderTopWidth: StyleSheet.hairlineWidth, borderColor: 'rgba(255,255,255,0.08)'}}>
          <Text style={{color: 'rgba(255,255,255,0.38)', fontSize: 10, fontWeight: '700', letterSpacing: 1.3, marginBottom: 13}}>{COPY.pressTitle}</Text>
          <Image source={PRESS} style={{width: '86%', height: undefined, aspectRatio: 624/71, opacity: 0.6}}
                 accessibilityLabel="Forbes, NewsWatch and AMC"/>
        </View>

        {/* NOTE: "What you get" (existing section, one-string change) renders after this, then: */}
        {/* legal row — REQUIRED on the purchase screen (3.1.2); lives at the end of the scroll, never in the dock */}
        <View style={s.legalRow}>
          <Pressable onPress={p.onRestore}><Text style={s.legal}>Restore purchases</Text></Pressable>
          <Text style={s.legal}> · </Text>
          <Pressable onPress={p.onTerms}><Text style={s.legal}>{COPY.terms}</Text></Pressable>
          <Text style={s.legal}> · </Text>
          <Pressable onPress={p.onPrivacy}><Text style={s.legal}>{COPY.privacy}</Text></Pressable>
        </View>

      </ScrollView>

      {/* dock — R4 */}
      <View style={s.dock}>
        <View style={s.dbar}>
          <View style={{flex: 1}}>
            <Text style={s.dockTitle}>{dockTitle}</Text>
            <Text style={s.dockSub}>{dockSub}</Text>
          </View>
          <Pressable style={({pressed}) => [s.cta, pressed && {backgroundColor: T.goldPress}]}
                     onPress={() => { p.track('purchase_started', {tier: sel}); p.onPurchase(tier.pkg); }}>
            <Text style={s.ctaTxt}>{ctaLabel}</Text>
          </Pressable>
        </View>
        <Text style={s.trust}>{COPY.guarantee}</Text>
      </View>
    </SafeAreaView>
  );
}

function TlStep({label, text, on, last}: any) {
  return (
    <View style={s.step}>
      <View style={[s.dot, !on && s.dotOff]}/>
      {!last && <View style={s.stepLine}/>}
      <View style={{flex: 1}}>
        <Text style={s.stepLabel}>{label}</Text>
        <Text style={s.stepText}>{text}</Text>
      </View>
    </View>
  );
}

// helpers — formatting only; NEVER price math into display currency by hand
function renewTitle(t: Tier) {
  return t.key === 'annual' ? 'In 12 months' : t.key === 'quarterly' ? 'In 3 months' : 'In 1 month';
}

// ─── styles: values match the HTML spec's measurements table exactly ─────────
const s = StyleSheet.create({
  root: {flex: 1, backgroundColor: T.bg},
  nav: {height: 44, backgroundColor: T.navy, flexDirection: 'row', alignItems: 'center',
        justifyContent: 'space-between', paddingHorizontal: T.gutter},
  back: {color: '#8FA0C0', fontSize: 20, width: 24},
  navTitle: {color: T.txt, fontSize: 15, fontWeight: '600'},
  restore: {color: '#8FA0C0', fontSize: 12.5, textDecorationLine: 'underline'},
  body: {flex: 1, paddingHorizontal: T.gutter, paddingTop: 18},
  h1: {color: T.txt, fontSize: 29, fontWeight: '800', letterSpacing: -0.8, lineHeight: 32, marginBottom: 2},
  h1Teal: {color: T.teal},
  sub: {color: T.txt55, fontSize: 13, marginBottom: 14, letterSpacing: 0.1},
  proof: {flexDirection: 'row', alignItems: 'center', gap: 12, height: 56, marginBottom: 14,
          borderTopWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
          borderColor: 'rgba(255,255,255,0.09)'},
  avatars: {height: 28, width: 137},
  r1: {color: T.txt66, fontSize: 12}, r1b: {color: T.txt, fontWeight: '600'},
  r2: {color: T.txt55, fontSize: 11.5, marginTop: 1},
  hero: {borderWidth: 1, borderColor: T.tealBd, borderRadius: T.radius.hero, padding: 15,
         backgroundColor: T.tealSoft, marginBottom: 10,
         shadowColor: T.teal, shadowOpacity: 0.09, shadowRadius: 12, shadowOffset: {width: 0, height: 0}}, // iOS-only glow — Android ships border+gradient fill as the selected state, by design
  heroDim: {borderColor: 'rgba(255,255,255,0.13)', backgroundColor: 'rgba(255,255,255,0.035)', shadowOpacity: 0}, // FULL deselect — never opacity-only; ✓ chip hides (see spec)
  heroRow: {flexDirection: 'row', justifyContent: 'space-between', marginBottom: 8},
  heroName: {color: T.txt, fontSize: 13, fontWeight: '700'},
  heroThen: {color: T.txt66, fontSize: 12},
  heroTrial: {color: T.teal, fontSize: 20, fontWeight: '800', letterSpacing: -0.4, marginBottom: 2},
  zeroRow: {flexDirection: 'row', alignItems: 'baseline', gap: 8, marginVertical: 4},
  zero: {color: T.txt, fontSize: 44, fontWeight: '800', letterSpacing: -1.7, fontVariant: ['tabular-nums']},
  zeroCap: {color: T.txt66, fontSize: 13},
  struck: {color: T.txt45, fontSize: 15, textDecorationLine: 'line-through'},
  struckSm: {color: T.txt45, textDecorationLine: 'line-through'},
  heroSub: {color: T.txt66, fontSize: 12.5, lineHeight: 18},
  heroDate: {color: T.txt66, fontSize: 12.5, lineHeight: 18, marginTop: 7, paddingTop: 8,
             borderTopWidth: StyleSheet.hairlineWidth, borderColor: 'rgba(255,255,255,0.09)'},
  b: {color: T.txt, fontWeight: '600'},
  ramp: {flexDirection: 'row', alignItems: 'center', gap: 10, height: 58, borderWidth: 1,
         borderColor: T.bd, backgroundColor: T.card, borderRadius: T.radius.ramp,
         paddingHorizontal: 14, marginBottom: 8},
  rampOn: {borderColor: T.teal, backgroundColor: 'rgba(0,212,170,0.07)'},
  rampName: {color: '#C8D2E6', fontSize: 12.5, fontWeight: '700', letterSpacing: 0.6},
  rampCharged: {color: 'rgba(255,255,255,0.5)', fontSize: 11, marginTop: 2, fontVariant: ['tabular-nums']},
  rampBill: {color: T.txt55, fontSize: 11.5},
  rampDay: {marginLeft: 'auto', color: '#C3CDDF', fontSize: 15, fontWeight: '600', fontVariant: ['tabular-nums']},
  rampDayCap: {color: T.txt45, fontSize: 10, fontWeight: '400'},
  anchor: {color: '#8593AD', fontSize: 11, lineHeight: 15, marginBottom: 12},
  tl: {borderWidth: 1, borderColor: T.bd, backgroundColor: T.card, borderRadius: 14, padding: 13, marginBottom: 12},
  tlTitle: {color: '#C8D2E6', fontSize: 10.5, fontWeight: '700', letterSpacing: 1.1, marginBottom: 11},
  step: {flexDirection: 'row', gap: 12, paddingBottom: 11, position: 'relative', alignItems: 'flex-start'},
  dot: {width: 11, height: 11, borderRadius: 6, backgroundColor: T.teal, marginTop: 3},
  dotOff: {backgroundColor: '#5F6F8D'},
  stepLine: {position: 'absolute', left: 4.75, top: 13, bottom: -1, width: 1.5, backgroundColor: 'rgba(0,212,170,0.28)'},
  stepLabel: {color: T.txt, fontSize: 12.5, fontWeight: '600'},
  stepText: {color: T.txt66, fontSize: 12.5, lineHeight: 17},
  seal: {flexDirection: 'row', gap: 12, padding: 14, backgroundColor: T.tealSoft,
         borderWidth: 1, borderColor: 'rgba(0,212,170,0.22)', borderRadius: T.radius.seal, marginBottom: 26},
  sealH: {color: T.txt, fontSize: 14, fontWeight: '800', marginBottom: 3},
  sealP: {color: T.txt66, fontSize: 12, lineHeight: 17},
  sealBy: {color: T.teal, fontSize: 11, fontWeight: '700', marginTop: 5},
  dock: {paddingHorizontal: T.gutter, paddingTop: 12, paddingBottom: 12, borderTopWidth: StyleSheet.hairlineWidth,
         borderColor: 'rgba(255,255,255,0.08)', backgroundColor: T.bg}, // + insets.bottom via react-native-safe-area-context (RN's SafeAreaView is a no-op on Android)
  dbar: {flexDirection: 'row', alignItems: 'center', gap: 12, marginBottom: 8},
  dockTitle: {color: T.txt, fontSize: 13.5, fontWeight: '700', fontVariant: ['tabular-nums']},
  dockSub: {color: T.txt55, fontSize: 11, fontVariant: ['tabular-nums']},
  cta: {height: 48, paddingHorizontal: 22, borderRadius: T.radius.cta, backgroundColor: T.gold,
        alignItems: 'center', justifyContent: 'center'},
  ctaTxt: {color: T.ctaText, fontSize: 15.5, fontWeight: '900'},
  trust: {color: '#8593AD', fontSize: 11, textAlign: 'center'},
  legalRow: {flexDirection: 'row', justifyContent: 'center', paddingTop: 20, borderTopWidth: StyleSheet.hairlineWidth, borderColor: 'rgba(255,255,255,0.08)'},
  legal: {color: 'rgba(255,255,255,0.38)', fontSize: 11, textDecorationLine: 'underline'},
  saveChip: {fontSize: 11, fontWeight: '800', letterSpacing: 0.3, color: '#04121C',
             backgroundColor: T.teal, paddingHorizontal: 9, paddingVertical: 3, borderRadius: 5,
             overflow: 'hidden', fontVariant: ['tabular-nums']},
  heroDesc: {fontSize: 12.5, color: T.txt66, lineHeight: 17.5, marginTop: -4, marginBottom: 9},
  divider: {fontSize: 11, fontWeight: '700', letterSpacing: 1, color: T.txt45, marginBottom: 10},
  rampDesc: {fontSize: 10, fontWeight: '500', color: 'rgba(255,255,255,0.42)', marginTop: 3},
  compare: {fontSize: 11.5, lineHeight: 17, color: 'rgba(255,255,255,0.5)', textAlign: 'center',
            marginBottom: 14, fontVariant: ['tabular-nums']},
  compareYours: {color: T.teal, fontWeight: '700'},
  chip: {position: 'absolute', top: -9, right: 14, height: 18, paddingHorizontal: 8, borderRadius: 4, backgroundColor: T.teal, justifyContent: 'center', zIndex: 1},
  chipTxt: {color: '#04121C', fontSize: 9, fontWeight: '800', letterSpacing: 0.7},
  rchip: {position: 'absolute', top: -7, right: 12, width: 17, height: 17, borderRadius: 9, backgroundColor: T.teal, alignItems: 'center', justifyContent: 'center', zIndex: 1},
  rchipTxt: {color: '#04121C', fontSize: 11, fontWeight: '900', lineHeight: 13},
  heroTrialSuffix: {fontSize: 12, fontWeight: '700', color: T.txt55, letterSpacing: 0.2},
});
